原文:TowardsDataScience Blog
协议:CC BY-NC-SA 4.0
是时候构建您的数据科学项目了
原文:https://towardsdatascience.com/its-time-to-structure-your-data-science-project-1fa064fbe46?source=collection_archive---------12-----------------------
构建数据科学项目的简单模板
[作者插图]
为什么笔记本电脑在数据科学界如此受欢迎?
当你深入数据科学领域时,你会很快注意到笔记本是工作和分享数据科学项目的常用工具。
在某种程度上,这是一个有充分理由和良好基础的选择。笔记本将代码、图形和文本汇集在一个单一快速的交互式“生态系统”中。
让我们退一步讲,
要运行一行 python 代码,我可以说没有什么比直接在控制台中执行它更快的了。然而,当您必须运行多行代码时,这就变得不方便了。想象一下,在一个控制台中定义一个功能是多么的笨拙和不切实际。
另一方面,python 脚本是创建和执行长代码的一种便捷方式。然而,它在数据科学的环境中也是不切实际的,因为它不能很好地扩展。事实上,无论何时你想写一段代码,你都必须创建一个 python 文件。因此,花点时间想想在一个数据科学项目中创建的笔记本单的数量。当然,它将让您了解您将创建多少 python 文件,以及确保它们的执行顺序是多么具有挑战性。
简而言之,笔记本提供了一种运行 python 代码的方式,就像在控制台上一样快,同时还能编写代码片段。因此,它们扩展了基于控制台的行为,并增加了交互性。
就我个人而言,我发现这些是使笔记本电脑在数据科学社区中获得如此巨大人气的核心功能。
“一个笔记本结构”运行良好,直到…
作为一名数据科学学生,我过去常常以笔记本的形式提交我的作业和微型数据科学项目。除了我上面提到的优点之外,在一个笔记本上组织我的工作也很方便,因为我可以嵌入图形和注入降价来编写文本/等式,从而进一步阐述我的推理。
直到我参与了一个研究实习,我才意识到这个“一个笔记本结构”的局限性。特别是,我注意到当项目变得越来越大时,这种方法不能很好地扩展。
当我开始获取、清理和探索数据时,单格的数量呈指数级增长,我很快就被数量庞大的变量淹没了。此外,当尝试新的方法时,我注意到我一直在写重复的代码。最后,为了避免变量之间的歧义以及一些笔记本单格不会影响其他单格,我将代码的一些(许多)部分切换到注释中。老实说,我通常会关闭笔记本内核并重启环境。
简而言之,在一个笔记本中编写整个项目的代码是不切实际的,在这个笔记本中,人们获取、探索和清理数据,然后设置、训练和评估模型。
如果一个人想发现一个 bug,或者决定在一个又长又乱的笔记本上做些什么,这甚至会是一个噩梦,笔记本上满是切换成注释的代码块。
从这次不幸的经历中,我明白了“一个笔记本结构”并不是处理大项目的最佳选择。
[作者的迷因]
在采取行动之前先谷歌一下
我相信数据科学是软件编程和应用数学(概率、统计、优化……)之间的接口。在从事数据科学项目时,我经常意识到我编码比其他任何东西都多。清理数据需要代码,理解数据需要可视化,而可视化又需要代码,…
当我编码时,我开发了所谓的“谷歌一下”反射。因此,每当我忘记/怀疑某件事或陷入某个错误时,我就简单地谷歌一下。
“有人说一个软件工程师只是一个专业的谷歌搜索者”, Fireship,如何像一个高级软件工程师一样“谷歌一下”。
就个人而言,养成这种习惯是最好的救生习惯之一,它可以让你从用详细的包装文件来填充大脑中解放出来。与其死记硬背,不如学会如何高效地搜索信息。
接下来,我浏览了互联网来寻找我的问题的解决方案,特别是一个用于组织的项目结构,从而能够扩大我的工作。显然,我找到了我要找的东西。我发现了一个千篇一律的模板项目:“ cookiecutter data-science ”。它是一个千篇一律的模板,旨在提供一个标准化的数据科学项目结构。
标准项目结构背后的动机
我承认我的不幸经历让我明白我是多么迫切地需要一个项目结构。然而,在仔细研究了模板描述和它的 GitHub 库之后,我更加确信标准结构的重要性,并且意识到它为“你和其他人”提供的优势。
一方面,它可以有效地组织思想和代码,从而加快工作流程。所以,你不会在“路中间”迷路,或者站在笔记本前无语地想“我到底在想什么”。
另一方面,它保证了代码的可共享性和可复制性。因此,其他人将能够快速直接跳转到他们感兴趣的代码部分。更重要的是,他们不会在重新执行你的代码时遇到麻烦。
我立刻认为我解决了我的问题,我所需要的就是采用这个项目结构。然而,这并不像我想象的那么容易。的确,熟悉包含一些文件和文件夹的项目结构非常困难,以至于我完全忽略了它们的用途——尤其是对于像我这样的初学者。
为了使这个项目结构适应我的用例,我花了几个小时的思考和实验来理解这个结构,并根据我的需要调整它。最后,我决定采用下面的结构:“简单的 DS 项目”
“简单的 DS 项目”结构
在这一节中,我将详细介绍“简单 DS 项目”结构的每个组件。此外,我将强调每一个项目背后的目的和动机。
“简单的 DS 项目”结构[作者插图]
“数据”文件夹
数据科学的一个基本目标是从数据中获得洞察力。因此,它是必不可少的成分。
该文件夹的目的是收集所有项目原始数据。另一方面,它也可以作为一个“桶”,用来保存预处理过的数据,以避免每次都重复相同的操作。
“笔记本 _ 探索 _ 清理”文件夹
该文件夹将包含所有与数据清理相关的笔记本。但是为什么把探索和清理放在一个文件夹里,而不把它们分开放在不同的文件夹里呢?
在我参与的每个项目中,我从来没有能够独立于清理数据来探索数据(反之亦然)。事实上,正是在探索阶段,我决定了如何处理这些数据。例如,在可视化数据列中缺失值的比例之后,我决定如何处理它们(例如,删除它们,或者用中位数替换它们……)。
最后,在探索-清理您的数据之后,确保将它保存在“data”文件夹中,以便以后在构建模型时使用。
“笔记本 _ 模型”文件夹
顾名思义,这个文件夹专门用于构建、训练和评估模型的笔记本。我强烈建议将每个型号放在一个笔记本中。
此文件夹的典型结构是, model_1。 ipynb , model_2.ipynb ,…或者如果你想给你的文件起一个有意义的名字,没有什么比用模型的名字命名更好的了,例如,linear _ regression . ipynb, lasso.ipynb , ridge.ipynb , elasticNet.ipynb
“py_scripts”文件夹
让我们想象以下(循环出现的)情况,
在 exploration_1.ipynb 中,你写了一段代码来可视化一个数据列。后来,在尝试另一种方法时,您需要在 exploration_2.ipynb 中使用相同的代码来制作类似的可视化效果,所以您复制粘贴了这些代码以便重用。在 exploration_3.ipynb 中,你另一次需要这个可视化,所以你再次复制粘贴那个东西,…
同时使用多台笔记本电脑的一个主要缺点是编写重复的代码。“py _ script”文件夹的主要目的就是为了克服这样的问题。事实上,它将作为一个 python 包,您可以在其中放置所有重复的代码。所以,每当你注意到你在多次复制粘贴一个代码时,只要把它重写为一个函数,把它放入 python 模块,瞧!当你需要它的时候,你可以直接导入它,就像导入一个普通的 python 模块一样。
“README.md”文件
自述。 md 是一个用来描述你的项目的 markdown 文件。在这里,您将设定项目的背景,提及其目的,并陈述重现其发现的指导原则。
在浏览项目时,我总是先浏览它们的自述文件,然后再深入研究它们的内容。因此,请始终记住,该文件位于项目的最前面。
的”。gitignore”文件
当在一个大项目上工作时,使用版本控制软件变得强制性,特别是如果项目有不止一个贡献者的话。 Git 结合 GitHub 就是其中之一,可以使项目管理变得简单高效。
不使用版本控制时的麻烦
尤其是”。gitignore" file 包含了 Git 不应该跟踪的所有文件和文件夹的名称,因此不会与 GitHub 库同步。通常,它包含缓存和构建文件夹的名称。此外,您可以在其中包含“data”文件夹,尤其是当您使用超过 100MB 的大型数据集时。
除此之外,如果你不熟悉 Git 和 GitHub(坦白地说,你不应该熟悉),可以考虑花点时间去了解它们。当然,这将是你编程生涯的一个转折点。
“environment.yml”文件
在数据科学中,第三方包的使用无处不在,如 numpy、scikit-learn 。那么,您如何告诉其他人运行您的代码需要安装哪些依赖项呢?
“environment.yml” 意在回答这个问题,因为它包含了 Jupyter 内核运行代码或笔记本所需的所有 python 包。
除此之外,这个文件很容易创建和使用。要使用 anaconda 提示符将您的 conda 环境导出到您的项目目录,只需使用 cd 并运行以下命令:
conda env export > environment.yml
通过将这个文件添加到您的项目中,您可以确保其他人在执行您的代码时不会发现问题,更重要的是,您可以保证您的发现的可重复性。事实上,其他人只会运行下面的命令来复制您的 conda 项目环境
conda env create -f environment.yml
连接到它,然后运行代码。
“简单 DS 项目”入门
生成项目结构
使用“简单的 DS 项目”结构很容易上手。您所需要做的就是安装cookiecutters——一个从模板项目创建项目的 python 包——通过运行
pip install cookiecutter
然后,执行下面的命令,按照提示创建/设置项目及其名称
cookiecutter https://github.com/Badr-MOUFAD/cookiecutter-simple-DS-project.git
完成后,“简单的 DS 项目”结构将在您的本地机器上生成,并准备开始工作…
集成开发环境(IDE)说明
当使用这个项目结构时,您会注意到您不断地从一个笔记本移动到另一个笔记本,从一个笔记本移动到一个 python 文件,…我发现这有点限制性。
IDE 是减轻这种限制的工具,因为它们可以在项目目录和文件之间轻松地转换。事实上,通过侧栏中显示的目录树,您可以看到项目的所有信息。此外,您可以在同一环境中修改文件。
在我的例子中,我使用 VS 代码结合 Jupyter 扩展。
总结和结论
“简单 DS 项目”是一个受“cookiecutter 数据科学”启发的模板。它提供了一个入门级的结构来组织你的工作,当处理一个“有点大的项目”。
此外,它不是一个可以完全遵循的结构。你可以根据自己的需要进行调整和完善。老实说,我会根据我参与的项目不断地修改它,添加/删除文件和文件夹。
记住,当你在项目中工作时,你组织工作的方式会不断发展。就我而言,我注意到自己最近开始越来越向最初的结构——“cookiecutter 数据科学”靠拢。当我重新思考是什么阻止了我直接采用它时,我发现并不是结构本身的复杂性,而是我不熟悉处理一个项目,在这个项目中,我操作多个具有不同扩展名的文件夹和文件(python、notebooks、markdowns 等等)。我相信“简单 DS 项目”模板会让您顺利完成这一过渡。
最后,要查看模板的实时版本,可以访问它的 GitHub 库。在那里,您还可以找到源代码。
是时候像条形图一样使用 AI 和机器学习了。
原文:https://towardsdatascience.com/its-time-to-use-ai-and-machine-learning-like-bar-charts-a21caf6ebcfa?source=collection_archive---------31-----------------------
组织需要更广泛地部署人工智能和机器学习,而不仅仅是数据科学团队。
是的,将 ML 民主化会导致不完善的模型,有时甚至是错误的决策。但是不完美的 ML 并不比不完美的 Excel 商业分析差。数据的可用大小和规模要求更多的分析师具备一套升级的技术。
AI 和机器学习(AI / ML)被太多的谨慎和崇敬对待。AI / ML 作为一个下棋、赢得危险的黑盒进行营销,它获得了不应有的恐惧和尊重。结果,它成了一种特殊的工具,为“大项目”而保留。
但是机器学习仅仅是一种新的方式将已经被检验的模式映射到已经被瞄准的结果变量。
以前,在 BI 报告工具中,分析师可能会深入到一些人口统计变量:按年龄、性别、地理位置和与公司的关系划分的流失率条形图。从这一结果中,营销人员可能会注意到一种模式,即年龄在 35-50 岁之间的男性,他们拥有较高的余额和贷款产品,以最高的利率保留下来。这种特定的分析几乎每天都在几乎每个组织中发生,并且可以被认为是“快速人类学习模型”
作者在 Einblick 中创建的图像
机器学习完成完全相同的任务。随着数据科学工具(如 AutoML)的最新进展,ML 的创建开始变得大众化,现在获取结构化数据集并创建良好的模型变得轻而易举。缺乏 Python 或 R 的知识不再是 ML 应用的障碍。
AutoML 工具复杂多样;Einblick 的 AutoML 向导是一个直接应用于分类任务的例子——作者在 Einblick 中创建的图像
一旦 ML 运行完成,现在很容易重现上面的洞察力。首先,检查特征重要性输出将向我们展示最重要的人口统计因素的定性等级排序。该模型已经成为评估候选驱动因素的一种更全面的方法,并且可以进一步彻底探索顶级驱动因素(包括使用条形图!)
XGBoost 回归的 Einblick 中的 Shapley 可视化— 作者在 Einblick 中创建的图像
同样,好的 ML 工具会进一步解释所创建的模型,它可以让您遍历各个预测。这成为一种实用而具体的方式来显示和理解客户的个人资料如何导致预测的行为。大多数人类会欣赏有形的例子,即使机器学习是大规模完成的。
了解一个账户的不同变量如何影响预测的和实际的客户流失。— 作者在 Einblick 中创建的图片
一个 ML 模型并不真的需要成为一个生产化的评分 API 来对我们的分析师有用。上述可视化非常快完成,识别关键驱动因素和使用直接输出的可消化范围意味着分析师不需要太多特殊培训。当然,也没有限制;图形化表示的基于 XGBoost 的模型可以按原样生产,也可以由数据科学家进行微调。
尽管在不了解完整技术实现的情况下使用机器学习存在缺陷,但由不完善的 ML 支持的任何商业决策都不可能比仅在电子表格中产生的现有不完善分析更糟糕。一个不完美的分析世界认识到,以上的 ML 输出是递增的,例如,根据直觉逐个搜索驱动程序。
最终,组织数据科学战略需要在应用高级数据科学方面争取更大的灵活性,并创造一种环境,认识到在多个应用级别从 ML 中获取价值是很容易的。在日常分析中,数据透视表、饼图和机器学习应该同等对待,因为它们在为明智的决策创建数据驱动的输入方面都有重要作用。
原载于 ein blick:https://ein blick . ai/its-time-to-use-ai-and-machine-learning-like-bar-charts/
Einblick 是世界上第一个可视化数据计算平台,创建与数据最自然的交互。在https://einblick.ai/product/了解更多关于我们的信息
多智能体强化学习与合作人工智能
原文:https://towardsdatascience.com/ive-been-thinking-about-multi-agent-reinforcement-learning-marl-and-you-probably-should-be-too-8f1eac?source=collection_archive---------4-----------------------
帮助人类相互合作的工具?
埃里克·克鲁尔在 Unsplash 上的照片
多代理强化学习(MARL)是强化学习的一个子领域,它变得越来越相关,并且一直让我感到惊讶——在继续阅读这篇文章之前,你必须观看 OpenAI 的这个视频,它展示了在这一领域正在进行的惊人研究。
无论如何,MARL 已经在流行的策略游戏中取得了令人难以置信的成功,已经证明是有趣的新兴行为的催化剂(如上文所述),并将是几项新兴技术持续发展的关键,不同自动驾驶汽车之间的通信就是这样一个例子。这篇文章将首先讨论这个子领域是什么,描述它与合作人工智能的关系,然后迅速转移到这些研究领域在未来将产生的巨大影响。
什么是多智能体强化学习?
普通强化学习关注的是一个环境中的单个主体,寻求在该环境中最大化总报酬。你可以想象——或者只看这个视频——一个正在学习走路的机器人,它的总体目标是不摔倒地走路。它会因为没有摔倒而获得奖励,通过反复试验,并最大化这些奖励,机器人最终学会了走路。在这种情况下,我们有一个单个代理人寻求通过最大化总报酬来实现目标。
多智能体强化学习研究多个智能体如何在一个共同的环境中相互作用。也就是说,当这些智能体与环境和其他智能体相互作用时,我们能观察到它们合作、协调、竞争或集体学习来完成特定的任务吗?它可以进一步分为三大类:
合作:所有的代理都朝着一个共同的目标努力
竞争:代理为了完成一个目标而相互竞争
两者的混合:想象一下一场 5v5 的篮球比赛,同一个队的人互相配合,但是两个队却在互相竞争。
MARL 的一个典型例子是一群机器人试图营救一个人。每个机器人对其环境只有部分的可观察性(他们只能看到他们下面的一小块土地),因此机器人需要相互协调来营救个人。
在这里,我们可以看到科幻与现实之间的壁垒迅速化解。你可能已经在想象机器人团队建造房屋、自动驾驶汽车无缝交互或分布式资源管理。
大规模合作、合作人工智能及其未来影响
大规模的合作是我们人类取得惊人成功的关键因素。正是通过人类的集体智慧和协作,我们才能够完成令人难以置信的壮举。为了实现大规模合作,我们创建了能够实现大规模合作的机制。也就是说,我们创造了协议(信仰系统,政府系统,等等。)来诱导大规模的合作,这样一个人在与完全陌生的人合作时会感到舒服。人类的许多失败和生存威胁可以被视为一个协调或合作的问题(核军备竞赛,气候变化,世界大战等)。).如何使用 MARL 来解决这些协调问题?进入合作 AI 。
合作人工智能是人工智能中的一类问题,寻求使用人工智能来解决或有助于解决合作问题。这项研究的重点是(1)建立具有合作能力的人工智能,以及(2)创造可用于促进群体(包括机器和人类)合作的人工智能。
人类的成功在很大程度上取决于我们与他人合作的能力,如果有一天人工智能可以用来改善人类之间的合作,这可能会导致人类进步和减轻人类痛苦方面的令人难以置信的进步。
人工智能可以用来增强人类的超级能力——大规模合作。在这一框架下,不难想象人工智能被用来帮助谈判和平条约、创造更强大和公平的政府形式,或者帮助人类相互合作以避免存在性灾难(气候变化、流行病、核浩劫)。事实上,这种研究已经存在,最近多主体系统框架被用于设计税收政策:
多智能体强化学习以及其他学科(博弈论、自然语言处理、多智能体设计等)。)是将被用来解决合作人工智能中存在的这些问题的工具。这是另一个在 MARL 框架内对这类问题进行研究的例子。
想了解更多?
如果你有兴趣学习更多关于合作人工智能的知识,我推荐阅读合作人工智能中的未决问题。我有没有被说服开始在泥灰岩里打探?这个 github repo 提供了一个不错的 MARL 研究论文集。流口水来进行你自己的泥灰实验?看看宠物动物园!
无耻地为我的网站和 twitter 插上一脚——更多的 MARL(项目/博客帖子)内容即将到来!
参考文献
[1]: Y .哈拉里,智人 (2015)
[2]:艾伦·达福、爱德华·休斯、约拉姆·巴赫拉赫、坦图姆·科林斯、凯文·r·麦基、乔尔·z·雷博、凯特·拉森、托雷·格雷佩尔,《合作人工智能中的开放性问题》(2020 年),https://arxiv.org/abs/2012.08630
[3]:不太可能的 AI。(2021 年 4 月 12 日)。CSL 研讨会:雅各布福斯特[视频]。YouTube。https://www.youtube.com/watch?v=ii_SwIsY8aU&ab _ channel = implebable ai
[4]: P. Barekatin,https://www . quora . com/What-is-multi-agent-reinforcement-learning
Python 中的 Jaccard 相似性和 Jaccard 距离-统计
原文:https://towardsdatascience.com/jaccard-similarity-and-jaccard-distance-in-python-statistics-f6aa214a9816?source=collection_archive---------45-----------------------
在本教程中,我们将探索如何在 Python 中计算 Jaccard 相似性(索引)和 Jaccard 距离
乔尼·克洛在 Unsplash 上的照片
目录
- 介绍
- 什么是 Jaccard 相似性
- 计算 Jaccard 相似度
- 什么是 Jaccard 距离
- 计算雅克卡距离
- 非对称二属性的相似性和距离
- 用 Python 计算 Jaccard 相似度
- 用 Python 计算 Jaccard 距离
- Python 中非对称二属性的相似性和距离
- 结论
介绍
Jaccard 相似性(Jaccard 指数)和 Jaccard 指数被广泛用作相似性和相异性度量的统计量。
它在实用统计中的应用范围从简单的集合相似性,一直到复杂的文本文件相似性。
为了继续学习本教程,我们需要以下 Python 库:scipy、sklearn 和 numpy。
如果您没有安装它,请打开“命令提示符”(在 Windows 上)并使用以下代码安装它:
pip install scipy pip install sklearn pip install numpy
什么是 Jaccard 相似性
Jaccard 相似性(也称为 Jaccard 相似性系数,或 Jaccard 指数)是一种用于测量两个集合之间相似性的统计数据。
它的用途进一步扩展到测量两个对象之间的相似性,例如两个文本文件。在 Python 编程中,Jaccard 相似性主要用于度量两个集合之间或者两个非对称二进制向量之间的相似性。
数学上,Jaccard 相似度的计算就是简单地取集合交与集合并的比值。
考虑两套 A 和 B :
作者图片
那么它们的 Jaccard 相似性(或 Jaccard 指数)由下式给出:
作者图片
让我们把这个公式分成两部分:
1。提名人
作者图片
2。分母
作者图片
使用 Jaccard 相似性的公式,我们可以看到相似性统计只是上述两个可视化的比率,其中:
- 如果两组相同,例如 A = {1,2,3}和 B = {1,2,3},那么它们的 Jaccard 相似度= 1。
- 如果集合 A 和 B 没有公共素,比如说 A = {1,2,3}和 B = {4,5,6},那么它们的 Jaccard 相似度= 0。
- 如果集合 A 和 B 有一些公共素,例如 A ={1,2,3}和 B = {3,4,5},那么它们的 Jaccard 相似度是区间上的某个值:0 ≤ J(A,B) ≤ 1。
计算 Jaccard 相似度
考虑两组:
- A = {1,2,3,5,7}
- B = {1,2,4,8,9}
或者视觉上:
作者图片
第一步:
作为第一步,我们需要找到 A 和 B 之间的集合交集:
作者图片
在这种情况下:
作者图片
第二步:
第二步是找到 A 和 B 的套装接头:
在这种情况下:
作者图片
第三步:
最后一步是计算交集和并集的大小之比:
作者图片
什么是 Jaccard 距离
与 Jaccard 相似性(Jaccard 指数)不同,Jaccard 距离是两个集合之间不相似性的度量。
数学上,Jaccard 距离的计算是集合并集和集合交集之差与集合并集之比。
考虑两套 A 和 B :
作者图片
那么它们的 Jaccard 距离由下式给出:
作者图片
让我们把这个公式分成两部分:
1。提名人
提名者也可以写成:
作者图片
这实际上是 A 和 B 之间的设置对称差,由下面信息图中的黄色区域显示:
2。分母
分母实际上是 A 和 B 的集合联合,如下面信息图中的黄色区域所示:
使用 Jaccard 距离的公式,我们可以看到,相异统计量只是上述两种可视化的比率,其中:
- 如果两组相同,例如 A = {1,2,3}和 B = {1,2,3},那么它们的 Jaccard 距离= 0。
- 如果集合 A 和 B 没有公共素,比如说 A = {1,2,3}和 B = {4,5,6},那么它们的 Jaccard 距离= 1。
- 如果集合 A 和 B 有一些公共素,例如 A ={1,2,3}和 B = {3,4,5},那么它们的 Jaccard 距离是区间上的某个值:0 ≤ d(A,B) ≤ 1。
计算雅克卡距离
考虑两组:
- 一个 = {1,2,3,5,7}
- B = {1,2,4,8,9}
或者视觉上:
第一步:
作为第一步,我们将需要找到 A 和 B 之间的集合对称差:
作者图片
在这种情况下:
作者图片
第二步:
第二步是找到 A 和 B 的集合接头:
在这种情况下:
作者图片
第三步:
最后一步是计算对称差和并集的大小比:
作者图片
非对称二属性的相似性和距离
在本节中,我们将研究 Jaccard 相似性和 Jaccard 距离的更具体的应用。更具体地说,它们对不对称二属性的应用。
从它的命名上,我们已经可以猜到一个二属性是什么了。它是一个只有两种状态的属性,这两种状态是:
- 0,表示属性不存在
- 1,意味着属性存在
这种不对称性源于这样一个观点,即如果两个属性都存在(都等于 1),那么它被认为比两个属性都不存在(都等于 0)更重要。
假设我们有两个向量, A 和 B ,每个向量都有 n 个二属性。
在这种情况下,Jaccard 相似性(指数)可以计算如下:
作者图片
和 Jaccard 的距离可以计算为:
作者图片
其中:
- M_{11}是属性的总数,其中 A 和 B 都为 1
- M_{01}是属性的总数,其中 A 为 0, B 为 1
- M_{10}是属性的总数,其中 A 为 1, B 为 0
- M_{00}是属性的总数,其中 A 和 B 都为 0
并且:
作者图片
例子
为了更简单地解释这一点,考虑可用于购物篮分析的示例。
您经营着一家拥有 6 个产品(属性)和 2 个客户(对象)的商店,并且还要跟踪哪个客户购买了哪个商品。你知道:
- 顾客甲买了:苹果,牛奶咖啡
- 顾客 B 买了:鸡蛋、牛奶、咖啡
可以想象,我们可以构建以下矩阵:
作者图片
其中每个客户的二进制属性表示客户是购买了(1)还是没有购买(0)特定产品。
问题是找到这两个客户的 Jaccard 相似性和 Jaccard 距离。
第一步:
我们首先需要找到每个 M 的属性总数:
作者图片
我们可以通过对计数求和来验证这些组。它应该等于 6,这是属性(产品)的 n 个:
作者图片
第二步:
既然我们有了所有需要的输入,我们现在可以计算 Jaccard 相似性:
作者图片
和 Jaccard 距离:
作者图片
用 Python 计算 Jaccard 相似度
在本节中,我们将使用在第一节中定义的相同器械包:
- A = {1,2,3,5,7}
- B = {1,2,4,8,9}
我们从用 Python 定义它们开始:
下一步我们将构造一个函数,将 set A 和 set B 作为参数,然后使用 set 操作计算 Jaccard 相似度并返回:
然后测试我们的功能:
您应该得到:
0.25
这与我们手动计算的统计数据完全相同。
用 Python 计算 Jaccard 距离
在本节中,我们继续使用与上一节相同的器械包( A 和 B ):
我们从用 Python 定义它们开始:
下一步,我们将构造一个函数,将集合 A 和集合 B 作为参数,然后使用集合运算计算 Jaccard 相似度并返回:
然后测试我们的功能:
您应该得到:
0.75
这与我们手动计算的统计数据完全相同。
在 Python 中计算非对称二进制属性的相似性和距离
我们从导入所需的依赖项开始:
使用我们在理论章节中使用的表格:
作者图片
我们可以创建所需的二进制向量:
然后使用库的函数来计算 Jaccard 相似性和 Jaccard 距离:
您应该得到:
Jaccard similarity is equal to: 0.4 Jaccard distance is equal to: 0.6
这与我们手动计算的统计数据完全相同。
结论
在本文中,我们探讨了 Jaccard 相似性(索引)和 Jaccard 距离,以及如何在 Python 中计算它们。
如果你有任何问题或对一些编辑有建议,请随时在下面留下评论,并查看更多我的统计文章。
原载于 2021 年 12 月 14 日 https://pyshark.comhttps://pyshark.com/jaccard-similarity-and-jaccard-distance-in-python/。
詹姆斯·乔伊斯和机器学习
原文:https://towardsdatascience.com/james-joyce-and-machine-learning-3948e55270c0?source=collection_archive---------24-----------------------
雅克·博普拍摄于 Unsplash
实践教程
用张量流标点 Penelope
詹姆斯·乔伊斯-
佩内洛普是这本书的主角。
介绍
在这篇文章中,我们将通过训练一个模型来识别和标点佩内洛普中的意识流。佩内洛普是爱尔兰作家詹姆斯·乔伊斯的著名作品《尤利西斯》的一部分。在本文中,我们将仔细研究詹姆斯·乔伊斯的独白式作品《佩内洛普》。佩内洛普可能很难读懂。
卡尔·荣格总结佩内洛普 -
在这里,令人窒息的空虚变得如此紧张,以至于达到了爆发点。毫无希望的空虚是整本书的基调。
语言结构
在进行机器学习之前,让我们简单地了解一下《佩内洛普》中的语言结构。Penelope 的结构相当奇怪,不像英语语料库的通常结构。佩内洛普以“是”字开头,也以“是”字结尾。佩内洛普完全致力于《尤利西斯》中的角色莫莉·布鲁姆的“主观”。佩内洛普通常因其“意识流”而被引用。意识流的定义在不同的学术领域会有所不同,但一般来说,它指的是一系列连续的句子。
符号化
为了达到本文的目的,我们将简单地用意识流来表示佩内洛普中缺少标点符号。这是对一个学术上很重要的想法的过度简化,但是它将帮助我们在本文的范围内工作。按照意识流的描述,我们可以想象乔伊斯带我们深入莫莉·布鲁姆的思想。
人们通常不用标点符号来思考,这在詹姆斯·乔伊斯的作品中得到了证明。因此,为了找到句子,我们不能使用 nltk 包中的句子分词器。这是因为分词器会通过在字符串中查找句点字符来给出句子。因此,我们不能使用标记符,因为 Penelope 只有两个句点,分隔大约 25000 个单词。这就是机器学习的用武之地。
模型
第一步包括从互联网档案馆或古登堡计划获取纯文本文件。下一步包括考虑一种方法,我们可以根据詹姆斯·乔伊斯的作品训练一个模型。一种简单的方法是使用二进制分类。我们可以用数字 1 标注尤利西斯中的每一句话,用 0 标注一组不是句子的单词。下一步是考虑模型的架构。一个 LSTM 有一个强大的架构设计,特别是对于 NLP,TensorFlow 使它非常容易使用。由于我们将此视为二分类问题,我们可以看到我们的损失函数对应于二交叉熵。
model = tf.keras.Sequential([ tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64, return_sequences=True)), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy']) model.summary()
考虑到训练需要一些时间,我们可以将计算分成两部分。训练我们的模型,然后标点佩内洛普。为了训练我们的模型,我们可以将佩内洛普从尤利西斯的剩余部分中分离出来。下一步将涉及到逆转尤利西斯剩余部分的每一句话。这些颠倒的句子将被标记为 0。随着本文的深入,这样做的原因将变得更加清楚。现在我们有了我们的训练数据,我们有了我们的模型,所以我们要做的就是训练模型。仅在几个周期内训练该模型后,我们可以看到大约 97%的准确度。一旦在 Google Colab 上训练了模型,我们就可以保存我们的模型,以便在本地机器上测试它。
Epoch 1/10 1488/1488 [==============================] - 188s 119ms/step - loss: 0.5992 - accuracy: 0.6194 Epoch 2/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.2330 - accuracy: 0.8816 Epoch 3/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.1538 - accuracy: 0.9264 Epoch 4/10 1488/1488 [==============================] - 178s 119ms/step - loss: 0.1149 - accuracy: 0.9471 Epoch 5/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.0933 - accuracy: 0.9578 Epoch 6/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.0762 - accuracy: 0.9644 Epoch 7/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.0696 - accuracy: 0.9675 Epoch 8/10 1488/1488 [==============================] - 178s 119ms/step - loss: 0.0609 - accuracy: 0.9714 Epoch 9/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.0534 - accuracy: 0.9732 Epoch 10/10 1488/1488 [==============================] - 177s 119ms/step - loss: 0.0544 - accuracy: 0.9733<tensorflow.python.keras.callbacks.History at 0x7f86f03d6160>
标点符号 Penelope
为了检查我们的模型是否理解一个句子的一般构成,我们可以给模型输入几个句子。第一句话来自尤利西斯,我们看到模型预测这句话有 99%的准确率。第二个句子是一个普通的英语句子,我们可以看到这个模型也预测正确。现在,如果我们颠倒第二句话,这里我们看到模型计算的分数小于 1%。这意味着我们的模型通常正确地计算英语句子的构成。我们现在可以运行一个简单的循环,从左到右穿过佩内洛普。一旦我们的模型以 99.99%的准确率找到一串单词,我们就可以简单地将句子写入文本文件。
s="Solemnly he came forward and mounted the round gunrest." seq = tokenizer.texts_to_sequences([s]) pad_s = pad_sequences(seq, maxlen=max_length, truncating=trunc_type) print(model.predict(pad_s)) [[0.]]s="My name is Sean." seq = tokenizer.texts_to_sequences([s]) pad_s = pad_sequences(seq, maxlen=max_length, truncating=trunc_type) print(model.predict(pad_s)) [[0.]]s="Sean is name my." seq = tokenizer.texts_to_sequences([s]) pad_s = pad_sequences(seq, maxlen=max_length, truncating=trunc_type) print(model.predict(pad_s)) [[0.00011346]]
下面我们可以看到我们的模型实时标点佩内洛普。
f = open(fpath+'PenolopePunctuated.txt', "a") i=0 sentence=""for c in range(24186): sentence=sentence+" "+wordlist[i] currentvalue=model.predict(check(sentence)) if(currentvalue>0.9999)&(currentvalue<1): f.write(sentence+",") i=c sentence="" i=i+1 f.close()
作者图片
反射
看来实验是成功的。目的是让 Penelope 的可读性稍微好一点,同时保留文本中两个独特的句号。詹姆斯·乔伊斯可能是最具智力挑战的作家之一。他设置智力迷宫只是为了嘲讽他的读者。这方面的一个例子是佩内洛普中的单词“tattarrattat”。对古典语言学家来说,这是一个珍贵的词。这个词有许多独特的属性。第一,它是一个拟声词。这仅仅意味着乔伊斯创造了一个听起来和拼写一样的单词。用稍微专业一点的术语来说,这个词的词法和音位之间有明显的关系。但也许最有趣的发现与单词“tattarrattat”的回文性质有关。这意味着即使语料库是反向计算的,这个单词的状态仍然保存在 Penelope 中。
图片作者(截图来自 PenolopePunctuated.txt)
所以“tattarrattat”这个词倒着读和正着读是一样的。更有趣的是这个词的上下文。乔伊斯用这个词来表达“敲门”。一个问题出现了,乔伊斯为什么要把这个特定的单词做回文?我认为他这样做是为了提醒他的读者,即使文集被倒着读,他的创造力仍然会找到敲开读者意识之门的方法。机器学习可以让我们制作出更加易读的版本;虽然我现在还不会不顾一切。阅读尤利西斯目前极其耗费时间。卡尔·荣格很好地传达了这一点。他写道,《尤利西斯》是一个迷人的故事,讲述了都柏林没有发生什么事情的一天。但正是《尤利西斯》中的文字让它成为多产的英国作家詹姆斯·乔伊斯的代表作。
最后的话
阅读我们的文本文件的内容让我们对莫莉·布鲁姆的主观性有了一个清晰的了解。然而,从 NLP 的角度来看,还有改进的空间。一个从左到右线性移动的循环是不够的。人类可以从标点符号和单词之间的相关性中获得意义。语言学上有一句名言,“从一个人交的朋友就可以知道这个人是谁”。在这种情况下,可以预期一个句子的最后一个单词和下一个句子的第一个单词之间的句子预测较低。这是作为模型的训练与反对尤利西斯。通过增加一些额外的条件,我们也许可以开始对《T2》中的语言有一个更好的了解。用句号或逗号给语料库加标点也可以为使用用 BERT、Word2vec 等计算的语义向量来可视化和分析语料库让路。[3][4].这个项目的源代码可以在 Apache 2.0 许可下这里获得。标点语料库也可以在这个库中找到。
参考
[1] R. Ellmann,《詹姆斯·乔伊斯书信选》(1975) ,维京出版社。
[2] C .荣格,《c . g .荣格文集【第 15 卷】(1953) ,柏林根基金会。
[3] T. Mikolov,K. Chen,G. Corrado,J. Dean,向量空间中词表征的高效估计(2013) ,计算与语言[arXiv.org]。
[4] J. Devlin,M. Chang,K. Lee,K. Toutanova, BERT:用于语言理解的深度双向变换器的预训练(2018) ,计算与语言[arXiv.org]。
一月版:一个更好的世界在等着你
原文:https://towardsdatascience.com/january-edition-a-better-world-awaits-d98e7d2964c7?source=collection_archive---------35-----------------------
月刊
新年快乐,欢迎来到 2021 年!
照片由格雷格·拉科齐在 Unsplash 上拍摄
在我们新年的第一期月刊中,我们想分享一些来自《走向数据科学》最新专栏的精选内容:数据改变。
在 2020 年的动荡和心碎中,围绕大数据的叙事被重新定义。我们的关注点从个人利益转移到如何利用数据科学造福世界各地的人们。今年,我们看到了比以往更多的关于使用数据科学和机器学习来建模流行病、预测野火、揭示我们机构中的种族歧视等的文章。
我们的编辑团队真的很喜欢从这些作品中探索和学习。通过为这一主题开辟专栏并对这些故事给予更多关注,我们希望进一步推进我们在 TDS 的核心信念之一:数据科学可以为紧迫的社会、环境和政治挑战提供洞察力。希望下面的选择将丰富和扩展您对我们如何将数据科学工具应用于社会公益的理解。
2021 年,你将如何运用你的技能让这个世界变得更美好?我们迫不及待地想把你的贡献加入这个专栏!
《走向数据科学》的编辑 Elliot Gunn 和 Linda Chen。
造福社会的数据科学
由 Ioana Spanache 博士 — 6 分钟阅读
超越我们想看什么类型的电影,到我们想生活在什么类型的世界。为社会公益进行数据科学的资源、例子和机会。
警察拦截和搜查中的种族差异
由迪沙·凯瓦拉马尼 — 9 分钟阅读
多年来警察拦截和搜查的统计分析,以确定警察部队是否对少数群体有偏见。
近距离观察加州野火中燃烧的生物质
由劳伦低 — 7 分钟读完
将加州大学圣克鲁斯森林地理站点的树种和生物量数据与野火进行比较——就像圣克鲁斯市区西北部的那场。这是我们的发现。
模拟疫情的传播
通过拉凯什·钦塔 — 20 分钟阅读
科学家如何模拟流行病?政府是如何制定封锁计划的?我们怎么知道曲线是否已经变平了?
COVID19:用 Python 可视化社交距离的影响
通过 Adarsh Menon — 6 分钟读取
使用 pandas 和 python 中的 matplotlib 可视化,一个人可以对平坦化曲线产生指数级影响。
新冠肺炎对学业成绩差距的影响
由巴尼特杨 — 21 分钟读完
随着新冠肺炎·疫情将美国经济置于混乱之中,随着种族的不同,经济前景的恶化将如何影响学术成就的差距?
使用优步移动数据优化救护车响应时间
塔哈·布洪 — 7 分钟阅读
蒙特卡洛模拟,评估基础设施对救护车响应时间的影响(伦敦塔桥案例研究)。
抑郁到想自杀
由 Eunjoo Byeon — 7 分钟阅读
使用多线性回归确定导致抑郁症状进展的一些关键因素。
学生应该如何利用时间来提高幸福感?
由陈敏仪 — 7 分钟读取
花在生活不同方面的时间如何影响我们的幸福水平?用 Jupyter 笔记本分析调查数据。
新播客
- Rob Miles — 我为什么要关心 AI 安全?
- 本·戈泽尔— 通往 AGI 的非正统之路
- 尼古拉·巴尔丁— 人工智能符合法律:偏见、公平、隐私和监管
- 乔迪·罗斯—AGI 需要具体化吗?
新视频
- 评估线性关系 |艾米莉·a·哈尔福德
- 空间数据分析:单去聚类 | Fouad Faraj
- 一个从零开始的源代码语言分类模型 | Amal Hasni & Dhia Hmila
我们也感谢最近加入我们的所有伟大的新作家玛丽·库尔萨特、乔·肯尼迪、奥萨索娜·伊弗欧卢瓦、乔纳·卡纳、莉亚·波普、张萌、琳达·温斯坦、舒巴姆·帕坦尼亚、吉列米娜·萨特·施奈德、法西姆·霍斯 马克·奈姆,内森·普拉特,阿曼达·j·切尼博士,达尔西·泰勒,德文·奥辛,丹尼尔·桑德斯,埃拉姆·穆纳瓦尔,阿纳·博拉,朱丽娅·乔,萨姆·斯塔克曼, 我们邀请你看看他们的简介,看看他们的工作。
一月份最引人入胜的小数据
原文:https://towardsdatascience.com/januarys-most-engrossing-small-data-cc230e2e45b9?source=collection_archive---------61-----------------------
全球风险报告
作为一个老学派的数学家,我不寻求从事大数据。相反,这篇文章是关于一个大主题的小数据。自 2006 年以来,每年 1 月发布年度《全球风险报告》( GRR ),作为年度世界经济论坛的背景材料(见下文脚注 1)。这些报告是冗长的文件,在这里可以免费获得分析的风险在未来几年(“中期”)会对世界经济产生重大影响的事件的意义上。这些报告提供了来自大型专家小组的一致意见。就我的目的而言,核心部分是一张图表,显示了 36 种风险中每一种风险的感知可能性和经济影响(见脚注 2)。请看下面这张 2020 年 1 月的图片(或此链接)。
2020 年全球风险报告
图表采用标准格式:横轴表示相对可能性,纵轴表示相对经济影响(见脚注 3)。因此,最大的感知风险(在新冠肺炎前夕)出现在右上角,从极端天气开始,然后是气候行动失败。
对我来说,最有趣的潜在概念问题是中期未来的可预测性如何?人们经常看到一些随意的断言,比如“没人预测”(或“我预测”)苏联解体,或类似 9/11 的袭击,或 2007-2008 年的金融危机,或新冠肺炎疫情。这种断言是荒谬的。这些本质上都是不可预测的事件,因此只谈论概率是有意义的。作为一个具体的例子,关于欧洲冷战的话题,1985 年对 1985-1995 年的一个概率评估(见脚注 4)是
65%:现状
25%:东欧内部叛乱导致苏联控制力下降
5%:苏联对西德的军事攻击
5%:苏联因内部原因而解体
对于最后一个选择,他们的短语“帝国崩溃”被证明是相当准确的。
能否判断过去概率评估的准确性?
在预测锦标赛的受控条件下(见脚注 5),人们确实可以通过评分规则来确定相对预测能力,但这需要不同的预测者评估相同的事件集合。我从来没有见过这样的中期预测数据。GRR 中的许多事件都是相当模糊的,所以不清楚如何获得预测的准确性。
因此,一个底线是,我们不能正式测试 GRR 在过去有多准确。还要注意这是小数据 : 13 年乘以 36 次事件乘以 2 次评估大约等于 1000 个数字。
但是看看过去 GRR 的分析并非正式地讨论它们的准确性是很有趣的。
那么,他们是如何预测新冠肺炎的呢?事实上,疫情风险每年都出现在 GRR 的左上角象限(低可能性,大影响)。正如许多人回顾过去时指出的那样,专家们早就预测这样的疫情会在某个时候发生,同时也承认在任何一年发生的可能性都很小。
2007-2008 年的金融危机怎么样?2007 年 1 月的报告图表(在危机显现之前)判断最大的风险是资产价格崩溃。所以那是成功的。
我们随便挑一年吧。2013 年的主要风险是长期财政失衡、供水危机、温室气体排放增加、和严重的收入差距。随后几年到底发生了什么?GRR 不再那么担心财政失衡(至少在 2021 年之前)。供水危机还没有成为头条新闻,但在 GRR 的列表中仍然很重要。不断增加的温室气体排放已被重新命名为气候行动失败,在可预见的未来,这无疑仍将是最大的风险之一。严重的收入差距,改名为社会不稳定(对我来说有点意外)随后向中间下降。因此,2013 年没有显著的成功,但反过来说,也没有没有预料到的重大事件。
具有竞争天性的读者可以尝试编写自己的风险列表,与即将到来的 2021 年 GRR 进行比较。我个人最大的猜测包括新冠肺炎引发的财政危机和社会动荡,以及网络攻击。往年,GRR 都是在 1 月中旬举行,但 2021 年的世界经济论坛实体会议目前被推迟到 5 月(而且可能会进一步推迟?),所以我不确定 2021 年的 GRR 什么时候会出现。
所以为什么要在乎呢?
在个人层面上,我们很少有人不考虑个人的未来就过完一天又一天。任何一个阅读 medium.com 的人肯定都会关注当今世界正在发生的一些事情,以及随之而来的对未来的希望和担忧。然而,我们每个人都倾向于只关注未来可能不同的几个方面,隐含地假设其他方面将保持不变。例如,最初的《星际迷航》设想了一个相当乌托邦式的复杂技术未来,但与令人生厌的 20 世纪 60 年代风格的女性形象并置。阅读 GRR 风险清单可能会促使你思考,如果这些风险中的一个或另一个成为现实,你现有的希望和恐惧会受到怎样的影响。
在组织层面上,任何对不确定的未来进行理性规划的尝试都需要将情景规划和概率预测结合起来,这些是互补的而不是对立的:如果你认为某个地缘政治事件有 40%的可能性,你能设计出两个可能发生的情景和三个不可能发生的情景吗?
最后,GRR 举例说明了一个非常广泛的基线原则:在任何不确定的环境中,如果其他人已经考虑过它,并且一个人可以确定一个共识或“中间意见”,那么它作为基线是有价值的。当形成你自己的观点或研究别人的观点时,你或他们能阐明为什么他们与基线不同吗?如果不是,就不要太在意他们的看法。
年度变化图
脚注 1:世界经济论坛因其每年一月在达沃斯召开的会议而闻名(并受到广泛批评)。但它也协调除 GRR 之外的许多分析的生产。我个人猜测,这类报告往往会比学术或政府机构的报告更准确,因为它们的输入范围更广,而且更少受制于特定的利益或意识形态。
脚注 2:随着时间的推移,所研究的风险发生了缓慢的变化,最初考虑的较少。
脚注 3:2007 年的图表有数字可能性和数字美影响。多年来,坐标轴标签已经变得更加定性:对于 2020 年的图表,专家们被要求在 1-7 的模糊尺度上评估可能性和影响,并绘制平均值。这使得评估准确性的尝试更加复杂。
脚注 4 :詹姆斯·邓尼根和奥斯汀·贝的《肮脏战争快速指南。
脚注 5:预测锦标赛涉及陈述给定事件在给定截止日期之前发生的概率,通常是提前 6-12 个月。见本高中水平博览会或本更精采的记述。但是对于中期赛事来说,这样的比赛并不实用,因为需要等到赛事结果确定之后。
使用 BERT 实现 97%准确率的日语多类文本分类
原文:https://towardsdatascience.com/japanese-multiclass-text-classification-with-97-accuracy-using-bert-11b1fdc7c27e?source=collection_archive---------16-----------------------
将 NHK(日本广播公司)节目分为多种类型
康纳·乐迪在 Unsplash 上的照片
您是否有一些日文文本,如客户反馈、用户评论或邮件内容,并希望从手头的数据中提取见解?下面介绍的模型在处理日语文本时,在多类分类方面表现出色。该模型很好地理解了日语,并且可以潜在地用于许多应用领域中的许多目的,例如流失率预测、消费者细分、消费者情绪分析。
P.S:这个模型只需更改 2 行就可以轻松适应其他语言。
数据
我们将使用 NHK(日本广播公司)的节目信息作为我们的数据源。通过 NHK 的节目安排 API ,可以获得全日本电视、电台、网络电台未来 7 天安排的所有节目的【标题、字幕、内容、流派】。
问题陈述
利用节目的标题、副标题和内容,我们将尝试预测其类型。一个节目可以有多种类型,如下所示:
In the above case, we will be assuming this show’s genre is 情報/ワイドショー (Information/ Wide show)
该模型的一个改进将是可以预测每个节目的多个类型,这将使该问题成为多类多目标问题。
【探索性数据分析(EDA)】
我收集了 2021 年 8 月 30 日至 2021 年 9 月 24 日之间播出(或将播出)的 10,321 个独特节目的信息。以下是 10,321 个节目中所有 13 种类型(标签)的分布情况:
作者图片
正如您所看到的,数据是高度不平衡的,因此我们将在将数据分为训练和测试时对其进行“分层”(即:保持测试数据中的标签分布与整个数据集相同),并使用加权 F1 分数作为适合不平衡数据集的准确性度量。
数据处理
我们将数据分为训练(80%)和测试(20%)数据集。我们将在训练数据集上训练模型,并在 10 个时期内跟踪测试数据集的准确性(时期:模型遍历整个训练数据集的 1 个步骤)。产生最高精度的时期将被用作最终模型,并且来自该模型的结果将被认为是模型精度。
型号
我们模型的输入将是每个节目的“标题”、“副标题”和“内容”的连接。有些节目没有上述所有信息,但只要至少有一个字段(标题总是可用)就没问题
模型的输出将是 13 个可用类型的概率分布,我们将把具有最高概率的类型作为模型输出,并将其与真实值进行比较。
伯特
为了训练这个模型,我们将使用 BERT 的一个预训练模型,并针对我们的问题对它进行微调。查看这篇文章,看看伯特是如何工作的。我们将使用🤗拥抱人脸库,它提供了一个获取预先训练的 BERT 模型的接口。我们将要获取的预训练模型是Bert-base-Japanese-v2,它是由东北大学的研究人员使用维基百科中的 3000 万个日语句子训练的。对于微调,我们将使用BertForSequenceClassification模型,因为这本质上是一个序列分类问题。
结果
我们在 Google Colab 的 GPU 运行时环境中训练该模型,因为 BERT 是一个繁重的模型,它在 CPU 上花费的时间明显更长。训练数据以 32 的批量提供给模型,以加速学习并防止 RAM 溢出。模型经过 10 个时期的训练:以下是这 10 个时期的损失和准确性指标:
作者图片
10 个历似乎足够了,因为训练损失和测试精度在该点之后持平。让我们看看我们是如何预测每个标签的:
作者图片
最后,让我们检查混淆矩阵:
作者图片
看起来不错吧?
结论
在本文中,我们使用了一个 BERT 预训练模型,并对其进行了微调,用于多类文本分类,以 97%的准确率将日本电视和广播节目分类为多个流派。
我观察到的一个趋势是,随着训练数据变大,准确性也在提高。2 周的数据产生 94%的准确性,而 1 个月的数据能够产生 97%的准确性。我预计,随着更多数据的收集,该模型可以达到近乎完美的预测精度。该模型的下一步将是预测每个节目的多个流派,这将使该问题成为多类别多标签问题。
如果你在 有任何问题/反馈,请告诉我。您可以在下面找到源代码和数据集:
数据集:https://github.com/danyelkoca/NHK/blob/main/data.csv
代码:https://colab . research . Google . com/drive/12 ezr 2 q 4 mzhe 9m _ Ppv _ RfuqnKt-g 9 yx3f?usp =共享
*本文日文版:【https://qiita.com/dannyk/items/bee0249af1f77bc416d8 *
黑客快乐!
用于数据分析的 JavaScript
原文:https://towardsdatascience.com/javascript-for-data-analysis-2e8e7dbf63a7?source=collection_archive---------12-----------------------
随着网络开辟了合作的新领域,网络的本地语言 JavaScript 是探索数据和交流见解的最佳选择。
图片:瑞奇·罗瑟的 双摆图 。经许可使用。
关于相机的消亡有利于配备相机的移动电话,蔡斯贾维斯曾打趣说:“最好的相机是你随身携带的。”换句话说,便携性和便利性胜过分辨率、颜色、散景、*等方面的技术差异。*如果你想拍照,手里的相机比留在家里的相机要好。
当被问及 JavaScript 与 Python、R 或 Julia 进行数据分析时,我会想到这一点。其他语言都很棒。它们具有强大的功能,如运算符重载和多维数组、强大的数学和可视化开源库以及活跃的社区。很容易争论为什么它们中的任何一个可能比 JavaScript 更适合数据分析。
然而在我看来,语言差异无关紧要。
部分原因是 JavaScript——语言、网络平台和开源生态系统——每年都在变得更强大。浏览器供应商、标准组织、研究人员和大量全球贡献者对 JavaScript 做了大量的工作。当我们说 JavaScript 很快时,这不是侥幸;是因为人做的快。我们都从这一联合努力中受益。
因为 JavaScript 是网络语言,它可以在任何地方运行。就像你口袋里的照相手机一样,几乎每个人都有一个运行 JavaScript 的浏览器——就是你用来阅读本文的浏览器。JavaScript 支持交互式可视化、探索性解释、现场分析、模型模拟、计算艺术、游戏、测验,你能想到的……当有人分享用 JavaScript 实现的分析时,你看到的不仅仅是他们工作的静态快照;你在浏览器中运行它。通过查询数据、调整假设和提问,你可以超越被动阅读。
JavaScript 是我们有史以来最丰富的交流媒介,但由于网络的开放性,它还有更多的东西:一种可以检查和修补的媒介,用于学习和合作。浏览器开发工具允许您查看、进入甚至修改正在运行的代码。
网络让我迷上了编程。我最初是通过查看源代码学习 JavaScript 的:如果一个网页做了一些很酷的事情,我会查看它的源代码并修改它,以了解它是如何工作的,并看看我是否可以重新利用它来制作一些有趣的东西。JavaScript 的即时性和可访问性——以及无尽的可能性——使它令人陶醉。
在过去的二十多年里,激励我继续开发软件的是这样一个想法:当你在网上开发东西的时候,你也在教别人如何开发东西。我们都在这个原始的想法之汤里游泳,给予和接受创造性的灵感。
协作和交流是网络存在的原因。网络是我们工作的方式。这是我们学习的方式。这是我们分享想法的方式。正是 web 使 JavaScript 在数据分析方面表现出色(除此之外还有很多其他方面)。在网络上,我们可以一起实时编辑和运行代码,共享数据的探索性视图来回答问题,并解释概念,几乎没有摩擦。我们几乎可以做任何我们能想象的事情。
因此,我钦佩其他语言中许多强大的功能和库,但 JavaScript 因其可移植性和便利性而成为我的首选。这是分享现场代码最简单的方法,任何人都可以编辑。
当然,还有很多事情要做。JavaScript 可能不是一种用于数据分析的语言,但是这种语言可以扩展以更好地支持它。
我很乐观。JavaScript 在过去十年中有了显著的改进,增加了 async/await、arrow 函数、promises、迭代器、生成器等等。多亏了 ES 模块和像 Skypack 这样的服务,我们可能最终会看到 Node.js 和浏览器之间更容易的库互操作性(或者至少更少的 bundlers、transpilers 和 loaders 问题)。随着 WebGPU、WebAssembly 和其他标准的开发,JavaScript 的未来一片光明。(详见本·施密特的 JavaScript 和数据编程的下一个十年。)
我们还需要新的库和抽象,让我们花更多的时间思考数据,而不是纠结复杂的编程。诸如 Apache Arrow 、 Arquero 、 tidy.js 、 Observable Plot (我也参与了其中)和 Vega-Lite 等开源项目都有所帮助。
用可观察图制作的奥运会运动员体重(公斤)直方图。蓝色代表女运动员;橙色代表男性。
作为另一个用于可视化的开源 JavaScript 库 D3.js 的作者,我听说有人从其他人在网络上公开分享的作品中受到启发,开始学习可视化。有些人甚至以此为职业。
我希望有了更好的工具——特别是支持网络协作的工具——更多的人可以一起释放用数据思考的力量。
JAX 第一印象
原文:https://towardsdatascience.com/jax-first-impressions-d85f3c498c21?source=collection_archive---------36-----------------------
数值计算
Python 中速度惊人的独立于硬件的数字代码
JAX 因服用类固醇而变得愚蠢
最近我一直在探索 JAX 图书馆。它是由 Google 工程师开发的用于数值计算的高性能库。目前,它是一个研究图书馆(还没有 1.0 版),不是谷歌的官方产品。然而,它正在机器学习工程师中获得牵引力。我特别感兴趣的是用 Python 编写数值密集型算法,这些算法在 CPU 和 GPU 上都非常快。
JAX 有一个与 NUMPY 非常相似的 API,尽管它不是 NUMPY 的直接替代品。JAX 包括一个 JIT(实时)编译器,它使用 XLA 来转换基于 JAX 的函数。XLA(加速线性代数)是一个特定领域的线性代数编译器。这使得代码运行速度比普通 Python 实现快 100 倍。
JAX 推广函数编程。JIT 只能编译纯函数。一个纯函数的输出仅仅基于它的输入,并且没有副作用。因此,在 JAX 编程需要一些小心。特别是,JAX 中的数组类型,称为DeviceArray
,是一种不可变的数组类型。一旦创建了阵列,就不能修改。这使得编译器可以轻松地对代码进行推理,而不用担心任何副作用,并积极地对其进行优化。不可能修改数组中的单个条目。不过,JAX 以[index_add](https://jax.readthedocs.io/en/latest/_autosummary/jax.ops.index_add.html)
、[index_update](https://jax.readthedocs.io/en/latest/_autosummary/jax.ops.index_update.html)
和类似操作符的形式提供了功能替代方案来实现同样的功能。
JAX 的一个行 QR 分解
我不会在这里详细介绍 JAX。你可以阅读同样的优秀文档。下面我展示一个我在 JAX 实现的 QR 分解的实现。通常情况下,我们因式分解[A = QR]。因为 Python 是以行为主的,所以我按行进行因式分解[A = RQ],其中 Q 的行是正交的。使用改进的 Gram-Schmidt 过程实现因子分解。
注意在函数factor_mgs
中应用的装饰器jit
。它确保当函数被调用时,它被及时编译。还要注意 Q 中的一行是如何使用index_update
函数更新的。使用index_add
函数更新 A 中的行。
一点基准测试
我在一台 MacBook 上测试了这个功能。对一个 64x128 矩阵(具有正态分布的随机条目)的因式分解的第一次调用花费了大约 12 秒。后来下降到 300 微秒左右。当心 JAX 的异步调度。在结果数组上调用block_until_ready()
,确保所有的计算都正确地完成了基准测试。第一次调用时,128x256 随机正态矩阵的因式分解需要大约 35 秒。随后的尝试减少到大约 1 毫秒。
一个逐行 QR 更新例程
像正交匹配追踪这样的算法可以通过使用 QR 更新过程来优化,其中子矩阵 A 是从字典的原子逐步[逐行]构建的。因此,A 的因式分解也可以逐行建立。下面的函数用逐行分解的矩阵 A 的第 k 行 A 更新 Q 和 R。它使用了 Gram-Schmidt 算法的一个步骤。
让我们展示一些如何使用这个更新过程的代码。
在每次迭代中,我们发送 A 的第 I 行来更新因式分解。Q 和 R 数组被更新并从 update 函数返回。由于 update 的输入参数是不可变的,因此,更新后的数组必须返回给调用者,以确保更新得到反映。XLA 编译器将确保尽可能多地重用设备数组。
注意,我们没有在更新函数定义上直接使用 jit 装饰器。函数的第四个参数 k 是一个整数,它的值在不同的调用中是不同的。从 JIT 的角度来看,它被称为静态参数。不能直接 JIT 编译。然而,在这条线上
update = jit(update, static_argnums=(3,))
我们能够指定哪些参数适合 JIT 编译,哪些必须被视为静态的。这使我们能够在这样的函数上应用 JIT。更多信息,请阅读JAX——锋利的钻头。
笔记
- 默认情况下,JAX 创建 float32 数组并执行 32 位浮点运算。如果您想使用 float64 阵列,请启用该功能。
- 如果您的函数不满足 JIT 的约束,使用它将抛出非常详细的错误。需要一些练习来学习如何编写简洁的数值算法的函数版本。
- 异步调度可能会导致错误的基准。小心确保所有的计算都已完成。
有限分析的 JAX 实现
原文:https://towardsdatascience.com/jax-implementation-of-fea-3e9a690e59c9?source=collection_archive---------50-----------------------
实践教程
和利用神经网络的高效逆问题求解
图片由作者
如果你还没听说的话, JAX 作为一个“服用类固醇的笨蛋”在网上获得了很多关注。就其核心而言,它可以被认为是对 NumPy 的替代,其中数组计算可以在可用的 GPU 或 TPU 上加速。仅此一点就值得一看,尤其是如果您有许多 NumPy 代码,您可能希望通过 GPU 加速来加速。目前, NumPy API 的大部分都是以一一对应的方式实现的,以及 SciPy 中一些最常用的函数。
加速数字仅仅是 JAX 实用化的开始。所有 JAX NumPy 数据结构都可以与大多数纯 Python 代码结合使用,以创建可以自动区分的函数。这包括计算标量函数的梯度,以及向量函数的雅可比矩阵。这些操作可以被组合以计算梯度的梯度等。自动微分功能的更多信息记录在这里。
此外,还有一个内置的实时编译器,用于编译要在 CPU/GPU/TPU 上执行的函数,并支持自动矢量化,即针对标量参数编写的函数可以很容易地跨数组映射。这些可以与前面提到的自动微分功能一起使用。
最后,还有一个与 JAX 相关的非常薄的神经网络库,称为 stax 。其他功能更全面的库,如俳句、亚麻或 Trax 都是基于 JAX 技术开发的。
在接下来的内容中,我将通过实施有限分析(FEA)模型,然后在训练神经网络对潜在未知的本构模型进行逆解时,使用有限残差作为目标函数的一部分,来强调 JAX 的大多数这些特征。
作为一个模型问题,我们将从一维压力扩散方程开始,该方程控制具有流体密度 ρ 和小压缩率 c 的多孔介质中的单相流体流动。
假设稳态,乘以左边的测试函数 δp ,并在域(0 ,L )上按部分积分,我们得到
在哪里
κ 为多孔介质的渗透率, μ 为流体粘度。 λ 被称为迁移率,并被假定为空间变化的。
使用伽辽金近似,即 p = Nᵢ pⱼ 和 δp = Nᵢ 对于 I,J = 1,2,… 基函数,并且将域分割成 n 个区间,我们现在有
其中对于那些在 I ᵗʰ节点上具有支持的函数,隐含了在 J 基函数上的求和。上面的右边是我们的剩余,即 R
下面,我们将使用高斯积分对这个剩余向量进行积分,并求解未知的节点压力 pⱼ 。不失一般性,我们将只考虑 Dirchelet 边界条件,即 q(x) = 0 。
虽然这个模型问题是线性的,但我们将实现 FEA 模型以使用方程的剩余形式,并使用非线性牛顿-拉夫森解算器来求解未知数,其中每次迭代的雅可比矩阵通过 JAX 的自动微分来计算。所有的计算都是以一种可以在 GPU/TPUs 上加速的方式编写的,并且是即时编译的。
下面是我们需要的导入,请注意,我们为 JAX 显式启用了 64 位浮点数,因为默认为 32 位。
下面,我们将通过 FEA 使用上面的实现来解决正向问题,以验证事情是否正常工作,并生成一些参考数据,我们将在后续的反向问题中使用。这里,移动功能是
这里我们将写我们的反问题求解器。我们将从上面的FEAProblem
类继承,这样我们可以重用一些已经定义的函数。
当“已知数据”作为训练数据提供时,我们的目标函数将是有限残差的 l ₂-norm。因为我们正在解决的问题是一个稳态问题,我们将需要向目标函数提供本构模型的端点,否则有无限个有效的解来学习仅相差一个常数的本构模型。如果我们把这种技术扩展到与时间有关的问题,我相信可以避免提供边界约束的需要。
我们将使用jax.experimental.stax
模块中的一些函数,只是为了使神经网络的构建更容易。我们这里的最小化器将使用来自jax.scipy.optimize.minimize
的二阶"BFGS"
方法。
这里,我们假设我们的数据是在有限模型的节点处提供的,但是通过有限形状函数在任何给定的空间位置评估残差,可以很容易地概括这种限制。
下面我们将使用前面的有限解生成的数据来测试我们的反问题求解器。首先,我们定义我们的神经网络架构。这是一个相当简单的函数,因此我们不需要大型和/或复杂的神经网络。这里,我们有一个只有 4 个节点的输入层和一个 tanh 激活函数,它提供给单个节点输出。更复杂的架构也可以工作,以更多的计算成本产生相同的结果。
我们还需要定义层Dense64
,它与stax.Dense
相同,但被初始化为使用 64 位浮点,以与我们在 FEA 残差计算中的数据结构保持一致。
现在我们实例化模型并求解逆问题,即训练网络。我们必须提供本构模型的端点。假设问题是抛物型的,那么反问题就有无穷多个解(它们都有相同的形状,但是有一个恒定的比例因子)。我们可以通过考虑一个依赖于时间的问题并提供依赖于时间的训练数据来消除这种限制,我们将把它留给未来的工作。
在区域范围内绘制神经网络函数,并与参考进行比较,我们可以看到逆解算器已经很好地“学习”了迁移率函数。
只是为了验证,我们将使用我们的神经网络作为我们的正向有限解算器中的迁移函数,以证明产生的压力也是准确的。
这种方法相对于例如基于物理学的神经网络的主要优势在于,我们仅“学习”了本构模型,即迁移率函数,而不是具有所提供的边界条件的偏微分方程的解。相反,我们依靠我们的有限实现来计算解决方案。这意味着我们现在可以使用我们“学到的”本构模型来精确地解决不同边界条件的问题。
最初发表于【https://johnfoster.pge.utexas.edu/blog/jax-fea/】。
使用 GPT 的爵士乐一代
原文:https://towardsdatascience.com/jazz-music-generation-using-gpt-a7ed52b0f468?source=collection_archive---------27-----------------------
亚历克斯·萨莫拉的爵士之夜
实践教程
使用 GPT 和钢琴卷帘窗编码方法生成爵士音乐
生成式预训练转换器或 GPT 模型在处理自然语言处理(NLP)任务时取得了惊人的结果。然而,该模型结构并不是 NLP 所独有的,并且已经被用于解决其他问题,例如时间序列预测或音乐生成。在这篇文章中,我将分享我使用一个非常简单的 GPT 模型来创作音乐(特别是爵士乐)的方法。
目录
- 背景
- 钢琴卷帘窗编码方法
- 数据预处理和分析
- GPT 模型
- 培养
- 推理
- Web 应用程序
- 结论
背景
音乐生成任务已经在过去使用深度神经网络解决,例如 RNN 特别是 LSTM 或 CNN 以及最近的 Transformer。在某种程度上,解决这个问题的方法很大程度上受 NLP 的影响,因为歌曲中的音符和段落中的文本之间的结构非常相似。与文本相比,处理音乐的主要区别在于信息编码步骤,我们将在后面探讨。
此外,我选择爵士乐的原因是因为它的不可预测性(爵士乐也是我最喜欢的音乐类型之一)。正如他们常说的,在爵士乐中没有“错误”的音符,特别是在即兴创作中,我很好奇当在爵士乐数据集上训练时,预测的音乐听起来会是什么样子。对于这个项目,我们将只关注使用钢琴乐器产生爵士乐。
所有的预处理和培训笔记本都可以在最后找到!
钢琴卷帘窗编码方法
通常,midi 对象包含相当多的信息,根据您使用的库,您可以从 MIDI 文件中提取不同的数据或相同的数据,但格式不同。另一方面,MIDI 音符是非常标准的,因为它是 MIDI 乐曲的组成部分。它包含以下信息:速度,音高,开始时间,结束时间。
就像我之前提到的我们如何转换 NLP 模型来解决音乐生成任务,我们需要以一种相对类似于 NLP 中单词编码的方式来编码这些 MIDI 音符信息(NLP 通常使用基于索引的编码来嵌入单词)。然而,对于 MIDI,我们需要表现每个音符多达 4 个特征,其中也包括时间特征。
基于索引的单词编码和嵌入— 文本编码:综述 [1]
为了解决这个问题,我们决定将 MIDI 转换为钢琴卷帘窗格式,采样间隔为每 16 个音符。因此,钢琴卷首是大小为(song_len,128)的 2D 数组,其中 song_len 是歌曲中第 16 个音符的总数,128 是 MIDI 歌曲中可能的音高数。
MIDI 流(左)转换为钢琴卷帘窗阵列(右)的示例
这种数据编码方法表示每个恒定时间间隔的音符,因此,允许我们将整首歌曲表示成一个紧凑的 2D 数组。从这里,我们可以实现一种类似的单词编码方法,即基于索引对每个音高组合进行编码,然后将它们送入嵌入层。
我们决定不包括速度特征,因为这将导致我们的音高组合词汇爆炸。第 16 个音符是最佳的音程,因为它可以足够准确地表现音乐细节,同时也可以防止我们的钢琴卷阵列变得太长。
理解了这种方法之后,让我们开始研究代码吧!
数据预处理和分析
对于我们的数据集,我们选择了 Doug McKenzie 爵士钢琴数据集。虽然这个数据集中只有大约 200 首 MIDIs,但它包含了各种各样的流行爵士歌曲,而钢琴部分通常是干净的,连贯的,很少有缺失的部分。
由于数据集中的所有歌曲都有不同的调号,并且在不同的 BPM 中播放,因此我们进行数据预处理,以便使这些特征标准化。这个标准化步骤很重要,因为它不仅允许模型更好地理解歌曲的结构和模式,而且有助于减少我们的模型稍后的词汇量。
我们使用 Pythonpretty-midi和 music21 来辅助数据解析和处理步骤。为了提取钢琴部分,我们过滤掉了包含最多音符的流(因为这是钢琴流的常见情况)。extract_midi_info()函数将帮助我们获取需要偏移的调号以及钢琴卷帘窗的 bpm。
preprocess_midi()函数将帮助我们使用 pretty_midi get_piano_roll 函数获取钢琴卷帘窗数组。
对于预处理的最后一步,我们遍历数据集中的所有 MIDI,解析、预处理并将钢琴卷帘窗数组保存为。npy 格式。
GPT 模型
对我们的数据进行编码后,我们现在可以将其输入到 GPT 架构中,以训练一个自回归模型。如果你不太确定 GPT 是如何工作的,我推荐你读一下杰伊·阿拉姆马的这篇博文【2】,它非常详细,见解深刻,我从中学到了很多。
简而言之,GPT 仅利用变压器架构的解码器模块,并将这些解码器模块堆叠在一起,以增加网络的复杂性。
GPT 架构由堆叠的变压器解码器模块组成— 通过生成式预训练提高语言理解
以下 GPT 模型的代码引用自 Apoorv Nandan 用微型 GPT【3】生成的文本。
对于令牌和位置的嵌入,我们使用了正弦和余弦函数。
位置编码—你所需要的只是注意力【4】
随意掩饰的自我关注
变压器组
最终模型。
模型摘要:
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 600)] 0 _________________________________________________________________ token_and_position_embedding (None, 600, 128) _________________________________________________________________ transformer_block (Transform (None, 600, 128) 99584 _________________________________________________________________ transformer_block_1 (Transfo (None, 600, 128) 99584 _________________________________________________________________ transformer_block_2 (Transfo (None, 600, 128) 99584 _________________________________________________________________ dense_18 (Dense) (None, 600, 40000) ================================================================= Total params: 10,578,752 Trainable params: 10,578,752 Non-trainable params: 0
培养
为了训练我们的模型,我们首先需要从数据集创建训练输入和输出。因此,我们首先将每个独特的音高组合分配给一个整数,如前一部分所述。对于我们的词汇表,我们只考虑前 40000 个最常用的音符组合,包括未知和填充标记。最后,我们对输入数据的钢琴卷首数组进行标记。
我们还将创建我们的定制生成器。该模型的输入将是固定 sequence_length 的符号序列,输出将是相同长度但向右移动一个符号的序列。对于每个时期,我们将遍历数据集中的所有歌曲,在每首歌曲中,我们选择一对输入和输出序列在随机位置进行训练。如果需要的话,我们也可以使用填充。
终于准备好训练了!我们实验了 1500 个时期,32 个批量大小和 600 个序列长度。
推理
为了预测一首歌曲,我们需要给它输入一些开始音符,并填充到与训练序列长度相同的大小,即 600。
得到钢琴曲后,我们可以接着转换成 MIDI。这里有几个推理样本,前 5 秒或 10 秒是种子。
这首歌已经播了 10 秒了
网络应用
我和我的团队还创建了一个 web 应用程序来展示我们的建模能力。我们将 React JS 用于 web 应用程序,将 Flask 服务器用于推理任务。为了部署该产品,我们使用 Google 云服务来托管 React web 应用程序和 Flask 推理服务器。
这里有一个 web 应用程序的简短视频演示,不幸的是,由于我的谷歌云平台信用已经用完,应用程序本身不再可用:(
web 应用程序的演示
我们的 web 应用程序的快照
结论
尽管只有大约 200 首歌曲的小数据集,我们还是设法开发了一个可以很好地预测爵士音乐的模型。然而,在评估过程中仍然有一些过度拟合的迹象,尽管我们试图增加辍学率,但我们相信这个问题会在更大的数据集上得到解决。
最后,这是我的第一个数据科学项目,它真正教会了我这个项目是为计算数据科学模块而做的。我们这个团由许鞍华、肖恩·林、西德哈斯·普拉文和梅组成。特别感谢我的团队成员和我的教授 Dorien Herremans 和 Soujany 茯苓的帮助和指导。
源代码:
- N 预处理 otebook
-N训练推理 otebook
-项目 Github
参考文献: 【1】西里坡,罗莎丽亚。“文本编码:回顾”,2019 年 11 月 21 日。https://towards data science . com/text-encoding-a-review-7c cccf。
[2]阿拉马尔,杰伊。"图解 GPT-2(可视化变压器语言模型)."图解 GPT-2(可视化变压器语言模型),2019 年 8 月 19 日。http://jalammar.github.io/illustrated-gpt2/.
[3]南丹,阿波洛夫。" Keras 文档:用微型 GPT 生成文本."用微型 GPT 生成文本,2020 年 5 月 29 日。https://keras . io/examples/generative/text _ generation _ with _ miniature _ GPT/。
[4]瓦斯瓦尼、a、n .沙泽尔、n .帕马尔、j .乌兹科雷特、l .琼斯、A. N .戈麦斯、l .凯泽和 I .波洛苏欣。“你需要的只是关注。arXiv 2017。” arXiv 预印本 arXiv:1706.03762 (2017)。
詹金斯为 CI 死了:为什么人们讨厌它,有什么替代方案?
原文:https://towardsdatascience.com/jenkins-for-ci-is-dead-why-do-people-hate-it-and-whats-the-alternative-8d8b6b88fdba?source=collection_archive---------2-----------------------
如何自动建立你的 Docker 图像;案例研究。
艾米莉·法里斯在 Unsplash 上拍摄的照片
Jenkins 是一个独立的、开源的自动化服务器,提供了用于 Windows、Mac OS X 和其他类似 Unix 的操作系统的软件包。
如果你访问该项目的登陆页面,它会告诉你 Jenkins 是领先的开源自动化服务器,拥有数百个插件来支持任何项目的构建、部署和自动化。
这种说法可能是对的,但这并不意味着 Jenkins 提供了一种简单直接的方法来实现这一切。相反,它提供了数百个插件,如果你想完成任何事情,你都需要设置这些插件,这使得这个项目既通用又复杂。
在这个故事中,我们看到了为什么如果你想在 2021 年建立一个持续集成(CI)管道,Jenkins 不是合适的选择,以及有什么替代方案。如果你有任何疑问,可以在评论区开始讨论!
Learning Rate 是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
自动构建您的 Docker 图像
我相信很多阅读这篇文章的人都喜欢詹金斯。我相信你们中的很多人现在都很愤怒。也许你已经花了几个月的时间来学习 Jenkins,并真正深入到了它的内部。如果你处在类似的位置,我可以理解你为什么还不准备放弃。
为了试图说服你,我将用一个简单的例子。今天我将介绍最流行的 CI 用例:如何自动化构建和推送 Docker 映像的过程。
然而,这不是一个关于如何用 Jenkins 构建和推送 Docker 映像的教程。相反,我将只提供一个高层次的观点。因此,假设您已经有了一个将代码推送到 Github 的存储库,下面是实现最终目标所需的步骤:
- 安装 Jenkins: Jenkins 通常作为一个独立的应用程序在它自己的进程中运行,带有内置的 Java servlet 容器/应用服务器(Jetty)。但是,您也可以使用官方 Docker 映像在容器中运行 Jenkins。
- 与 GitHub 集成:你可以通过浏览器(通常在
localhost:8080
上)访问 Jenkins 并创建一个新项目。您应该指定 GitHub 存储库 URL 地址,何时触发构建,以及目标命令。 - 安装 Docker 插件:要构建图像并将其推送到存储库(例如 Dockerhub),您需要安装一个新的插件。例如,您可以安装 Docker 插件、Docker 构建步骤和 CloudBees Docker 构建和推送插件。
- 配置 Docker 插件:您应该在 Jenkins UI 中指定一个新的构建步骤,利用新安装的插件。此外,您应该指定您想要将图像推入的注册表、图像名称和标记以及您的凭证。
为什么这样不好?
现在我们已经看到了这个过程,你可能会说这还不算太糟。只需四步。然而,有些步骤并不容易跨越:
- Jenkins 实例通常太复杂。这意味着组织需要一名 Jenkins 专家来安装、维护和保护实例。
- 专家成为瓶颈。作为开发人员,您依赖 Jenkins 专家或管理员来创建新项目、构建、发布等。这很快成为一个漫长的过程。
- 自助服务寻求者将创建新的、不安全的 Jenkins 实例来解决专家瓶颈问题。此外,他们还会不经测试就安装新插件,引入漏洞。
- 通常,在开始使用 Jenkins 之前,你需要安装大量的插件。不幸的是,这使得实例过于复杂,难以导航,而且 Jenkins 控制器的速度也慢了下来。
- 您可能不希望在自动化工具上做这么多手工工作。Jenkins 很难有效地扩展。并不是所有的插件都受
Jenkinsfile
支持,所以一个 Jenkins 实例很难在没有手工操作的情况下备份。此外,数据库只是磁盘上的 XML 文件,这使得纵向扩展或横向扩展或执行高可用性升级成为一场噩梦。 - Jenkins 需要一台专用服务器(或几台服务器)来运行。这导致了额外的费用。通常情况下,一个组织需要有一个 DevOps 团队专门负责 Jenkins。
有什么选择?
对于 CI,也许不仅仅是 CI,我更喜欢使用 GitHub 动作。那么,如何用 GitHub 动作构建和推送 Docker 图片呢?让我们看看步骤:
- 设置:在项目的根文件夹中创建一个新文件夹。把它命名为
.github/workflows
。 - 配置:在您创建的
workflows
文件夹中添加一个新的 YAML 配置文件。这个文件看起来有点像这个。一系列步骤,每一步都做一件简单的事情。 - 认证:将你的注册中心(例如 Dockerhub)的证书作为一个加密的秘密添加到你的项目中。
- Run:开始这个过程所需要的只是将新的代码提交到 GitHub。
最重要的事?无需安装、配置、维护、升级或修补。一切都运行在 GitHub 服务器上,你不必在意。此外,有了 GitHub marketplace (把它当成 Jenkins 插件的替代品),你几乎可以做任何事情。
结论
Jenkins 是领先的开源自动化服务器,拥有数百个插件来支持任何项目的构建、部署和自动化。
然而,Jenkins 的方法并不是建立 CI 渠道的简单直接的方法。安装、维护、插件、安全,等等,使得过程变得缓慢,制造瓶颈,并引入漏洞。
这个故事展示了使用 Jenkins 和 GitHub 操作自动构建和推送 Docker 图像所需的步骤。在我看来,使用像 GitHub Actions 这样的工具就像用 Jenkins 的方式一样。你有什么看法?
关于作者
我叫 Dimitris Poulopoulos ,是一名为 Arrikto 工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
拼图玩具人工智能从 A 到 Z
原文:https://towardsdatascience.com/jigsaw-puzzle-ai-from-a-to-z-b4bdb53d8686?source=collection_archive---------6-----------------------
使用基本的人工智能工具组装任意现实生活中的谜题
作者图片(由卡斯特兰拼图公司的“北极冰层下的生活”制作)
介绍
自人工智能早期以来,我们已经看到了处理拼图难题的多种尝试。然而,爱好者主要集中在特定的方面:只有方瓦,只有非扫描,单色等。
在这里,我们来看一下全面的方法,这种方法在非常简单的同时处理了整个问题。这项工作的想法是展示我们如何能够利用今天的基本人工智能工具来处理拼图游戏解决方案的所有方面。
我们将使用 Google Colab 和 Python 来完成这项工作,这样每个人都能够轻松地理解和复制它,而无需任何特殊的软件。
“用 300 行代码在 60 秒内解决”(图片由作者提供)
概观
我们将解决 15 块瓷砖的难题。对于计算机来说,这是一个复杂的问题,因为复杂性主要不是来自瓷砖的数量,而是来自瓷砖的几何形状。我们的拼图是不均匀的圆形,并且有扭曲的弯曲边缘。
提出的方法是为了证明我们可以不局限于任何特殊类型的瓷砖。反之亦然,这个想法是用任何几何形状的瓷砖来解谜。
我们将浏览 300 行 Python 代码。不过,它只涵盖了 3 个主要步骤:
- 图像处理。它处理如何从扫描中提取瓷砖。
- 匹配。这是如何找到匹配的瓷砖以及匹配方式的核心部分。
- 组装。这是一个最终的算法,把瓷砖拼凑成一个整体的图像。
数量少得惊人的基本模块将足以解决我们的彩色拼图。
图像处理
我们将零散瓷砖的扫描图像作为输入。它必须是一个良好的扫描没有明显的扫描仪伪影,如彩色条纹或黑边。我的扫描是 A4 格式,重采样为 727 x 1000 像素,大约相当于 90 dpi。
首先,我们将图像绘制封装到函数中,以进一步简化代码。我们想关闭坐标轴,为单通道图像切换到“灰色”色图,等等。
然后,我们加载一个扫描,并使其 RGBA 进一步参与透明度维度。
作者图片
我们可以看到天蓝色背景上的彩色瓷砖。为了能够对瓷砖做任何事情,我们必须首先检测它们。这是一个单一的图像,我们必须把它变成 15 个独立的瓷砖。检测单色背景上的对象的一种可能方法是自适应阈值处理。我们将对我们的图像应用adaptiveThreshold()
工具,以便从背景中分离瓷砖。GaussianBlur()
是可选的,但在这里是必要的,因为有些瓷砖有白色边缘,这些边缘与背景融为一体,产生我们必须填充的裂缝。
作者图片
这看起来不错。尽管如此,我们也无能为力。我们需要的是一个清晰的二值图像,其中每个像素要么是瓷砖,要么是背景。因此,我们将使用 OpenCV 轮廓检测和填充技术来完成这个简单的技巧。为了抑制由绘画形成的额外轮廓,我们按照长度降序对检测到的轮廓进行排序,并取 15 个最大的轮廓(手动,因为为了简化代码省略了自动化)。
作者图片
还不好。我们可以看到瓷砖有粗糙的边界和由原始扫描图像中的阴影形成的突出尖峰。抑制尖峰的典型方法之一是中值滤波,即将每个素转换为周围素的平均样式。换句话说,如果你的同伴都很矮,而你很高,你也会变矮:)。在median_filter()
之后,我们通过画一个黑色的轮廓来修整我们的形状。这是为了去掉上面模糊操作产生的阴影和多余的像素。
作者图片
嗯,那很好!现在图像是二进制的:瓷砖对背景,没有半色调。我们可以将它叠加在原始图像上,一个接一个地提取彩色瓷砖。另一个聪明的工具boundingRect()
将帮助我们从大图像中切割出想要的区块。它的作用是检测包含一个形状的最小包围盒。
作者图片
我们终于拥有他们了!15 块彩色瓷砖,位于 300 x 300 图像的中心,背景透明。前奏结束了,该是主幕了!
相称的
匹配的关键思想是拿一对瓷砖,在它们的轮廓中寻找相似的部分,沿着那些部分比较颜色,然后尝试锁定那些部分而不丢失像素。
我们从重新调整我们的瓷砖开始。让我们把它们放在 1400 x 1400 的画布上,这是我们将用来组装整个拼图的画布。这只是一个技术操作,没有任何秘密含义。
在匹配算法中将使用四个辅助函数。所有这些都基于基本的 2D 几何学:
getColors()
被设计成沿着图像的子区域拍摄彩色像素。在子区域的每 3 个点上,我们取 2 个点与子区域的内部和外部正交,深度为 3 个像素(因为我们不知道瓷砖的确切位置),将颜色转换为 HSV 并添加到列表中。putOnAnvil()
将输入图像作为 NumPy 数组,并使用 PIL 方法对其进行偏移/旋转,这只是一个技术问题。我们使用它来旋转和重新定位画布瓷砖,将其子上下文中心放入图像中心,以与匹配的瓷砖融合。- 在组装或装配过程中,当我们移动和旋转拼图和拼块时,
rotatePoint()
有助于跟踪拼块中心。 reScale()
又是一个技术上的东西,用来把点坐标从平铺图像的(300,300)平移到拼图画布的(1400,1400)。在两个空间中工作是必要的,以节省时间而不处理额外的零像素。
所有进一步的代码请记住,OpenCV 工作在(x,y)坐标域,而其他模块是(y,x)。因此,我们将不得不不断地交换和翻转。
在我们深入研究匹配算法之前,让我们先来看看我们将使用的一些主要概念。当我们进行匹配时,我们会谈到图块 A 和 B、子区域和子区域中心(pointA 和 pointB)、边界矩形和图块中心、最小面积矩形及其中心(cA 和 cB)和角度(angleA、angleB)、typepointA 和 typepointB 来说明它是 tab 还是空白、画布中心(“anvil”)以及我们用于颜色匹配的近子区域点。
关键概念(图片由作者提供)
然后我们来看匹配算法本身。看起来很吓人。尽管如此,结构还是很简单。我们用 OpenCV findContours()
提取两块瓷砖的轮廓。然后,我们取图块 A 的小子图,并将其与图块 b 的小子图进行比较。子图是通过主轮廓的滚动和切片得到的。
我们一个接一个地经历 3 个匹配循环(轮廓匹配、颜色匹配和预拟合)。名字是不言自明的,而想法是不断减少匹配的数量,通过某些标准过滤掉不好的。例如,两个外形相似的轮廓,可能在颜色上完全不相容,所以它们甚至不会到达试衣间。此外,试衣间非常耗时,因此我们希望最少数量的匹配能够通过筛选。
让我们来看看每种情况下都发生了什么:
- 轮廓匹配。这是用公开的简历
matchShape()
完成的。虽然对于曲线来说不是 100%好(我更喜欢用我的方式对待那些),我们在这里使用它来简化故事。在匹配之前,我们检测子上下文类型(标签或空白)和一般外观(使用minAreaRect()
)以节省时间和避免明显的失败。正确检测旋转角度需要两个辅助标志(“共线”和“共线”)。这是因为minAreaRect()
只返回象限 III 中的(0,-90°)角度,而matchShapes()
根本不返回任何角度。尽管如此,还是有出路的。 - 配色。它遍历过滤后的表单匹配,沿着每个子上下文获取色点,并将它们与
fastdtw()
距离度量进行比较(在这里解释为)。关键是我们必须将颜色转换成 HSV 格式。拼贴的原始 RGB 可能会产生误导,因为 130 与 190 一样接近 160,交换两个通道可能会给你相同的度量,但颜色完全不同。HSV 会做得更好。 - 预装配。这是一个直接尝试融合瓷砖,使用火柴达到这一水平。我们取两个画布块,将它们的子上下文中心叠加在画布的中心,旋转适当的角度,并计算度量。如果图块匹配良好,我们将拥有最小的像素损失(图块不重叠)和最小的结果对轮廓长度(关节边相互贴在一起并隐藏起来不被测量)。一个更健壮的版本包括子区域的叠加和交集比率的计算(此处省略)。
匹配算法的参数化将需要从难题到难题的手动调整:
LENGTH
。这里的子节点长度是 160,大致接近最小节点长度。对于拼图来说,它必须是可变的,因为拼图的大小变化很大。PRECISION
。这只是一个粗略的过滤器,以消除明显的不匹配。精度允许在子区域边界矩形的尺寸上有差异。它必须非零,因为图像处理不是 100%准确。STEP_A
、STEP_B
。步骤只是我们用来获取另一个分包合同的转移。值 1 是一个梦,但它会永远循环,你必须寻找妥协。MAX_*.
这些参数决定了相应指标的上限。它们主要取决于分辨率和子区域长度。
现在我们在一个循环中为所有可能的配对运行匹配算法。105 对中每对大约 0.5 秒。在这里不到一分钟,但对于 128 块 8192 对的拼图来说,需要一个小时。因此,要快速处理大型拼图,我们需要优化(numba、并行线程)以及算法技巧,如早期拼图分组、预组装等。
得到的匹配列表包含关于图块编号、滚动值、子区域中心坐标、旋转角度和度量的信息。如果没有满足我们限制的匹配,我们的结果将是空的。我们也可能会错过一些比赛,瓷砖可能会在组装过程中锁定。
当我们的匹配算法从图块 0 转到图块 14 时,我们只记录升序对,如(1,5)、(2,6)等。但是,如果(1,5)匹配,那么(5,1)也匹配。这就是为什么我们把比赛展开成一个完整的列表,翻转成对。最后,我们按照配对和匹配度量对匹配列表进行排序,得到如下结果:
作者形象
装配
组装的关键思想是通过匹配找到并在画布上有一个图块,尝试将 B 图块锁定到它,根据匹配信息移动和旋转拼图和添加的图块。
也就是说,我们通过匹配子块的中心来获取匹配块,在画布中心将一个点叠加在另一个点上,并按适当的角度旋转块。为了保持对旋转和定位的控制,我们总是在画布的中心做这个动作。我们可以认为这是把每对新的中心放在画布的中心,就像放在铁砧上融合一样。这使我们能够在装配过程中正确旋转和跟踪轮廓和图块中心坐标。
我们将使用一个有用的函数来简化组装算法。当我们旋转拼图和瓷砖时,它的作用是在画布上记录瓷砖的中心和角度。
我们的拼图有 22 个关节,但是匹配算法返回了 37 个匹配。这意味着我们必须在装配过程中过滤正确的零件。我们通过控制像素损失来实现这一点。如果我们向拼图中添加一个新的图块,它出错并部分重叠,这将导致颜色像素的丢失。当像素损失低于某个比率(在我的例子中是所添加图块的 f.i. 10%)时,我们接受该图块,如果它更高——可能我们的匹配是错误的,我们必须尝试另一个。
为了简单起见,我们在这个算法中放弃替换瓷砖,最多只做 10 次尝试来组装拼图,或者在画布上达到 15 个瓷砖时退出循环。这意味着,如果我们放错了瓷砖,或者把正确的瓷砖放错了,我们就不能回去重新做。然而,对于这个难题,这就足够了。
作者形象
嗯,很管用!虽然…图像看起来有点破旧。这是由于多次旋转过程中信息失真造成的。此外,不要忘记我们从一开始就在低分辨率下工作。至少,我们可以看到水下的人们在微笑:)
现在,我们找到了解决方案,可以用找到的匹配项标记原始切片。对于每一对,我们在锁的位置画出一个特定颜色的圆圈,并在两者里面放一个匹配的数字。
作者形象
如您所见,并非所有制表符和空格都已标记。这是因为 14 把锁就足够组装这个拼图了。其他人会自动锁定。
结论
在本文中,我们已经经历了拼图游戏解决的整个周期。它清楚地表明,我们可以做到这一点,而无需深入学习,简单地将算法逻辑与当今的高性能人工智能工具相结合。
许多解释和一些算法(如检测瓷砖的数量,在组装过程中更换瓷砖等)被删除,以使故事简短,并保持重点放在总路线上。
虽然这不是最终的方法,但它是全面的,并显示了我们可以使用当今简单的负担得起的工具来处理现实生活中的问题的方法。
学校几何和基本 Python AI 的知识恰好足以解决一个问题,这个问题就在昨天还令人头疼。
在:GitHub \ Jigsaw-Puzzle-AI找到源文件
金贾+ SQL = ❤️
原文:https://towardsdatascience.com/jinja-sql-️-7e4dff8d8778?source=collection_archive---------3-----------------------
来源:特朗普来自 Pixabay
用于可维护、可测试数据分析的宏
SQL 是分析师的面包和黄油。它强大、富有表现力、灵活——但是一门语言给你的力量越大,你就有越多的方式搬起石头砸自己的脚。
更好的抽象可以帮助我们做到这一点。如果我们能够抽象出我们一直使用的代码,那么我们只需要编写并检查代码一次。有了正确的抽象,您还可以开始考虑单测试 SQL,这在专业分析中做得不够,但应该是标准实践。
Jinja 宏 特别适合对 SQL 进行抽象——如果您决定为您的数据组织使用类似 Fishtown Analytics 的 数据构建工具——那么我们将专门探讨如何使用 Jinja。但是这里的经验应该是通用的,超越任何一个特定的工具。
你可以跟随我在 这个 Git repo 中包含的一些玩具用例。本文中与回购中的文件相对应的代码片段将以类似如下的内容开头:
-- path: macros/volume_by_demographic.sql
用例#1:参数化查询
这里有一个场景:假设您有一个星型模型,有一个users
表链接到各种其他表,如purchases
、sessions
、marketing_pushes
等。
来源:作者
你可能想知道purchases
的销量是如何随着人口统计而变化的。该查询可能如下所示:
-- Get purchase volume by demographic select users.age_group, users.region, count(*) as purchase_volume from purchases join users on purchases.user_id=users.user_id group by 1,2
到目前为止很简单。但是,如果我们希望用户会话的数量减少相同的维度呢?
我们会做:
-- Get session volume by demographic select users.age_group, users.region, count(*) as session_volume from sessions join users on sessions.user_id=users.user_id group by 1,2
这是很多重复的代码!当我们可以用一个抽象来编码关于数据模型的基本假设时,为什么要写同样的东西两次呢?
让我们写一个宏。这些宏片段具有用粗体表示的宏逻辑,当调用宏时,变量名在查询的最终“呈现”中被替换为字符串值。为了更好地理解语法,查看一下 Jinja 模板设计器文档 。
-- path: macros/volume_by_demographic.sql{% macro volume_by_demographic(entity_table, count_col_name) %}select users.age_group, users.region, count(*) as {
{ count_col_name }} from {
{ entity_table }} join users on {
{ entity_table }}.user_id=users.user_id group by 1,2{% endmacro %}
使用这个宏,我们对购买量和会话量进行的最后两个查询可以通过简单的调用完全呈现出来:
-- path: models/purchase_volumes.sql{% from 'volume_by_demographic.sql' import volume_by_demographic %}-- Get purchase volume by demographic {
{ volume_by_demographic('purchases', 'purchase_volume') }}
或者:
-- path: models/session_volumes.sql{% from 'volume_by_demographic.sql' import volume_by_demographic %}-- Get session volume by demographic {
{ volume_by_demographic('sessions', 'session_volume') }}
在 repo 中尝试一下,看看值是如何交换进来以呈现最终查询的!
>> ./render.py models/session_volumes.sql
现在,如果有一个业务范围的决定,即“人口统计”的概念不应该只是年龄组和地区,因为“地区”太粗略,无法做出任何可操作的决定,会怎么样?
你可以直接回到宏代码中,把“区域”改成“州”或者什么的,并保持宏的最终调用点不变。你只需要在一个地方做出改变。
你所做的抽象是为业务分析实现一个接口,在这里你可以交换后端实现,让你的分析逻辑的消费者不受影响——留给你一个 SQL 代码库,它很容易随着动态需求而改变。
正如我们将会看到的(我们已经犯了一些错误),构建接口是棘手的,但是从某个地方开始总比没有接口好。
用例 2:可组合查询
SQL 允许您在子查询或公共表表达式中嵌套逻辑(甚至是递归的),让您可以无限地将一组记录与另一组记录链接起来。
像这样的查询可能会令人毛骨悚然,即使是我们正在探索的完美的玩具示例。例如,假设我们希望将每个人口统计组的会话量与购买量相关联,以了解每次会话的预期购买量。
在原始 SQL 中,这可能看起来像:
with purchase_volumes as (select users.age_group, users.state, count(*) as purchase_volume from purchases join users on purchases.user_id=users.user_id group by 1,2),session_volumes as (select users.age_group, users.state, count(*) as session_volume from sessions join users on sessions.user_id=users.user_id group by 1,2)-- Get purchase/session ratios for each age_group*state combinationselect age_group, state, purchase_volume / session_volume as ratio from purchase_volumes join session_volumes on purchase_volumes.age_group=session_volumes.age_group and purchase_volumes.state=session_volumes.state
这是一大堆代码,我们声明的每一项都是潜在的出错点。 可证明的事实是 复杂性使得像基于 SQL 查询的决策过程这样的系统对小错误更加敏感,这一点在专业分析领域经常得到证实*。*
抽象化缩小了 bug 的表面区域。上面的查询可以归结为:
*-- path: models/purchase_sessions_ratio.sql-- Get purchase/session ratios for each age_group*state combinationselect age_group, state, purchase_volume / session_volume as ratio from ( {
{ volume_by_demographic('purchases', 'purchase_volume') }} ) p join ( {
{ volume_by_demographic('sessions', 'session_volume') }} ) s on p.age_group=s.age_group and p.state=s.state*
更不用想了!
现在,我们可以只关注转换率逻辑,同时相信基本的体积计数逻辑是正确的。更少的移动部件=更少的破碎系统。
试试在 回购 :
*>> ./render.py models/purchase_sessions_ratio.sql*
用例 3:可扩展的组合
抽象让你比我们已经建立的更加灵活。如果我们不看会话与购买的比率,而是看营销推送与会话的比率,以及和营销推送与购买的比率,会怎么样?
我们可以对我们处理的最后一个 SQL 块进行宏化,这样它就可以将不同的表引用作为参数!我们将有一个numerator_vol_ref
来指定哪个表的计数应该是输出比率的分子,还有一个denominator_vol_ref
来指定相同的分母。
*-- path: macros/volume_ratio.sql{% macro volume_ratio( numerator_vol_ref, denominator_vol_ref ) %}select age_group, state, N / D as {
{ numerator_vol_ref }}_over_{
{ denominator_vol_ref }} from ( {
{ volume_by_demographic(numerator_vol_ref, 'N') }} ) p join ( {
{ volume_by_demographic(denominator_vol_ref, 'D') }} ) s on p.age_group=s.age_group and p.state=s.state{% endmacro %}*
现在,如果我们想再次计算购买-会话比率,我们可以问:
*-- path: models/purchase_sessions_ratio_2.sql{%- from 'volume_ratio.sql' import volume_ratio -%}-- Get ratio of purchases to sessions for a demographic group {
{ volume_ratio('purchases', 'sessions')}}*
但是,如果我们想查看漏斗中的一系列步骤,并且这些步骤是动态的,那该怎么办呢?
输入:Jinja 控制流。我们可以在 SQL 模板中执行循环和 if-conditionals,这是一个超能力。
让我们创建一个引用最后一个逻辑块的宏,但是让我们将一个denominator_vol_ref
和一个numerator_vol_ref
的整个数组作为参数。
*-- macros/volume_ratios.sql{% macro volume_ratios(numerator_vol_refs, denominator_vol_ref) %}select age_group, state, {%- for ref in numerator_vol_refs -%} {
{ ref }}_over_{
{ denominator_vol_ref }} {%- if not loop.last -%},{%- endif -%} {% endfor %} from ({
{ volume_ratio(numerator_vol_refs.0, denominator_vol_ref) }}) as base {% for ref in numerator_vol_refs %} {% if not loop.first %} join ({
{ volume_ratio(ref, denominator_vol_ref) }}) as {
{ref}}_r on {
{ref}}_r.age_group=base.age_group and {
{ref}}_r.state=base.state {%- endif -%} {%- endfor -%}{% endmacro %}*
那里发生了很多事。让我们来分解一下我们正在做的一些事情。
在这个街区:
*select age_group, state, {%- for ref in numerator_vol_refs -%} {
{ ref }}_over_{
{ denominator_vol_ref }} {%- if not loop.last -%},{%- endif -%} {% endfor %}*
我们将遍历表引用的列表,我们将使用这些表引用的人口统计量作为比率分子。if not loop.last
条件语句确保我们在除最后一列之外的每个列名后面都加上一个逗号,以得到格式良好的 SQL。
这为我们提供了一组对子查询中公开的列名的引用。
在这个街区:
*from ({
{ volume_ratio(numerator_vol_refs.0, denominator_vol_ref) }}) as base*
我们使用数组中的索引.0
从第一个分子表引用创建一个体积比率子查询,并将该子查询别名为base
。
在最后一块:
*{% for ref in numerator_vol_refs %} {% if not loop.first %} join ({
{ volume_ratio(ref, denominator_vol_ref) }}) as {
{ref}}_r on {
{ref}}_r.age_group=base.age_group and {
{ref}}_r.state=base.state {%- endif -%} {%- endfor -%}*
我们再次遍历分子表引用列表。因为我们已经使用了第一个表引用来创建base
子查询,所以我们忽略第一个带有if not loop.first
条件的子查询。
然后,我们针对每个分子表引用的新的volume_ratio
子查询以编程方式连接base
,最终暴露出我们想要的所有列。
我们可以在文件中调用宏:
*-- path: models/example_funnel.sql{%- from 'volume_ratios.sql' import volume_ratios -%}{
{ volume_ratios([ 'purchases', 'sessions', 'supportTickets' ], 'marketing_pushes') }}*
试试在中的回购中的:
*>> ./render models/example_funnel.sql*
这个东西将呈现一个庞大的查询,很难理解,手工组装起来会很痛苦。但是用宏呢?不是可怕可怕。
用例#4:单测试
这是整件作品的真正主题。
我们已经看到抽象是如何让我们通过构建小的乐高积木来构建复杂的查询的。如果你在工作流程中使用它们,你可以从相对简单的部分构建出最终复杂的东西。
我们还看到这些部分是可参数化的,我们可以提供给宏的一个关键参数是对一个表的引用!这种引用可以是对“真实的”物化数据库表、公共表表达式或视图的引用。重要的是,我们可以对生产数据或使用参数化查询,这是我们有意设计并存储在某处的一组合成数据。
这使我们能够编写一堆行,覆盖我们希望查询处理的所有测试用例,并检查结果是否如预期的那样。然后,一旦我们确定它有效,我们就可以对实际的业务相关数量使用完全相同的查询逻辑。
如果我们用已经探索过的代码进行一些单测试,我们可能已经发现了一些错误。例如,在volume_ratio.sql
中,我们正在做整数除法,不会在我们的查询结果中得到real
个数字。我们也不检查 0 值分母,这在最坏的情况下是个坏消息。
还有几个例子,我们对公开的列如何命名做了隐含的假设——对命名方案的任何微小改变都会破坏下游的宏,而调用者不知道发生了什么。
进行单测试强调了您的组织正在构建的整个分析卡片屋,并允许您迭代,直到您获得一个既正确又易于更改的“数据 API”。
它还允许您自信地进行更改,并简单地检查之后的回归,而不是将您的时间花费在生产记录中试图进行健全性检查。
分析工程
只有当你以一种特殊的方式塑造你的分析团队时,这篇文章的教训才会有用。在开始使用 Jinja2 和宏的抽象魔法之前,一些隐含的假设必须成立:
- 您需要在一个统一的代码库中对分析工作进行编码,而不是在几个团队的商业智能工具页面中进行编码。
- 应该有一个连续的“部署”管道,从代码库获取最新的 SQL,并实际上将其分流到仪表板/报告等。这个管道可以处理 Jinja 渲染、查询导出等。
- 必须有一个框架来启动测试,如果有一个假设或逻辑错误可能产生不正确的查询结果,则 停止该行 。
开始这一切的一个很好的方法是浏览 Fishtown Analytics’ 数据构建工具 的文档,该工具旨在处理像这样的组织的许多细节。
沿着这条路走下去,你的分析组织将超越侦探、特别查询处理程序和部落机构知识仓库。
这将把他们变成一个由分析工程师组成的团队,他们的工作是可重复和可靠的。
资源
- Jinja2 模板文档
- 数据构建工具教程
在 Julia 中连接数据框架
原文:https://towardsdatascience.com/joining-dataframes-in-julia-c435e3da32f3?source=collection_archive---------28-----------------------
学习所有连接——使用 DataFrames.jl 的内部、外部、交叉和半连接
W 帽子是什么加入的?我们为什么要这么做?我们如何使用 DataFrames.jl 来实现呢?在这篇文章中,我将展示一些关于如何连接数据帧的实用而简单的例子。
照片由蒂姆·约翰逊在 Unsplash 上拍摄
要获得所有媒体文章的完整访问权限,包括我的文章,请考虑在此订阅。
简单连接
上一次,我们弄清楚了如何使用 DataFrames.jl 索引、排序和聚合我们的数据。Joins 是另一个非常常见和重要的操作,出现在列表数据的世界中。跨两个数据框架的联接是基于两个表中存在的共享列值来组合两个数据集的操作。我们称这一列(或几列)为键。因此,第一个表中的每条记录都与第二个表中的一条记录匹配—只要记录的值相同。让我们通过一个小例子来证明这一点。首先,我们设置表 A:
5×2 DataFrame Row │ id name │ Int64 String ─────┼─────────────── 1 │ 1 Alice 2 │ 2 Bob 3 │ 3 Claire 4 │ 4 Daniel 5 │ 5 Edward
该表包含所有个人的 id 及其姓名。假设我们有另一个表,其中包含这些个人的收入:
6×2 DataFrame Row │ id salary │ Int64 Int64 ─────┼─────────────── 1 │ 3 4078 2 │ 4 2395 3 │ 5 3662 4 │ 6 2202 5 │ 7 2564 6 │ 8 4545
我们现在有两张桌子:
- 保存人们的 id 和姓名
B_earnings
保存 id 和收益。
我们想合并这两个表,这样我们就可以一起看到姓名和收入。我们加入吧!
3×3 DataFrame Row │ id name salary │ Int64 String Int64 ─────┼─────────────────────── 1 │ 3 Claire 4078 2 │ 4 Daniel 2395 3 │ 5 Edward 3662
让我们详细讨论一下这个问题。参数 1 和 2 是我们要连接的两个表。参数 3 ( on
)告诉我们键列是什么。我们将使用该列来匹配表中的观察值。
如您所见,我们最终得到了 3 行 3 列。现在回到开始,看看这两个原始数据集是什么样子的。请确保您理解为什么我们最后只有这些行。
由于我们使用了innerjoin
,我们只保留了出现在两个数据集中的 ids 其他的我们都丢弃了。
还有其他种类的连接:
- 内部连接:获取出现在两个表中的 id
- 左连接:从左(第一个)表中取出所有的 id 和右表的值
- 右连接:与左连接相同,但是保留第二个表中的所有 id
- 外部连接:从两个表中取出所有 id,不管它们是否出现在另一个表中
不要害怕,我们现在将逐一检查所有这些连接!首先,让我们做一个左连接,看看只在第一个数据集中的 id 会发生什么:
5×3 DataFrame Row │ id name salary │ Int64 String Int64? ─────┼──────────────────────── 1 │ 1 Alice missing 2 │ 2 Bob missing 3 │ 3 Claire 4078 4 │ 4 Daniel 2395 5 │ 5 Edward 3662
在这里,我们保留了表 A 中的所有观察值,不管表 b 中的发生了什么。对于表 A 中没有匹配的记录,收入列的值为missing
。这是有道理的,因为我们从来没有真正看到这些收入数字。
当然也有右加入。这将保留第二个表中的所有行。
如果您想拥有两个表中的所有 id,使用一个外部连接:
8×3 DataFrame Row │ id name salary │ Int64 String? Int64? ─────┼───────────────────────── 1 │ 1 Alice missing 2 │ 2 Bob missing 3 │ 3 Claire 4078 4 │ 4 Daniel 2395 5 │ 5 Edward 3662 6 │ 6 missing 2202 7 │ 7 missing 2564 8 │ 8 missing 4545
加载更多丢失的值,但是我们现在可以看到所有的 id。查看 id6–8 的新的缺失名称!这是外部连接的作用。
这 4 个连接构成了表合并的基础。如果没有别的,记住这四个:
- inner:只保留键同时出现在两个表中的行
- 左/右:仅保留出现在左侧(第一个)或右侧(第二个)表格中的关键点
- outer:保留两个表中的所有键
更多奇异的连接
马克·巴宾在 Unsplash 上拍摄的照片
上面的连接会让你的数据分析需求走得很远,但是我想向你介绍一些不太为人所知,但是仍然有用的连接。
假设您想查看有收入数据的人的姓名,但实际上您并不想要第二个表中的所有列。这就是半连接的作用。它为您提供了与内部连接相同的行,但是没有添加第二个表中的任何列:
3×2 DataFrame Row │ id name │ Int64 String ─────┼─────────────── 1 │ 3 Claire 2 │ 4 Daniel 3 │ 5 Edward
这将返回 true,表明半连接与只包含表 a 中的列的内部连接相同。
我听到了,这很有用,但没那么疯狂。签出交叉联接:
30×4 DataFrame Row │ id name id_1 salary │ Int64 String Int64 Int64 ─────┼────────────────────────────── 1 │ 1 Alice 3 4078 2 │ 1 Alice 4 2395 3 │ 1 Alice 5 3662 4 │ 1 Alice 6 2202 5 │ 1 Alice 7 2564 6 │ 1 Alice 8 4545 7 │ 2 Bob 3 4078 8 │ 2 Bob 4 2395 9 │ 2 Bob 5 3662 10 │ 2 Bob 6 2202 11 │ 2 Bob 7 2564 ⋮ │ ⋮ ⋮ ⋮ ⋮ 21 │ 4 Daniel 5 3662 22 │ 4 Daniel 6 2202 23 │ 4 Daniel 7 2564 24 │ 4 Daniel 8 4545 25 │ 5 Edward 3 4078 26 │ 5 Edward 4 2395 27 │ 5 Edward 5 3662 28 │ 5 Edward 6 2202 29 │ 5 Edward 7 2564 30 │ 5 Edward 8 4545 9 rows omitted
哇,那爆炸得很快💥!交叉连接获取表 A 中的所有行,对于每一行,它都将其与表 B 中的每一行进行匹配。乍一看,这可能没有任何意义,但是这是一个查找所有两个表的组合的好方法。我们的新表有 30 行,即 5(表 A 的行)x 6(表 B 的行)。
让你相信这确实有用。假设你想通过改变成分来设计一款新的 Twix。为了理解盈利能力,你还需要计算出巧克力的总成本:
显示更好格式的结果截图。
现在你相信我了吧?交叉连接使巧克力创新变得更容易,所以它们很有用!
沃尔特·奥托在 Unsplash 上的照片
要加入的多个键
现在我们有了更好的巧克力,让我们学习如何在多栏中加入。将上述连接扩展到使用两个键是非常容易的。事实上,您所要做的就是将一个符号或字符串向量传递给连接函数的参数on
。为了演示这一点,让我们复制并添加另一列到我们的两个数据集。这将包含用户居住的城市名称。
julia> display(C_names) 5×3 DataFrame Row │ id name city │ Int64 String String ─────┼───────────────────────── 1 │ 1 Alice New York 2 │ 2 Bob London 3 │ 3 Claire London 4 │ 4 Daniel New York 5 │ 5 Edward London julia> display(D_earnings) 6×3 DataFrame Row │ id salary city │ Int64 Int64 String ─────┼───────────────────────── 1 │ 3 4078 New York 2 │ 4 2395 New York 3 │ 5 3662 New York 4 │ 6 2202 London 5 │ 7 2564 New York 6 │ 8 4545 New York
你可以这样想,我们有两个独立的数据库。一个在纽约,一个在伦敦。由于系统之间互不了解,它们分别跟踪用户的 id。因此,伦敦用户 1 的姓名与纽约用户 1 的姓名不同。的确,他们是不同的用户!因此,当我们合并这两个表时,我们希望确保姓名和收入不仅在用户 id 上匹配,而且在数据库名称上匹配。让我们在两列上做一些连接:
1×4 DataFrame Row │ id name city salary │ Int64 String String Int64 ─────┼───────────────────────────────── 1 │ 4 Daniel New York 2395
我们可以看到,只有丹尼尔出现在两个数据集中,并且住在同一个地方。
不同的列名
您可能面临的一个问题是,您的键列在您的数据框架中没有相同的名称。如果是这样,你有两个选择:
- 您可以使用
rename!
重命名列 - 或者您可以将名称映射作为
on
参数传递
现在我们想做的是用姓名连接新的收入表,但是我们想在新表中使用another_id
作为我们的连接键。
这里我们告诉 Julia 在进行连接时将=>
列映射到id
列。当您需要重命名列时,会使用完全相同的格式。
摘要
阅读完本文后,您应该掌握了以下连接函数:
- 内部:用于查看重叠部分
- 外层:用于保存所有东西
- 左/右:用于保留一个表中的所有行
- semi:用于在进行内部连接时只保留第一个表中的列
- cross:用于创建两个表的乘积,即让每一行都与每一行匹配。
您还知道如何连接多个键或不同的键名。感谢阅读!
连接熊猫数据框
原文:https://towardsdatascience.com/joining-pandas-dataframes-472e4a045bac?source=collection_archive---------13-----------------------
了解如何轻松合并熊猫数据帧
由 Unsplash 上的 CHUTTERSNAP 拍摄
通常,您的数据来自不同的来源。为了帮助您进行分析,您通常需要组合不同来源的数据,以便您可以获得所需的数据。在这篇文章中,我将讨论如何合并(连接)熊猫数据帧。关于这个主题的大多数文章都使用简单的数据帧来说明数据帧连接的概念——内部连接、外部连接、左连接和右连接。对我来说,理解这个主题的一个更好的方法是使用一个更现实的例子,这样你就可以理解并能够更好地记住这些概念。
我们开始吧!
创建数据帧
首先要做的是创建两个数据框。第一个创建航班号列表和他们出发的机场:
import pandas as pddf_flights = pd.DataFrame( dict( AIRPORT_CODE=['MIA','MIA','LAX','DCA','SIN'], FLIGHT_NO=['3322','3213','4223','5678','1234'] ) )df_flights
df_flights 数据帧是这样的:
下一个数据帧包含机场代码列表及其各自的机场名称:
df_airports = pd.DataFrame( dict( AIRPORT_CODE=['MIA','LAX','DCA','HEL','SFO'], AIRPORT_NAME=['Miami International Airport', 'Los Angeles International Airport', 'Ronald Reagan Washington', 'Helsinki-Vantaa Airport', 'San Francisco International Airport'] ) )df_airports
df_airports 数据帧看起来像这样:
列出所有航班的机场名称
假设您需要获得每个航班的所有出发机场名称。因为信息在两个数据帧中,所以需要连接这两个数据帧。在 Pandas 中,使用 merge() 方法连接数据帧。对于此要求,您可以基于 AIRPORT_CODE 列对两个数据帧执行一个“ left ”连接:
pd.merge(df_flights, df_airports, on='AIRPORT_CODE', how='left')
’ on '参数指定要联接的列。“左是指第一个数据帧— df_flights 。上述连接函数的结果如下:
注意来自 df_flights 数据帧的所有行(‘left’join)都在结果中。还可以观察到,由于机场代码 SIN 在 df_airports 数据帧中没有条目,因此它在 AIRPORT_NAME 列中有一个 NaN 值。
“左”连接确保结果包含第个数据帧中所有可用的机场代码。
列出各个机场的所有航班
如果您想列出所有机场的所有航班,该怎么办?在这种情况下,你可以执行一个’右’连接:
pd.merge(df_flights, df_airports, on='AIRPORT_CODE', how='right')
右是指第二个数据帧— df_airports 。上述连接函数的结果如下:
注意,结果现在包含了所有包含在 df_airports 数据框架中的机场(右连接)。此外,由于有两个航班来自 MIA 机场,结果将包含 MIA 机场的两行。此外,由于 HEL 和 SFO 机场在 df_flights 数据帧中没有任何离港航班,因此结果的 FLIGHT_NO 列将包含 NaN s
“右”连接确保结果包含第二个数据帧中可用的所有机场代码。
列出现有航班的机场名称
您之前看到‘left’join 结果包含从 SIN 机场出发的航班的条目:
但是,您可能希望忽略没有有效机场代码的航班。在这种情况下,您可以执行一个’ inner '联接:
pd.merge(df_flights, df_airports, on='AIRPORT_CODE', how='inner')
“内部”连接的结果现在将只包含在两个数据帧中都有机场代码的行:
“内部”连接确保结果中的机场代码在第一个和第二个数据帧中都可用。如果没有指定“how”参数,merge()方法的默认联接是“inner”。
列出所有机场名称和航班
与’innerjoin 相反的是’outerjoin,其中两个数据帧中的值在结果中都可用。
使用我们的例子,如果你想得到所有的机场名称和航班,你可以执行一个’ outer '连接:
pd.merge(df_flights, df_airports, on='AIRPORT_CODE', how='outer')
结果现在将包含所有机场,不管是否有相应的机场名称或是否有来自某个机场的航班:
“外部”连接确保结果中的机场代码在第一个数据帧和第二个数据帧中都可用。
基于不同列名的联接
到目前为止,我们的连接非常简单,其中两个数据帧具有我们想要连接的相同列名。在现实生活中,数据帧有不同的列名要常见得多。
假设 df_flights 数据帧的 AIRPORT_CODE 列现已更改为 IATA_CODE :
import pandas as pd df_flights = pd.DataFrame( dict( IATA_CODE=['MIA','MIA','LAX','DCA','SIN'], FLIGHT_NO=['3322','3213','4223','5678','1234'] ) )df_flights
要像前面一样执行连接,现在需要使用 left_on 和 right_on 参数显式指定各个数据帧的列名:
pd.merge(df_flights, df_airports, left_on='IATA_CODE', # column for df_flights right_on='AIRPORT_CODE', # column for df_airports how='left')
结果现在将包含来自两个数据帧的连接列( IATA_CODE 和 AIRPORT_CODE ):
现在,您可以继续删除其中一列。但是,在此之前,请观察最后一行的 IATA_CODE 和 AIRPORT_CODE 列——一列的值为“ SIN ,另一列的值为 NaN 。在这种情况下,您应该删除 AIRPORT_CODE 列(如果您打算显示所有离港航班的机场代码和机场名称):
pd.merge(df_flights, df_airports, left_on='IATA_CODE', right_on='AIRPORT_CODE', how='left').drop(columns='AIRPORT_CODE')
结果现在看起来像这样:
使用多列连接
除了联接具有不同列名的数据框架之外,还可以联接基于多列的数据框架。理解这一点的一个好方法是用一个例子。考虑以下数据帧:
df_confirmed = pd.DataFrame( dict( country=['Mainland China','Mainland China', 'US','Canada','US'], state=['Hunan','Anhui','Seattle, WA', 'Toronto, ON','Montana'], confirmed=[879,830,1,2,20] ) )df_locations = pd.DataFrame( dict( country=['Bulgaria','US','Mainland China', 'Mainland China','US','Canada'], state=['Montana','Montana', 'Hunan','Anhui', 'Seattle, WA','Toronto, ON'], lat=[43.4125, 46. ,27.61041, 31.82571, 47.7511, 43.6532], lng=[23.225, -109., 111.7088, 117.2264, -120.74, -79.3832] ) )
df_confirmed 数据帧如下:
而 df_locations 数据帧看起来像这样:
如果您想找到每个州的位置,您可能会尝试通过“州”连接两个数据框:
pd.merge(df_confirmed, df_locations, on='state', how='left')
但是,结果将包含六行,因为有两个蒙大拿州,一个在美国,一个在保加利亚:
正确的加入方式是基于国家 和 州列(作为列表提供)加入:
pd.merge(df_confirmed, df_locations, on=['country','state'], how='left')
结果现在是正确的:
您也可以使用 left_on 和 right_on 参数连接多个列。
结论
就是这样!我希望您现在对连接数据框架的工作方式有了更清晰的了解。以下是我们在本主题中讨论的内容的快速总结:
- ’ left '联接返回第一个数据帧中的所有行
- '右’联接返回第二个数据帧中的所有行
- ’ inner '联接返回两个数据帧中可用的所有行
- ’ outer '联接返回第一个数据帧和第二个数据帧中的所有行
- 您可以使用 left_on 和 right_on 参数连接基于不同列名的数据帧
- 还可以使用多个列名连接数据框架
没有连接键或公共索引的连接表
原文:https://towardsdatascience.com/joining-tables-without-a-joining-key-or-common-index-5955dc0a7a3a?source=collection_archive---------23-----------------------
使用模糊字符串匹配匹配马来西亚人的 2 个不同来源的产品评论,并比较 Jaccard 和 Levenshtein 算法。
当我必须匹配几个马来西亚在线购物平台的产品评论时,就出现了这种情况。让我们马来西亚人与众不同的是我们如何拼写和给出产品评论。
亚马逊评论(上)与马来西亚人的评论(下)。作者提供的截图。
我决定分享我遇到的这个挑战,以及一些可能使这个话题与他人相关的场景。
想象一下,如果今天,您需要连接来自不同来源的两个或多个表,这可能是因为以下情况:
- 2 家公司合并,需要合并他们的产品数据来清点库存。
- 您正在比较一系列产品的不同应用的一些评论。
- 从一个表格中寻找反馈,并尝试将相似的单词组合成一个。
其中一个问题是您无法控制数据,无法让他们重新索引他们的产品 id 或密钥,因为企业要求您尽快提供它们,或者这可能只是出于报告目的,企业无意重新索引以避免服务中断。
神奇的门户网站,帮助您跳转到本页所需的主题
- 今天的挑战
- 什么是 Levenshtein 距离和 Jaccard 指数
- 每种算法解决和未解决的问题
- UDF 在大查询中对 Jaccard Index 和 Levenshtein
- 今日挑战 Jaccard 指数用例
- 外卖积分
今天的挑战
为了让您更深入地了解我们今天的挑战,这里有一个我们今天将尝试加入的两个表的示例。我们想要得到每个评论出现的总数。代替 Levenshtein 距离,为了这个介绍,我们将集中使用 Jaccard 指数。虽然这可能看起来很荒谬,但这些都是我从马来西亚几大在线购物平台上收集的合法评论。
没有公共键连接的两个表。作者提供的截图。
做一个快速的谷歌搜索最终会有一个解决这个挑战的常用方法,那就是使用一种叫做 Levenshtein Distance 的算法。虽然 Levenshtein 距离在大多数情况下都很有效,但我想介绍另一种算法,称为 Jaccard Index/Jaccard similarity。这不是“更好”或“完美”算法的争论,而是另一种方法的介绍。在深入实际应用之前,我们需要知道 Levenshtein 距离和 Jaccard 指数算法解决了什么问题。
什么是莱文斯坦距离和雅克卡指数
通俗地说,Levenshtein 计算将一个字符串更改为另一个字符串需要多少步/编辑,Jaccard 计算第一个字符串与第二个字符串相交的程度。我在下面举例说明了这两种算法的不同之处。
作者创建的 Levenshtein 距离和 Jaccard 指数的插图。
在上图中,Levenshtein 不得不从 thaanks 中删除多余的“ a ,从而导致一次编辑,使第一个字符串与第二个字符串相似。由此 Jaccard 将比较每个唯一的字母是否存在于另一个字符串中。Jaccard 不考虑“ a 的数量,因此“ thaanks 中的 2 个“ a ”被视为一个“ a ”。
每种算法解决和未解决的问题
Levenshtein 距离是一些自动校正软件使用的许多算法之一。
作者 Chromium 对 thaank 的自动更正提示截图。
“感谢”和“thank”的编辑距离为 1,因此自动更正软件建议“感谢作为替代。由此,在一些事件中,用户决定输入“thaaaaaaaaaaaanks”,使用 Levenshtein 距离的自动校正软件可能无法解决该问题,因为编辑距离很大(删除 10 步*“a”*以使其成为“感谢”)。
作者 Chromium 对thaaaaaaaaaaaanks 的自动更正提示截图。
如上面的截图所示,我的浏览器没有建议任何替代词,因为我输入的单词可能与他们字典中的任何单词都有很大的编辑距离。我使用我的基于 Chromium 的浏览器作为 Levenshtein 将要展示的例子,但是,我并不是说使用 Levenshtein 距离作为他们的主要算法,他们可能有更复杂的算法来处理语法和不同的语言。
那么 Jaccard 会如何处理“thaaaaaaaaaaaanks”对“谢”呢?首先,两个单词将被制成一组唯一字符,两个字符串的唯一字符是“感谢”。将"谢"与"*谢"*相交导致所有字符相交,即 1。但是,当两个不同的单词具有相同的唯一字符时,Jaccard 可能不会返回理想的结果。以“好”和“神”为例,这两个字符串都有唯一的字符“神”,所以由于所有字符都相交,所以结果为 1。
现在您已经了解了这两种算法的工作原理,让我们看看如何在今天的挑战中应用 Jaccard 算法。
UDF 在对 Jaccard Index 和 Levenshtein 的大查询中
或者您可以使用相同的逻辑,并将其应用于其他数据库。
什么是大查询中的 UDF?用户自定义函数(UDF) 可以是 SQL 或 Javascript 的形式,允许你在大查询中创建类似编程语言的函数。
我们将使用 Javascript 创建 Jaccard 索引算法,并将结果与 Levenshtein 距离进行比较。bqutil
是来自谷歌云平台的一个项目,它托管了几个我们可以使用的预建功能。他们的 Github 库可以在这里找到。在撰写本文时,我已经创建了一个 pull 请求来添加 Jaccard 索引作为供公众使用的bqutil
UDF。
将创建一个名为 jaccard 的临时函数,它接受 2 个字符串并返回一个范围从 0 到 1 的浮点数。如果第一个字符串 sa 中的一个字符存在于第二个字符串 sb 中,则将交集大小增加 1。循环完成后,通过计算两个字符串的长度并减去交集大小来计算索引,然后将结果再次除以交集大小来获得交集字符的百分比。最后,返回一个 2 小数点的浮点数。
以上查询的结果。作者提供的截图。
然后运行 4 个测试用例来比较 Jaccard 指数和 Levenshtein 距离之间的结果。如图所示,Jaccard 将能够校正相似度为 1 或 100%相似的第一个和第二个示例(感谢和最佳产品示例)。由此 Levenshtein 指出需要 10 和 8 个步骤来将第二个字符串校正为第一个字符串。关于 doge 和 dodge 的第三个例子,Jaccard 指出两者是相似的词,我们知道它不是,Levenshtein 指出需要一个步骤来纠正 doge 的 dodge,反之亦然。
今天的挑战 Jaccard 索引使用案例
- 创造一个 UDF
- 创建两张桌子来模拟我们今天的挑战
- 交叉连接两个表,这样我们可以计算第二个表中的每个评论与第一个表中的每个评论之间的距离
对于那些仍然对交叉连接感到困惑的人,我已经根据我们当前的挑战在下面举例说明了它。
作者创建的 t1(左)和 t2(右)的交叉连接图。
交叉联接将一个表中的每条记录与另一个表中的每条记录相匹配。然后,我们对每条记录运行 jaccard 函数,只显示 Jaccard 索引大于 0.8 或 80%相似的记录。
今天挑战的结果
使用 Jaccard 索引和 80%相似度的阈值,它很好地支持了我们的数据集。第二个表中的每条记录都被正确地添加到第一个表中。
外卖点
- 不存在“最佳”算法的竞争
- 每种算法都有自己的用例
- Jaccard 索引处理非常具体的情况,不应该在不考虑每个模糊匹配项目的情况下将其概括为“goto”算法
使用 Jaccard Index,我能够根据一个定制的“马来西亚俚语”字典有选择地纠正我的数据集中至少四分之三的评论,我将这个字典定义为我的来自上述挑战的 t1 。读者们,我希望这能向你们介绍另一种选择,如果 Levenshtein 不像我一样对你们有利的话,你们可以考虑一下。
今天的文章 TowardsDataScience 博客中文翻译 2021(四百四十一)分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/96698.html