TowardsDataScience-博客中文翻译-2022-四十五-

TowardsDataScience 博客中文翻译 2022(四十五)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

数据流中的移位密码

原文:https://towardsdatascience.com/shift-cipher-in-streamlit-525225667237

如何快速构建和部署用 Python 编写的可共享 web 应用程序

马库斯·温克勒在 Unsplash 上的照片

概观

你有没有想过向不懂 Python 的人炫耀自己的 Python 代码?你有没有希望自己可以不用处理前端开发就能分享互动应用?

最*,我在玩 La Famiglia 时发现自己也处于这种情况,这实际上是一个棋盘游戏形式的密室。其中一个谜语涉及移位密码。线索中的每个字母都必须移动一定的数字。例如,将字母“A”移动一位会得到字母“B”。请看下面的插图。

移位密码的例子。图片作者。

不幸的是,我们的线索并不容易。我们必须将每个字母移动 19。手动计算字母表是一件麻烦的事,也很容易出错。Python 可以立即无误地处理这项任务。

我决定用 Streamlit 构建和部署一个用户友好的应用程序,而不是忍受这样一个容易自动化的任务。由于 Streamlit 处理前端,建造 一个工作原型用了不到 30 分钟。 Python 处理所有后端,Streamlit Cloud 允许共享。以下是成品的链接:

streamlit _ shift _ cipher _ app

最终产品。图片作者。

什么是 Streamlit?🔍

Streamlit 是一个 Python 库,可以将 Python 脚本快速转换为可共享的基于 web 的应用程序。Streamlit 处理所有前端,因此不需要 web 开发经验。仅依靠 Python,Streamlit 允许用户在很短的时间内构建应用程序,并继续积累数据科学家的追随者。此外,Streamlit 提供了广泛的小部件和直观的命令选择。

Streamlit 最棒的地方在于它可以非常轻松地分享应用程序。Streamlit Cloud 允许您免费部署、管理和共享应用程序。用户不必下载项目目录。他们甚至不需要 Python。他们只需要一个网络浏览器和一个链接。与此同时,应用程序经理可以将 GitHub 配置文件链接到 Streamlit Cloud,这样对代码的任何编辑都会自动更新。

先决条件🔑

本文涵盖了 Streamlit 对象和命令。它假设读者熟悉 Python 对象,如字符串、字典和函数。

密码📚

这个应用的 GitHub Repo 可以在这里找到。

首先,要安装 Streamlit,请在终端中运行以下命令。Streamlit 云中已完成的应用不需要安装 Streamlit 。由于这些应用程序是基于网络的,它们只需一个浏览器就能工作。但是,编辑和构建自定义的 Streamlit 应用程序需要安装 Streamlit 库。

pip install streamlit

库、文本和小部件

首先,导入库。streamlit为应用程序提供基础,string填充用于解码字符的字典。

st.titlest.writest.header在 app 中显示文字。phrase允许用户输入任何文本。shift_dir创建班次方向的下拉菜单。最后,shift_no接受一个表示短语中每个字母需要移动多少位置的数字。

功能

第一个函数create_dict创建两个字典。letter_to_no将字母表中的每个字母分配给其对应的从零开始的索引。no_to_letter反其道而行之。当给定一个索引号时,它返回一个字母。

st.cache是一个装饰器,它通过将结果存储在缓存中来加速应用程序。在重新运行函数之前,它会检查输入、外部变量、函数体或任何引用函数体是否发生了变化。如果没有任何变化,它将不会在后续运行中重新运行该功能。

create_dict循环遍历string.ascii_lowercase中的每个字母,这是字母表中所有字母的列表。enumerate是一个内置函数,它计算遍历一个可迭代对象的次数。它从 iterable 返回计数和值。两本词典都是从 0 开始的。

shift有三个输入— phraseshift_noshift_dir。像以前一样,st.cache阻止应用程序不必要的运行。该函数获取短语中的每个字母字符,并按照shift_dirshift_no的描述找到它的对应字符。该函数循环遍历每个字符i,并检查一些东西。首先,它检查i是否大写,并将该信息存储在upper_case中。由于letter_to_nono_to_letter字典使用小写字母,line 10i转换为小写。如果i是一个字母字符,那么letter_to_no返回其索引或ind。例如,“a”的索引为 0,“z”的索引为 25。如果shift_dir是“前进”,那么shift_no被加到ind上。否则,它被减去。

no_to_letter有从 0 到 25 的按键。但是,shift_ind可能小于 0 也可能大于 25。考虑下面的例子:

ind = letter_to_no["z"]       # ind = 25
shift_no = 2
shift_ind = ind + shift_no    # shift_ind = 27

mod 运算符%通过除法然后返回余数来解决这个问题。对于上面的例子,shift_ind % 26 = 1

如果i不是 alpha 字符,那么i被添加到answer中,不做任何处理。如果i是大写的,那么line 27会在将sub附加到answer之前将其大写。

输出

Line 3使用文本对象st.write显示用shift找到的解码短语。

利用 Streamlit 云部署

使用 Streamlit Cloud 部署应用只需不到 5 分钟。首先,我在这里创建了一个 Streamlit 云账户。接下来,我将我的 GitHub 个人资料链接到我的 Streamlit Cloud 帐户。这样做允许 Streamlit Cloud 填充下拉菜单,以便我可以轻松选择要部署的应用程序。

请注意下拉菜单以简化部署。图片作者。

结论👏

多亏了 Streamlit,你可以很容易地解码线索,甚至用 backwards 选项编写自己的隐藏消息。希望本教程展示了 Streamlit 所能提供的一切——简单性、可共享性以及最重要的速度。

感谢您阅读我的文章。如果您喜欢我的内容,请考虑关注我。还有,你可以通过订阅 Medium 来支持我和其他作家。使用我下面的推荐链接不会额外花费你。

欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/membership

机器人学习研究中的风向转变

原文:https://towardsdatascience.com/shifting-winds-in-robot-learning-research-2ead21671a65

强化学习已死,强化学习万岁!

鸣谢:拜伦·大卫(经许可)

当我告诉科技圈的人我从事机器人的机器学习时,他们的第一反应是‘哦,强化学习……’这并不罕见,我过去从不考虑这种说法。毕竟,在过去几年中,我们看到的很大一部分成功都是关于将机器人操纵框定为大规模学习,将问题转化为强化学习自我改进循环大规模扩展飞轮在此过程中学习大量课程,瞧!用伊利亚·苏茨基弗的不朽名言来说:“成功是有保证的。”

除了…这很复杂。强化学习(RL)可以说是一头难以驯服的野兽。这导致了有趣的研究动态,如果你的主要目标不是关注学习循环,而是关注表示或模型架构,有监督的学习就更容易工作。因此,许多 研究 线程专注于监督学习——在机器人术语中又称为行为克隆(BC)——而将 RL 留给读者作为练习。即使在 RL 应该大放异彩的地方,围绕随机搜索黑盒方法的各种变化也让“经典”RL 算法大放异彩。

然后… BC 方法开始变好真的 好的。如此之好,以至于我们今天的最佳操纵系统大多使用 BC,上面还撒了一点 Q 学习来执行高级动作选择。今天,不到 20%的研究投资是在 RL 上,基于 BC 的方法的研究跑道感觉更健壮。机器人学习研究几乎是 RL 代名词的日子是不是已经过去了?

虽然听起来很诱人,但我认为今天就放弃是非常有问题的。RL 的主要承诺是自主探索:随着经验扩展,没有任何人类保姆。这有两个主要后果:在模拟中执行大量经验收集的机会,以及在现实世界中自主数据收集的可能性。

有了 RL,你就有了一个机器人学习过程,它需要在模拟基础设施上进行固定投资,然后随着 CPU 和现场部署的机器人的数量而扩展——如果你能访问大量计算,这是一个很好的制度。但在以 BC 为中心的世界中,从可扩展性的角度来看,我们最终反而陷入了最差的局部最优:我们仍然需要投资于模拟,即使只是为了进行快速实验和模型选择,但当涉及到经验收集时,我们基本上只能随着在监督环境中控制机器人的人类数量而扩展。然后,当你自主部署机器人时,不仅人类启发的行为是你的天花板,而且封闭探索和持续学习的循环变得极其困难。谢尔盖·莱文雄辩地阐述了这代表的长期机会成本。

但是,很难突破 BC 的吸引力:押注于大规模模型很少是一个好主意,如果这些模型要求监督而不是强化,那么我们有什么理由争论呢?“巨型语言模型”革命应该让任何人暂停专注于设计复杂的训练循环,而不是一头扎进收集大量数据的问题中。也不是不可能想象,一旦我们接受了监督机器人的巨大固定成本,我们就可以让它们一直达到“足够好”的性能水*以取得成功——毕竟,这是自动驾驶汽车行业的整个战略。也不是不可能想象,一旦我们找到了更多可扩展的方法来在现实世界的机器人环境中释放自我监督学习,扬恩蛋糕上的樱桃开始尝起来有点酸了。

我不是唯一一个注意到研发领域风向变化的人。该领域的许多人已经将目光投向离线 RL,作为突破自主馆藏天花板的途径。最*的一些焦点是让 BC 和 RL 相互配合,将可扩展的探索带到监督设置,或者让 RL 假装它是一个监督顺序决策问题,以便保留大型变压器的理想缩放属性。这是对稳定的 MuJoCo 研究的一个令人耳目一新的突破,误差棒大到几乎无法在页面上显示(哈哈!).我预计未来几个月将会有更多关于这一健康的自我反省过程的具体表述出现,并有望出现新的见解,说明如何最好地驾驭 BC 的*期回报与 RL 的长期承诺之间的紧张关系。

感谢凯罗尔·豪斯曼对本文草稿的反馈。观点都是我的。

将你的思维从业余分析师转变为专业分析师

原文:https://towardsdatascience.com/shifting-your-mindset-from-amateur-to-professional-analyst-61383f913408

成为“真正的”数据分析师的旅程

在这个博客系列的开始,我开始向你证明你已经是一名数据分析师。是的,你!如果你正在读这篇文章,你此时此刻正在做分析。

Alexander SinnUnsplash 上拍摄的照片

我觉得从那里开始很重要,不仅仅是因为鼓舞人心的信息— 你不需要许可就可以进行数据分析,因为你已经在做了!—但也因为我相信有太多的数据科学偏见:我看到人们将神话般的常识应用于野外数据(例如,不相信 Instagram 上的每张照片都反映了现实),然后一旦他们开始将同样的任务称为“数据分析”或“数据科学”,就将它扔出窗外。

但不管我们怎么称呼它,我们的许多日常任务都涉及到分析。我们都是数据分析师。这是不是意味着我们都同样擅长这个?

业余分析师和专业分析师之间有很大的区别。我们在上一篇文章中详细介绍了前 3 个,现在我们将在这里继续我们的旅程。

专业数据与业余数据的区别# 4——了解职业

与业余分析师不同,专业分析师成为 分析师是出于选择 ,而不是出于不幸。对他们来说,分析本身就是一门优秀的学科,而不是通往其他职业的敲门砖(比如机器学习统计学)。

职业分析师是由选择 的 分析师,而不是由不幸。他们专注于达到自己职业的顶峰,而不是羡慕其他角色。

他们确信,顶级分析师至少和顶级统计/人工智能专家一样擅长处理数据,如果不是更多的话。(如果你持怀疑态度,请阅读此文。)他们为自己是分析师而自豪,这种自豪是理所应当的。

太多的分析工作描述听起来好像工具是工作中最重要的部分。他们不是。

专业分析师明白,让你成为分析师的不是你的职位,也不是你使用的工具和技术。如今,就业市场无处不在,因此,如果我说的一些话与你不一致,我可能会用与你的雇主不同的方式来使用“分析师”这个词。你可以随意用你所谓的相同角色来代替,我不介意。

图片由作者制作,为我的“数据科学到底是什么?”博文

我在上图和的一篇更长的博文中概述了不同的关注点,这篇博文解释了我使用术语“分析师”的意思,以及分析在各种数据科学学科中的位置。

工具不会让你变得专业。重要的是你如何对待他们。

每个数据科学学科都有助于使用相同的工具和公式,但他们使用它们的方式非常不同。你永远不会听到一位专家分析师声称,使用回归分析或 t 检验等统计方法意味着你在做统计。分析师、统计学家和人工智能专家使用同一个工具的方式有天壤之别。

专业分析师明白,让他们成为分析师的是他们在探索和灵感方面的专业知识,他们也倾向于这一点。如果你想了解更多,我有很多关于分析职业的资源,包括:

专业数据与业余数据的区别之五——拒绝成为数据骗子

失去我的职业尊严的最快方式是屈服于通过模仿分析和统计来快速赚钱的诱惑,而不理解它们的原因,从而污染每个人的声誉。如果你这样做,你比一个业余爱好者还不如。你是一个数据骗子:兜售有毒的后见之明。

专业分析师拒绝成为数据骗子

专业分析师拒绝兜售有毒的后见之明。

  • 分析师给你豁达的灵感。
  • 统计学家为你提供严格的测试。
  • 江湖骗子为你提供扭曲的马后炮,假装是分析加统计。

数据骗子的第一个警告信号是无法理解分析和统计是非常不同的学科 。不管你的正式职位是什么,没有规定说你不能想学就学,只要你不把它们弄混。当专业分析师在这两方面都很熟练时,他们知道什么时候脱下分析师的帽子,戴上统计学家的头盔。当存在不确定性时:

  • 分析 是对好问题的追求。
  • 统计 是对好答案的追求。

为了避免意外成为数据骗子,一次解决一个问题(阅读这篇)。

专业数据与业余数据的差异# 6——抵抗确认偏差

确认偏差意味着我们都可以看到同一个数字,并对它有不同的理解。事实不再仅仅是事实。

http://bit.ly/quaesita_confirmation确认偏见是一个微妙的怪物:即使你接触到与你的观点不一致的信息,你也可能不会接受。你可能会记错。你可能会找到忽略它的理由。你将继续挖掘,直到你看到的数字是你想看到的。头脑就是那样滑稽。

保罗 J 对确认偏倚的说明,经允许使用。

换句话说,确认偏差是数据驱动决策的死对头,因为它意味着一个事实不再仅仅是一个事实,无论你投入多少数学和科学去得到它。你和我可以看着同一个数字,而会有不同的感知。努力让自己接触更好的信息来源并不足以解决一个始于你眼前的问题。

许多人只是利用数据来让自己对已经做出的决定感觉更好。专业分析师对自己有更高的标准。

每次我的追随者释放面无表情的 snark——这经常发生——我就能确认我显然在这个社区做了正确的事情。永远不要改变,我亲爱的!

我为所有读者写了一个克服确认偏见的通用指南,但是如果你渴望成为一名专业分析师,你需要绝地级别的确认偏见解药。从一般指南开始,然后有意识地练习,以磨练防止确认偏差的两个最佳技能: 预先注册面对 脱发 时的豁达。

预先注册

消除确认偏差的一个方法是在查看数据之前设定你的决策标准。

问题:没有预先注册,在你找到数据落地的地方后,你可以自由移动球门柱。(当然你每次都进一球。你只是那个好。)**

解决方案:提前设定球门柱,抵制诱惑,以后再移动它们。

养成理解寻求问题和寻求答案的区别的习惯。当你寻找问题的时候,你可以自由处理你的数据而无需预先注册——但是你不允许使用你找到的任何东西作为证据。另一方面,如果你在寻找答案或影响行动,承诺遵守预先设定的决策标准是强制性的。从技术上来说,这是决策者的工作,而不是分析师的工作,但在实践中,这通常会被委托,所以在你掌握这项技能之前,你不能称自己为专家分析师。

点击了解更多关于如何做的

即使当你探索你的数据来寻找寻求问题而不是答案的时候,你仍然需要防止确认偏差的心理习惯,这让我们…

蝙蝠?蝴蝶?或者只是一个墨迹?这是 1921 年创立的罗夏测验中十张卡片的第一张。来源:维基百科

思想开放

当你保持一个开放的心态并且不严格地持有你的观点时,确认偏见就很难生根。在这种情况下,专业分析师就是杰迪。

虽然业余爱好者以从他们的数据中挖掘出一个强有力和令人信服的故事而自豪,但专业分析师对此太精明了。他们知道复杂的数据集本质上是罗夏测验——它们是阿波芬尼亚症的滋生地:对数据集进行分割的方式越多,他们实际上就越希望你从中找到错误的意义。这就是为什么专家分析师已经教会自己保持令人印象深刻的知识谦逊、锐利的怀疑和强烈的好奇心。他们太擅长自己的工作了,不会轻易下结论。

复杂的数据集实际上是在乞求你在其中找到错误的意义。

专家分析师永远不会试图向你推销一个通过拷问数据发现的故事,直到它坦白为止。相反,在谈论他们的发现时,他们使用模糊/软化的语言,他们有纪律为每件事提出多种解释。如果你怀疑某人是分析绝地,试试这个技巧:让他们为你解释一些数据/图表。他们产生多种解释的速度越快,产生的替代方案越多,他们的力量就越大。如果他们只停留在一个解释上,他们仍然处于业余幼虫阶段。

为了提升你的绝地技能,请到这里阅读我的《阿波菲斯尼亚分析》的长篇指南。

如果分析是你的激情,拥抱它。达到你选择的职业的高度,不要听任何人告诉你换一个不同的职业。照片由亚历山德罗·埃尔贝塔Unsplash 拍摄

本文到此为止。在下一期中,我们将继续探讨业余分析师和专业分析师之间的差异:

专业数据与业余数据的差异# 7——对数据的现实预期
专业数据与业余数据的差异# 8——知道如何增加价值
专业数据与业余数据的差异# 9——对时间的不同思考
专业数据与业余数据的差异# 10——细致入微的卓越观

让我知道你是否喜欢这个话题,不要忘记与你的社区分享你最喜欢的见解!

本系列的前几篇文章

*****

本系列的下一篇文章

https://kozyrkov.medium.com/how-to-form-realistic-expectations-about-data-622e85ab62cb

附言:你有没有试过在 Medium 上不止一次点击这里的鼓掌按钮,看看会发生什么? ❤️

喜欢作者?与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeSubstackLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格联系。*****

流星问题——简单解法和泊松过程演示

原文:https://towardsdatascience.com/shooting-star-problem-simple-solution-and-poisson-process-demonstration-739e94184edf

使用 TensorFlow 缩短模型部署

原文:https://towardsdatascience.com/shortening-model-deployment-with-tensorflow-d5a11044d0d

如何使用 TensorFlow 简化 ML 模型部署和服务

稳定扩散生成的图像来自提示“酷炫的机器学习和 AI stuff”。

U 通常,对于现实生活中的机器学习应用程序,最终目标是在生产中部署模型以供客户使用。但对于一个由机器学习应用程序驱动的系统来说,不仅仅是模型预测,还有另外两个主要步骤是预处理和后处理。

预处理与实际预测之前的所有步骤相关。对于图像分类,它可以是归一化,通常,视觉模型要求输入像素在 0 和 1 之间。在文本模型的情况下,预处理可以是文本标记化或删除空格和标点符号。预处理可以采取多种形式,但归根结底是处理输入,以便模型可以做出可靠的预测。

NLP 任务的预处理管道

另一方面,后处理负责处理模型输出所需的所有步骤,以使其呈现理想的形式。在分类任务的情况下,通常,模型输出每个类别的概率,但是终端用户通常不关心这些,所以我们需要处理这些输出,以便它们可以采用标签的形式,在文本情感分析中,这些标签通常是“正面”和“负面”。

后处理管道

当预处理和后处理成为一个复杂系统的一部分时,挑战就开始了,这个复杂系统可能已经在使用中,并且需要维护。

想象以下场景—您是一家提供医疗保健 ML 产品的公司的成员,该产品包括一个 API,客户可以使用该 API 发送图像并预测 x 射线是否有健康问题。最初,工程师们使用 ResNet-50 模型创建了一个具有良好结果的基线,因此该公司开始使用它,但经过几周的研究,科学家们提出了一个使用 MobileNetV2 的改进版本,因此团队更换了模型,几周后,他们再次提出了一个使用 EfficientNetB3 的更好的模型。太好了!在一个月内,您的结果提高了 3 倍,您的客户也更高兴了,但这里的挑战是什么?

在那一个月里,你必须调整你的系统来处理三种不同的模型,每一种模型都有不同的输入。举例来说,让我们假设团队使用基于 TensorFlow 的模型,并使用 Keras 应用模块加载它们,在那里您可以看到预训练的 ResNet-50 期望输入具有 224 x 224 x 3(宽度 x 高度 x 通道)的大小,通道是 BGR 的格式,并且每个颜色通道相对于 ImageNet 数据集以零为中心。 MobileNetV2 也期望图像大小为 224 x 224 x 3,但期望像素在-1 和 1 范围内。最后,efficient net B3期望图像的大小为 384 x 384 x 3,并且已经将图像缩放作为模型的一部分。
在这个场景中,您有一个带有特定预处理例程的初始管道,然后必须更改它以支持第二个模型,然后再次更改它以支持第三个模型,现在想象一下,如果您需要一周多次交换模型,并行服务多个模型,回滚到以前的模型,对照新模型评估旧模型,很容易混淆不同的预处理例程,丢失代码,或者忘记重要的细节。 特别是因为该公司很可能为不同的客户提供多种服务,这只是为了展示事情有多容易失控。

那么我们能做什么?虽然管理这样一个系统确实是一项复杂的任务,但是我们可以通过将模型的预处理和后处理逻辑融合或嵌入其中来使事情变得容易得多。

常规流水线与嵌入处理逻辑的流水线

让我们通过一个实际的例子来展示这个概念。
在这里,我将使用 TensorFlow 和“克里夫兰心脏病诊所基金会”数据集构建一个简单的系统,目标是对患者是否患有心脏病进行分类。这个例子是基于 Keras 代码的例子“结构化数据分类从零开始”。
你可以按照这个例子用 Colab 笔记本来写完整的代码。

为了简化起见,我们将只使用这些功能的一个子集,以下是关于它们的一些信息:

年龄:年龄以年为单位“数值”
性别: (1 =男性;0 =女性)
CP: 胸痛类型(0,1,2,3,4)
Thal:3 =正常;6 =修复缺陷;
7 =可逆性缺损【目标:】诊断心脏病(1 =真;0 =假)“目标(二进制)”

我特别选择了这些特性,因为它们有不同的数据类型和不同的预处理要求。

张量流预处理层

TensorFlow 有一种内置的方式来处理不同的数据类型,即预处理层,与常规预处理步骤相比,它们的一大优势是您可以将这些层与模型或 TensorFlow 数据集结合起来,以优化端到端的管道,同时使部署变得更加容易。

对于常规用例,需要对这些层进行调整以了解如何处理数据,“调整”步骤类似于我们在标准化数据之前需要了解某个特征的均值和标准差,这就是我们在第一种情况下要做的事情。

标准化(年龄特征)

对于数字特征“年龄”,我们将应用特征归一化,换句话说,我们将移动和缩放输入到以 0 为中心的分布中,标准差为 1,这正是[normalization](https://keras.io/api/layers/preprocessing_layers/numerical/normalization/)层所做的。首先,我们创建一个 TensorFlow 数据集,该数据集只加载来自 pandas 数据框的数据和原始要素和标注格式的地图

raw_data = tf.data.Dataset.from_tensor_slices(
            (dict(data[[“age”, “sex”, “cp”, “thal”]]), data.target))

之后,我们可以创建预处理层,通过使用adapt方法来学习如何使用该数据集对要素进行预处理。

age_preprocessing = L.Normalization()
age_preprocessing.adapt(raw_data.map(lambda x, y: x["age"])
                              .map(lambda x: tf.expand_dims(x, -1)))
print(f"Age mean: {age_preprocessing.mean.numpy()[0]:.2f}")
print(f"Age variance: {age_preprocessing.variance.numpy()[0]:.2f}")
---------- Outputs ----------
Age mean: 54.27
Age variance: 81.74

这里发生的事情是,我正在获取raw_data数据集,并通过映射 lambda 操作仅提取age特征,然后我扩展维度以适应预处理层的预期格式。
适应数据后,[Normalization](https://keras.io/api/layers/preprocessing_layers/numerical/normalization/)层学习到“年龄”特征的均值为 54.27,方差为 81.74。现在在此基础上,它可以正常化该特征的新数据。

IntegerLookup(性特征)

接下来,我们可以学习预处理“性别”特征,这是一个分类数字特征,这意味着每个值实际上意味着不同的类别,这里它们是(1 =男性;0 =女性)。对于这种特定情况,我们还希望该层能够处理未知或错误的值,在正常情况下,如果输入不同于 1 或 0,该层将抛出错误,并且不会给出输出,但这里这些值将被预处理为“-1”,模型将返回预测,这种行为取决于每个上下文。

sex_preprocessing = L.IntegerLookup(output_mode="int")
sex_preprocessing.adapt(raw_data.map(lambda x, y: x["sex"]))
sex_vocab = sex_preprocessing.get_vocabulary()
print(f"Vocab size: {len(sex_vocab)}")
print(f"Vocab sample: {sex_vocab}")
---------- Outputs ----------
Vocab size: 3
Vocab sample: [-1, 1, 0]

这里的output_mode=”int”表示这一层的输出是一个Integer
在适应数据之后,[IntegerLookup](https://keras.io/api/layers/preprocessing_layers/categorical/integer_lookup/)层了解到“性别”特征具有大小为 3(包括 OOV)的词汇表,并分配[-1, 1, 0] 作为输入的可能值。请注意,这一层的主要好处是处理未知值,因为输出等于输入。

整数查找(cp 功能)

对于另一个分类数字特征“cp”特征,我们也将使用类似于“sex”特征的[IntegerLookup](https://keras.io/api/layers/preprocessing_layers/categorical/integer_lookup/)层,但是我们将改变输出格式。

cp_preprocessing = L.IntegerLookup(output_mode="one_hot")
cp_preprocessing.adapt(raw_data.map(lambda x, y: x["cp"]))
cp_vocab = cp_preprocessing.get_vocabulary()
print(f"Vocab size: {len(cp_vocab)}")
print(f"Vocab sample: {cp_vocab}")
---------- Outputs ----------
Vocab size: 6
Vocab sample: [-1, 4, 3, 2, 1, 0]

这里的output_mode=”one_hot”表示该层的输出具有一键编码格式,在这种情况下,每个输出都具有类似于“[0,0,0,0,1,0]”的格式,这种格式可用于为模型提供更多关于特征的信息,尤其是如果特征不具有布尔序数性质。
在适应数据之后,[IntegerLookup](https://keras.io/api/layers/preprocessing_layers/categorical/integer_lookup/)层了解到“cp”特征具有大小为 6(包括 OOV)的词汇表,并分配[-1, 4, 3, 2, 1, 0]作为输入的可能值,进一步处理为独热编码格式。

字符串查找(thal 特征)

最后,对于“thal”特性,我们将使用[StringLookup](https://keras.io/api/layers/preprocessing_layers/categorical/string_lookup/)层,对于该特性,我们有可以作为文本或数字出现的字符串值。

thal_preprocessing = L.StringLookup(output_mode="one_hot")
thal_preprocessing.adapt(raw_data.map(lambda x, y: x["thal"]))
thal_vocab = thal_preprocessing.get_vocabulary()
print(f"Vocab size: {len(thal_vocab)}")
print(f"Vocab sample: {thal_vocab}")
---------- Outputs ----------
Vocab size: 6
Vocab sample: ['[UNK]', 'normal', 'reversible', 'fixed', '2', '1']

与“cp”功能类似,我们也有output_mode=”one_hot”,这意味着该功能的输出也将有一键编码格式,因为词汇表很小,这个选项应该很好。
在适应数据之后,[StringLookup](https://keras.io/api/layers/preprocessing_layers/categorical/string_lookup/)层了解到“thal”特征具有大小为 6(包括 OOV)的词汇表,并且将[‘[UNK]’, ‘normal’, ‘reversible’, ‘fixed’, ‘2’, ‘1’]分配为输入的可能值,这里[UNK]被分配为未知值(OOV),并且这些值被进一步处理为独热编码格式。

合并图层和数据集

现在我们已经有了适合我们数据的所有预处理层,我们可以让它们成为我们数据集管道的一部分。这样做的动机是 tf.data pipeline 可以利用预处理层来使数据摄取更快、更有效。另一种选择是将这些层用作模型的一部分,并以这种方式对其进行训练,本文的“在模型之前或模型内部预处理数据”一节中讨论了这种权衡。

由于这不在本文的讨论范围之内,我就不详细介绍这一部分了,但是你可以在“数据集”部分看看相关的 Colab 笔记本

建模

在常规设置中,对数据进行预处理后,您将得到如下所示的模型:

age_input = L.Input(shape=(1,), dtype=tf.float32)
sex_input = L.Input(shape=(1,), dtype=tf.float32)
cp_input = L.Input(shape=(len(cp_vocab),), dtype=tf.float32)
thal_input = L.Input(shape=(len(thal_vocab),), dtype=tf.float32)concat_inputs = L.Concatenate()([age_input, sex_input, 
                                 cp_input, thal_input])
x = L.Dense(32, activation="relu")(concat_inputs)
x = L.Dropout(0.5)(x)
output = L.Dense(1, activation="sigmoid")(x)

model = tf.keras.models.Model(inputs=[age_input, sex_input, 
                                      cp_input, thal_input], 
                              outputs=output)
model.summary()
---------- Outputs ----------____________________________________________________________________
Layer (type)              Output Shape Param # Connected to                      ====================================================================
age (InputLayer)          [(None, 1)]  0       []                                                                                                                                    sex (InputLayer)          [(None, 1)]  0       []                                                                                                                                    cp (InputLayer)           [(None, 6)]  0       []                                                                                                                                    thal (InputLayer)         [(None, 6)]  0       []                                                                                                                                    concatenate (Concatenate) (None, 14)   0       ['age[0][0]', 
                                                'sex[0][0]',
                                                'cp[0][0]', 
                                                'thal[0][0]']                                                                                                                        dense (Dense)             (None, 32)   480     ['concatenate[0][0]']                                                                                                         dropout (Dropout)         (None, 32)   0       ['dense[0][0]']                                                                                                                   dense_1 (Dense)           (None, 1)    33      ['dropout[0][0]']                                                                                                                    ====================================================================
Total params: 513
Trainable params: 513
Non-trainable params: 0
____________________________________________________________________

常规型号的图表

这个模型会有我们在开始时讨论的所有问题,对于部署,我们需要跟踪在训练期间使用的准确的预处理参数,这将需要维护人员的大量工作。幸运的是,TensorFlow 允许我们将模型预处理逻辑嵌入到模型中。

结合模型和预处理

将预处理层与我们的模型结合起来是相当简单的,事实上,我们基本上可以将任何可以转化为张量流图的张量流操作结合起来,让我们看看新模型是什么样子的:

age_input = L.Input(shape=(1,), dtype=tf.int64)
sex_input = L.Input(shape=(1,), dtype=tf.int64)
cp_input = L.Input(shape=(1,), dtype=tf.int64)
thal_input = L.Input(shape=(1,), dtype=tf.string)# Preprocessing
age_processed = age_preprocessing(age_input)
sex_processed = tf.cast(sex_preprocessing(sex_input), 
                        dtype=tf.float32)
cp_processed = cp_preprocessing(cp_input)
thal_processed = thal_preprocessing(thal_input)# Model prediction
output = model({"age": age_processed, 
                "sex": sex_processed, 
                "cp": cp_processed, 
                "thal": thal_processed})# Postprocessing
label_postprocess = label_postprocessing(output)
model = tf.keras.models.Model(inputs=[age_input, sex_input, 
                                      cp_input, thal_input], 
                              outputs=label_postprocess)
model.summary()
---------- Outputs ---------- ____________________________________________________________________
Layer (type)          Output Shape Param # Connected to                      ====================================================================
sex (InputLayer)      [(None, 1)]  0    []                                                                                                                                    age (InputLayer)      [(None, 1)]  0    []                                                                                                                                    cp (InputLayer)       [(None, 1)]  0    []                                                                                                                                    sex_preprocessing     (None, 1)    0    ['sex[0][0]']      
thal (InputLayer)     [(None, 1)]  0    []                                                                                                                                    age_preprocessing     (None, 1)    3    ['age[0][0]'] cp_preprocessing      (None, 6)    0    ['cp[0][0]'] 
tf.cast (TFOpLambda)  (None, 1)    0    ['sex_preprocessing[0] [0]']                                                                                                           thal_preprocessing    (None, 6)    0    ['thal[0][0]'] 
model (Functional)    (None, 1)    513  ['age_preprocessing[0][0]', 
                                         'cp_preprocessing[0][0]', 
                                         'tf.cast[0][0]', 
                                         'thal_preprocessing[0][0]']                                                                                                          label_postprocessing  (None, 1)    0    ['model[0][0]']  ====================================================================
Total params: 516
Trainable params: 513
Non-trainable params: 3 ____________________________________________________________________

组合模型的图表

请注意,我还包括了一个“后处理”层,该层负责将模型输出(0 或 1)映射到实际标签,我省略了创建它的代码,因为它与我们之前所做的类似,但它也包括在 Colab 笔记本中。
您可以看到模型本质上是相同的,但是第二个模型将所有预处理逻辑嵌入到模型图中,这种方法的优点是您可以保存和加载该模型,并且它将拥有运行推理所需的一切。让我们看看它们有什么不同:

第一个模型的推论

sample = {"age": 60, "sex": 1, "cp": 1, "thal": "fixed"}sample = {"age": age_preprocessing(sample["age"]),
          "sex": sex_preprocessing(sample["sex"]), 
          "cp": cp_preprocessing(sample["cp"]),
          "thal": thal_preprocessing(sample["thal"])}print(model.predict(sample))
---------- Outputs ----------
0

第二个模型的推论

sample = {"age": 60, "sex": 1, "cp": 1, "thal": "fixed"}print(model.predict(sample))
---------- Outputs ----------
"Have heart disease"

如您所见,使用第二种方法,您的推理服务需要做的就是加载模型并使用它。

结论

在本文中,我们讨论了我们在模型部署过程中面临的与模型的预处理和后处理相关的一些挑战,我们还研究了一种显著减少认知负载和维护工作以部署和服务模型的方法,即使用 TensorFlow 将所有逻辑嵌入模型本身。

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

基于 OSM 步行网络的最短路径算法

原文:https://towardsdatascience.com/shortest-path-algorithm-with-osm-walking-network-6d2863ae96be

利用爱沙尼亚塔尔图的 OSM 数据寻找最短步行路径

图片由作者提供。OSM 网络行走路线数据。从亲吻学生到塔尔图市的咖啡馆。

我终于回到了爱沙尼亚的文化之都塔尔图。美好思想之城神奇而辉煌。到处都是咖啡馆、酒吧、餐厅、文化活动和绿地。塔尔图为国际学生提供了一个舒适的城市校园,充满活力的氛围,我最喜欢的是,你可以步行到达大多数地方。令人惊讶的是,通过使用城市自行车、螺栓踏板车或仅仅通过步行,在城市中的移动是如此顺畅。

这个城市有很多地方可以参观,我决定用 OSM 步行网络来量化它。在这个故事中,我将解释如何在塔尔图的 OSM 步行网络中使用最短路径算法。主要的想法是量化从主要聚会点 、接吻的学生 到从 OSM 数据中提取的所有咖啡馆的距离。

看看这个故事的结局。

图片由作者提供。带有 OSM 步行网络和最短路径的最终地图

最短路径算法代码

要应用最短路径算法,你需要起点和终点。这些功能已经实现自动化,并在地理空间分析中发布。查看下面的文章,我是从哪里获取这些函数的。它们可用于 OSM 网络类型,如驾驶、自行车和步行。

https://medium.com/geospatial-analytics/shortest-path-algorithm-used-in-osm-street-network-33e0f864f34b

最短路径算法在由 Geoff Boeing 开发的 python 库 OSMnx 中实现,它获取开放的街道地图数据。

OSM 数据许可证

关于从 OSM 获取数据的回顾可以在下一篇文章中回顾:

https://medium.com/geekculture/fetching-green-areas-from-osm-data-a6ff835c40dc

编码实践

我们开始吧。

**import** pandas **as** pd
**import** geopandas **as** gpd
**from** shapely.geometry **import** LineString, Point
**import** osmnx **as** ox
**import** networkx **as** nx
**import** matplotlib.pyplot **as** plt

所有步行路线的起点将是接吻学生纪念碑。

**# creating the origin from Kissing Students** origin = gpd.GeoDataFrame(columns = [‘name’, ‘geometry’], crs = 4326, geometry = ‘geometry’)
origin.at[0, ‘geometry’] = Point(26.722181, 58.380073)
origin.at[0, ‘name’] = ‘Kissing students’

图片由作者提供。亲吻学生纪念碑。

现在,我们要去塔尔图城所有的咖啡馆。我们将使用这些位置作为最短路径分析的目的地。

**# fetching cafes as the destinations from OSM****# Specify the name that is used to seach for the data** place_name = ‘Tartu, Tartu linn, Tartu maakond, Estonia’**# Fetch OSM street network from the location** graph = ox.graph_from_place(place_name)**# Get place boundary related to the place name as a geodataframe** area = ox.geocode_to_gdf(place_name)**# Plot the streets** fig, ax = ox.plot_graph(graph)

图片由作者提供。塔尔图的 OSM 图。

从 Tartu 区域获取数据。

**# List key-value pairs for tags** tags = {'amenity': 'cafe'}**# Get the data** cafes = ox.geometries_from_place(place_name, tags)cafes = cafes[['name', 'geometry']]
cafes = cafes.reset_index(drop=True)**# getting centroids from polygons to avoid polygon geometric objects** cafes['geometry'] = [geom.centroid for geom in cafes['geometry']]

图片由作者提供。根据 OSM 的数据,塔尔图市有 43 家咖啡馆

这里我定义了在有起点和终点的步行网络中应用最短路径的函数。该函数是从 条最短路径算法得到的,用于 OSM 街道网络 做查看 Github repo 这个故事的查看功能。该分析直接返回 UTM 投影中的路线。

def **shortest_path**(origin, destination, network = ‘drive’):
 ‘’’
**origin and destination <GeoDataFrame> crs 4326, network <str> drive, bike, walk
RETURN shortest path <GeoDataFrame>**
 ‘’’

 **# creating a frame for fetching OSM data**
 combined = origin.append(destination)

 convex = combined.unary_union.convex_hull

 graph_extent = convex.buffer(0.02)

 **# fetching graph**
 graph = ox.graph_from_polygon(graph_extent, network_type= network)

 **# Reproject the graph**
 graph_proj = ox.project_graph(graph)

 **# Get the edges as GeoDataFrame**
 edges = ox.graph_to_gdfs(graph_proj, nodes=False)

 **# Get CRS info UTM**
 CRS = edges.crs

 **# Reproject all data**
 origin_proj = origin.to_crs(crs=CRS)
 destination_proj = destination.to_crs(crs=CRS)

 **# routes of shortest path**
 routes = gpd.GeoDataFrame()

 **# Get nodes from the graph**
 nodes = ox.graph_to_gdfs(graph_proj, edges=False)

 **# Iterate over origins and destinations**
 for oidx, orig in origin_proj.iterrows():

   **# Find closest node from the graph → point = (latitude, longitude)**
   closest_origin_node = ox.get_nearest_node(G=graph_proj, point=(orig.geometry.y, orig.geometry.x), method=’euclidean’)

 **# Iterate over targets**    for tidx, target in destination_proj.iterrows():
      **# Find closest node from the graph → point = (latitude, longitude)**
      closest_target_node = ox.get_nearest_node(graph_proj, point=(target.geometry.y, target.geometry.x), method=’euclidean’)      **# Check if origin and target nodes are the same → if they are → skip**
      if closest_origin_node == closest_target_node:
          print(“Same origin and destination node. Skipping ..”)
          continue

 **# Find the shortest path between the points**       route = nx.shortest_path(graph_proj, 
 source=closest_origin_node, 
 target=closest_target_node, weight=’length’)

 **# Extract the nodes of the route**       route_nodes = nodes.loc[route]

 **# Create a LineString out of the route**       path = LineString(list(route_nodes.geometry.values))

 **# Append the result into the GeoDataFrame**       routes = routes.append([[path]], ignore_index=True) **# Add a column name** routes.columns = [‘geometry’]

 **# Set coordinate reference system** routes.crs = nodes.crs **# Set geometry** routes = routes.set_geometry(‘geometry’)

 **return** routes

然后,我们就应用它。

routes = shortest_path(origin, cafes, network = 'walk')

这将返回一个包含 43 条路线的地理数据框架,我们可以在其中计算距离。

routes['distance'] = routes['geometry'].length

在一张柱状图中,我们可以观察到咖啡馆是如何集中在行走的前 1.000m。大约 20 分钟的路程?它有一些例外,有些咖啡馆离得很远,也可以在地图上看到。

图片由作者提供。从接吻的学生到咖啡馆的步行距离直方图

下一张地图显示了距离的强度。红色是最长的步行路线。

图片由作者提供。从接吻的学生到咖啡馆的步行路线按距离分类

如果你想回顾行走的网络。请检查一下这个故事的笔记本,看看它是如何被提取的。下一张地图显示了提取的步行网络的广度。

图片由作者提供。围绕使用的起点和终点的步行网络。

结论

塔尔图是一座步行的城市。如果你在市中心附*,在最*的 500 米到 1000 米范围内,你会有很多咖啡馆可供选择。因此,在接下来的 5 分钟、10 分钟、20 分钟内,你会看到坐下来喝杯咖啡的选项。上面的直方图可以从接吻的学生开始给出一个快速概览。python 库 OSMnx 在分析咖啡馆等服务的可访问性时很有用。可达性可以通过步行、驾车或骑自行车的距离来衡量。如果在 OSM 数据中没有找到所有服务,或者如果您希望不仅通过距离而且通过时间来分析可达性,则会发现局限性。

我应该每天检查我的数据质量吗?

原文:https://towardsdatascience.com/should-i-check-the-quality-of-my-data-every-day-db6cb3a438c0

提高数据质量是时间和成本之间的*衡

摄影Unsplash 上拍摄的照片

当我在进行数据质量培训时,一位与会者询问数据质量(DQ)检查的频率。我意识到这不是一个简单的答案。

所有数据并非生而*等;因此,DQ 检查因关键程度、时间和成本而异。尽管在每次更新或插入之后检查数据湖或数据仓库中的每个属性是非常好的,但是这样的操作通常是不可行的。

因此,每个组织都采用 DQ 框架来定义这些细节。所以尽管这个问题就像在问,“一根绳子有多长?”我们可以就一些有助于界定这一框架的基本原则达成一致。

定义关键数据

这是任何 DQ 框架必不可少的第一步。如果你不煮沸海洋,它会有所帮助。您的组织将哪些数据视为关键数据?临界的定义是什么?

高风险和高价值的数据。数据使您的组织能够:

  1. 增加创收机会
  2. 避免监管/声誉风险
  3. 实现效率,最终节约成本

如果数据没有做到以上任何一点,你应该问问自己,我们为什么要收集这些数据?一旦定义了这一点,就必须就您需要应用于这些数据桶的 DQ 检查的级别达成一致。

现在你知道了你的数据的重要性,试着找到 DQ 列表的优先级。这可以确保您将有限的 DQ 改进预算用在正确的属性上。

你可以基于几个因素的加权*均值来创建 DQ 优先级:这些数据多久被捕获、转换或传输一次?这些关键数据需要多少人工干预?这个数据需要的 DQ 分数是多少?

对于 DQ 检查,您应该将加权*均分数最高的属性优先于其他属性。一旦你有了清单,决定什么 DQ 检查你想申请和多久?

如果您不熟悉数据质量维度,我建议您首先阅读本文的第 7 条:

https://medium.com/geekculture/25-terms-to-help-you-in-your-career-in-data-science-14ca681b0699

技术 DQ 检查

技术 DQ 检查应在很大程度上实现自动化,并应用于所有已知的关键数据。每次 ETL 过程/数据捕获发生时,唯一性、完整性和完整性检查应该作为该过程的一部分进行。

如果 ETL 作业一天运行多次,您应该在一天的不同时间执行这些检查。这确保了重复的、不完整的和不一致的数据立即被提醒给工程团队。

根据检查数据及时可用性的原因,及时性检查可能属于技术或业务 DQ 检查。但是对于这个检查,每天检查以确保多个表的更新不会不同步,从而导致更多的下游数据问题是有意义的。

从一个结束表中恢复坏数据插入是非常复杂和昂贵的,对于这个场景,您应该每天执行 DQ 检查

商业 DQ 支票

如果糟糕的数据破坏了一份关键的商业报告或者影响了董事会的衡量标准,你会想要每天检查它的质量。根据业务 DQ 检查,检查频率将取决于业务优先级。

一旦您创建了基线配置文件,您应该根据业务优先级,每周或每月测试一次最重要的属性。准确性、有效性和一致性检查对于确保向下游反馈的信息可靠至关重要。

您的业务优先级可能是每天进行准确性和有效性检查,而一致性检查仅每周或每月进行一致性差的风险较小,因为完整性检查已经在您的技术 DQ 部门进行。

其他 DQ 支票

除了出于技术和业务原因的常规 DQ 检查之外,您可能想要检查特定用例的临时 DQ。例如,对于数据科学计划,开始一个新的数据工程项目等。此活动可能不定期,并取决于项目优先级。

如果项目创建了新的关键数据,则需要将其添加到关键数据元素清单中。在正常业务中,确保这属于业务或技术 DQ 部分。

临时 DQ 分析可能非常强大,因为它指出了一段时间没有检查或监控的数据区域。这为您提供了改善整体数据健康状况的机会。特别 DQ 检查将取决于用例,并且每天或每周检查

结论

因此,正如您所看到的,不幸的是,答案并不简单,它取决于检查数据质量的最终目标。我建议从上述原则开始,并根据你的学习调整你的策略。

如果你觉得这篇文章有帮助,请在下面留言告诉我。看看我在 Medium 上的另一篇文章:

如果您没有订阅 Medium,请考虑使用我的推荐链接订阅。它比网飞便宜,而且客观上能更好地利用你的时间。如果你使用我的链接,我会获得一小笔佣金,而你可以在 Medium 上获得无限的故事。

我也定期在推特上写东西;跟着我这里

我们应该在 Python 中使用自定义异常吗?

原文:https://towardsdatascience.com/should-we-use-custom-exceptions-in-python-b4b4bca474ac

Python 有如此多的内置异常,以至于我们很少需要创建和使用自定义异常。还是我们?

自定义异常:拥有自己的错误。照片由布雷特·乔丹Unsplash 上拍摄

我们应该使用自定义异常还是内置异常?实际上,这是一个非常好的问题。有人说,

不惜一切代价避免自定义异常。有如此多的内置异常,你很少需要一个自定义的。

还有人说,

在项目中使用自定义异常。将内置异常留给引发它们的典型情况,并引发自定义异常来通知与应用程序相关的错误,而不是代码本身。

Deitel 和 Deitel (2019)声称应该使用内置异常。Kapil (2019)建议在创建接口或库时使用自定义异常,因为这有助于诊断代码中出现的问题。Bader (2017)也是如此,他解释说,当代码遵循 时,自定义异常有助于用户,请求原谅比请求许可编码策略更容易。

如果您对这种情况感到困惑,或者您只是对自定义异常感兴趣,那么这篇文章就是为您准备的。我们将讨论是否应该在项目中使用自定义异常,而不是尽可能使用内置异常(所以,基本上,总是这样)。在我们继续之前,请注意这不是一个对与错的选择。相反,我们将寻找黄金法则,它将帮助我们找到正确的*衡。

定义自定义异常

首先,让我们看看如何在 Python 中定义自定义异常。这是一个简单的任务,因为您唯一需要做的就是创建一个从内置的Exception类继承的类:

我将在下面展示你可以做得更多;但事实是,大多数时候我只使用一个空类(实际上,它不是空的,因为它是从Exception继承的),因为这就是我所需要的。通常,我会添加一个描述性的文档字符串:

如您所见,如果添加 docstring,您不需要使用pass语句。也可以用省略号()代替pass语句。这三个版本的工作方式相同。选择在特定情况下效果更好的,或者你更喜欢的。

注意命名有多重要。在计算预测过程中出现问题时,这似乎是一个非常通用的定制例外。根据您的需要,您可以创建更加具体的异常,但是不要忘记使用信息丰富的名称。Python 中的一个通用规则是使用简短但信息丰富的名称。具有讽刺意味的是,自定义异常构成了一个异常,因为它们通常有很长的名称。这是因为大多数人——包括我自己——喜欢使用独立的异常名,我认为您也应该遵循这条规则。考虑这些信息丰富且模糊的异常名称对:

  • NegativeValueToBeSquaredErrorSquaredError
  • IncorrectUserNameErrorInputError
  • OverloadedTruckErrorNoLoadOnTruckErrorLoadError

左边的名字比右边的更具体,信息更丰富。另一方面,您可以将右边的异常视为左边的可以继承的一般错误;例如:

这被称为异常层次。内置错误也有其层次。异常层次结构可以服务于一个重要的目的:当你创建这样一个层次结构时,用户不必知道所有特定的异常(Lutz 2013)。相反,知道并捕捉一般的异常(在我们的例子中,它是LoadError)就足够了;这将使得捕捉从它继承的所有异常(OverloadTruckErrorNoLoadOnTruckError)成为可能。Bader (2017 年)强化了这一建议,但警告不要让这样的层级结构过于复杂。

然而,有时追求简单就足够了:

如果你认为NoLoadOnTruckError不应该是一个错误,因为卡车有时确实会空载,那你就对了。但是,请记住,异常不一定意味着错误;他们的意思是…嗯,他们的意思是例外。然而,这是一个以“Error”结束异常类名称的 Python 规则,所有内置异常都是这样命名的(例如,ValueErrorOSError)。

引发自定义异常

自定义异常的引发方式与内置异常相同。然而,值得记住的是,我们可以用几种方法做到这一点。

检查一个条件,如果不满足就加注

不满足特定条件时会引发异常。您可以在有消息或没有消息的情况下执行此操作:

捕捉一个内置异常并引发一个自定义异常

在这里,我们不提高ZeroDivisionError,而是提高自定义EmptyVariable Error。一方面,这种方法可以提供更多的信息,因为它指出了问题所在。另一方面,它并没有讲述整个故事;也就是说,仅提高EmptyVariableError不会通知用户变量为空,因此在使用get_mean()计算*均值时会被零除。开发者决定用户是否应该知道这些详细信息的需求;有时,没有这种需要,但其他时候追溯传递的信息越多越好。

我们可以通过与EmptyVariableError一起提供的消息来传达这一信息,但是有一种更好的方式来做到这一点——如下所述。

捕捉一个内置异常,并从中产生一个自定义异常

这里,我们在回溯中包括了EmptyVariableErrorZeroDivisionError;我们唯一改变的是在前面代码片段的第 10 行添加了as e,在第 11 行添加了from e

这个版本提供了更多的信息,因为它说了更多的细节:变量是空的,没有数据,当使用get_mean()计算*均值时,由于缺少数据而引发了ZeroDivisionErrorZeroDivisionError: division by zero这么说吗?肯定不是直接的,但是你需要仔细地分析回溯来间接看到它。

因此,从内置异常引发自定义异常有助于您传达关于所发生事件的更详细的信息。

丰富自定义异常

到目前为止,我们只使用了简单的自定义异常。尽管简单,但这种方法是高度可定制的,这是由于在引发异常时能够使用任何消息。但是,我们可以创建一个内置在异常类中的消息。这样,您就不必在引发异常时提供消息。看这里:

因此,如果您不提供truck_no,将不会使用任何消息。当您这样做时,NoLoadOnTruckError将被引发,并显示消息"The truck 12333 is empty"。这是一个简单的例子;你可以在这里阅读更多关于这个话题的

我只在一种情况下使用这种方法:否则我将不得不在代码的几个地方使用相同的长消息,这时我会引发这个异常。然后,用内置消息扩展这样一个异常类是有意义的,但是我尽可能保持这个类简单,尽量不使它的代码复杂。

然而,在其他情况下,我通常只使用一个从Exception基类继承的空类:提供我所需要的最简单的解决方案。尽管没有提供更丰富的功能,但丰富的异常类可能会变得复杂。此外,这样的类不能被进一步定制;您只能省略消息或使用内置于类中的消息,但不能使用不同的消息。考虑到这一点会使这个类变得更加复杂。

示例

既然我们已经知道了如何定义定制异常,那么让我们来看看它们是如何工作的。

首先,注意我改变了表示代码的方式,命令以>>>开始。这就是如何在[doctest](https://docs.python.org/3/library/doctest.html)中格式化代码及其输出,这是一个用于文档测试的内置模块。您不仅可以将文档作为doctest测试来运行,以查看代码是否运行良好,而且您还可以轻松地区分代码及其输出。

我决定在类型提示中使用类型别名。在我看来,这样的类型提示比直接添加到函数签名中的复杂类型提示更具可读性。因此,我们有了TimeSeriesDatesTimeSeriesValues类型,两者都是列表,前者是datetime.datetime.date对象,而后者是浮点数。

然后,我创建了IncorrectTSDataError,这是一个在时序数据不正确时使用的异常。注意,我们在数据不正确的三种不同情况下使用相同的异常类。这是一个简单的解决方案,在这种简单的情况下就足够了。我们可以创建一个异常层次结构,从IncorrectTSDataError继承三个定制异常。然而,在您的项目中,也许更复杂的异常层次结构会更好。

然后,定义主建模函数build_model()。当然,这是一个简化的函数,它只做两件事:

  • 检查数据是否正确:ts不缺失,y不缺失,tsy长度相同;和
  • 构建模型(由run_model()函数表示)。

我们可以将检查代码移到一个专用的函数中(例如check_data()),如果build_model()的代码变得更长,我肯定会这么做。

如果其中一项检查失败,该函数将抛出IncorrectTSDataError异常,消息取决于出错的原因。否则,该功能继续并调用run_model()功能。当然,这里的数据检查过于简单,因为它仅用于表示目的。我们可以检查数据是否确实是一个datetime.datetime.date列表;我们可以检查点的数量是否足以建立预测模型;等等。

现在,看看我们是如何运行run_model()函数的:我们使用try-except块来这样做,以便能够捕获在这个过程中抛出的任何错误。当我们捕捉到一个错误时,我们不沉默它,而是重新引发它,从它引发PredictionError:raise PredictionError from e。为了简单起见,我没有在错误中包含该消息。这样,原始错误将包含在回溯中。

为了运行它并看看它是如何工作的,我们需要run_model()函数。让我们创建一个只会引发错误的模拟(这里,ValueError)。

一个对象的模仿 是它模仿原始对象行为的人工表示,在我们的例子中,是run_model()函数。这意味着我们不需要整个对象(函数),只需要它的模拟来做我们需要模仿的事情。在这里,它养ValueError就够了。

这样,无论何时我们运行该函数,它都会引发ValueError:

这是我们正在运行的应用程序。我们将从单变量分布中生成值,因此数据中不太可能有趋势。

第 23–25 行的完整回溯如下所示(用省略号代替路径、名称等。):

我们现在应该看看使用内置异常而不是PredictionError进行回溯会是什么样子。我们需要首先改变build_model()函数:

这个版本的build_model()函数实际上没有多大意义,因为它只是简单地调用了run_model()。然而,它可以做得更多;例如,它可以检查数据或进行数据预处理。

让我们在与上面完全相同的场景中检查一下回溯:

查看下图来比较这两种追溯:

左侧是使用自定义异常时的回溯。右侧使用了内置的 ValueError。来源:作者。

请注意以下几点:

  • 使用定制异常的回溯提供了最初的错误(ValueError),但是使用定制的PredictionError 异常和定制的消息来解释它。
  • 与此同时,当使用内置异常类时,带有原始ValueError的部分比相应的回溯更简洁易读。

你同意我的观点吗,自定义异常的追溯更清晰?

raise MyException from AnotherExcepion语法非常强大,因为它使您能够显示根异常的回溯,以及定制异常的回溯。这样,就导致错误的问题而言,回溯比仅使用内置异常时获得的回溯信息要多得多。

结论

定制异常很容易创建,尤其是当您不去添加.__init__().__str__()方法时。这是一种非常罕见的情况,更少的代码意味着更多的功能。通常情况下,从Exception类继承一个空类是可行的方法。

但是对于一个空类,您需要做出一个决定:是否要写一个 docstring。(至于是选择pass语句还是省略号根本无关紧要,所以我们可以忽略它。)而答案是,视情况而定。这取决于你对你的班级有什么期望。如果它是一个服务于不同异常的通用类,那么你可能需要一个 docstring 来说明这一点。但是,如果选择这个选项,您需要考虑几个更精确的异常类是否能更好地完成这项工作。我不是说他们会,因为通常你不想使用 50 个自定义异常;不像你钱包里有 100 美元的钞票,有时 5 美元比 50 美元好。

还有一件事很重要:一个好的、信息丰富的名字。如果您使用了一个好的名称,那么即使没有 docstring 和消息,您的异常类也可能是独立的。即使您确实需要 docstring 和消息,您的异常仍然需要一个好的名称。一个好处是异常类可以有比典型 Python 对象更长的名称。所以,IncorrectAlphaValueErrorMissingAlphaValueError甚至看起来都不算长。

真正伟大的是raise from,它使我们能够从代码内部引发的异常中引发自定义异常。这个功能允许我们在回溯中包含两个部分:一个与最初引发的异常相关(尽管它不是必须内置的),它显示发生了什么以及发生在哪里;我们可以称之为误差的基本来源。第二部分与我们的定制异常相关,实际上使用与项目相关的词语向用户解释实际发生了什么。将这两部分结合起来使得回溯功能更加强大。

然而,不管你是在做一个你想提供给其他人的包,一个商业项目,还是其他任何东西(例如,一个展示带有一些分析的报告的笔记本),这都是有区别的。更详细地解释:

  1. 供他人使用的包。这种包通常会受益于自定义异常。它们需要被很好地设计和明智地使用,这样它们才能准确地显示出哪里出了问题。
  2. 一个商业项目。自定义异常通常是一个不错的选择。内置异常提供关于 Python 相关问题的信息,自定义异常将添加关于项目相关问题的信息。这样,您就可以通过将 Python 代码与项目语言相结合的方式来设计您的代码(如果出现异常,还可以进行回溯)。
  3. 轻量级代码,像在笔记本里。类似地,这可以是一个脚本的代码,甚至是一个片段,使用一次或两次。通常情况下,自定义异常是多余的,会不必要地使代码变得复杂。笔记本通常不需要如此深远的异常处理;因此,在这种情况下,您很少需要创建自定义异常。

当然,这些规则总会有例外,所以根据您的经验和项目的细节,在特定情况下选择最佳方法。但是 不要害怕使用定制异常 ,因为它们可以极大地有益于您的项目。

我希望你喜欢这篇文章。不知何故,很多书都默默地省略了这个话题,甚至不提自定义异常。当然,也有反例,比如我在文中提到的那些书。然而,我觉得大多数作者并不认为自定义异常是一个有趣和/或重要到值得讨论的话题。我的观点是相反的,原因很简单,我发现定制异常在相当多的项目中很有价值。如果一个人从未尝试过使用自定义异常,也许是时候这样做了,并检查自己它们是如何工作的。

在我看来,自定义异常为诊断问题提供了一个极好的工具。Python 以可读性和用户友好性著称;使用自定义异常可以帮助我们进一步改进这一点,尤其是当我们设计我们的包时。如果您决定不惜一切代价只使用内置异常来避免自定义异常,那么您就有降低 Python 可读性的风险。

因此,我希望从现在开始,您不要害怕创建自己的异常类。当您处理一个接口或包时,不要害怕创建一个嵌套的异常类层次结构。但是,有时您不需要自定义异常。重点是,考虑您的 Python 项目是否会从自定义异常中受益总是好的。如果您决定使用它们,请记住不要过于复杂,并且永远不要忘记 Python 的哲理:“简单比复杂好”,以及“扁*比嵌套好”

资源

你应该在数据可视化中添加文本表格吗?

原文:https://towardsdatascience.com/should-you-add-text-tables-in-your-data-visualisations-bf59d080d312

作为一名数据分析师,这么多年来,在我所有的需求讨论中,有一件事是很常见的。我总是在房间里至少有一个人请求仪表板中的一个细节选项卡,其中有一个包含所有可能字段的表格。要求来自各种各样的利益相关者——工程师、工程经理、产品所有者、项目经理、CSM、tam、营销经理、材料经理、全球供应经理等等。

米卡·鲍梅斯特在 Unsplash 上的照片

在我职业生涯的早期,我总是试图说服利益相关者利用他们从仪表板而不是原始数据中获得的洞察力。我曾经觉得,当可以从数据库中方便地查询原始数据时,在仪表板上添加原始数据是没有意义的。但这改变了。

正如朱利叶斯·凯撒所说,“经验是一切的老师。”

随着时间的推移,我有机会与许多有不同类型问题的利益相关者一起工作。我意识到,作为一名分析师,如果我想为我的客户建立一个一站式的自助分析能力,就需要满足他们的所有需求。在一些特定的用例中,表格非常有用。

精细数据剖析— 假设您正在处理客户反馈数据,其中包含客户评级和评论。现在,您当然可以进行情感分析和文字云来显示分布情况,但如果最终用户想要确切了解人们在特定问题领域报告了什么样的问题,一个包含所有独特评论的表格会非常有帮助。

数据分发— 一些最终用户(尤其是面向客户的角色)将使用您开发的工具中的图表来创建演示文稿,以展示给他们的客户。这些客户端可能已经请求了表格格式的数据,以便它们可以在内部进一步接收这些数据。因此,仪表板上有一个现成的表格,其中只有相关的维度和指标,这简化了他们的工作。

进一步处理— 一些最终用户将下载数据,在电子表格软件中进一步处理数据,以做出商业决策或发布预测。从现有图表中下载数据可能会导致列数不足,或者所有列都包含多余的列,以后可能需要删除这些列。在仪表板上有一个定制的数据表将会非常快。

我说过数据表不仅重要,而且在某些用例中是不可避免的,我也相信这个表不一定是单调乏味的。这是一个文本密集型的数据可视化,这使得表格的简洁和专业外观变得尤为重要。

有一些非常简单的技巧可以让你的桌子看起来同样漂亮,同时又不影响功能。

1.限制列数

作为一个经验法则,我尝试选择最重要的维度和度量,并且只以表格不会水*滚动的方式显示它们。因为在大多数可视化工具中没有窗格冻结选项,水*滚动只会增加更多的问题而不是价值。

为了实现这一点,您应该非常清楚地知道该功能的最终目标。在需求阶段,我通常会问我的客户很多问题,以了解他们将如何使用这个工具,以及他们希望从中获得什么。这通常有助于我确定/列出表中显示的 8-10 个主要领域。

我使用的另一个策略是将多个字段合并在一个字段中。例如,我在其中一个数据集中保存了州和城市的信息,这对客户来说是很重要的。因此,我没有将它显示为两个单独的列,而是创建了一个位置字段——“城市,州”。这为我节省了一些空间。另一个例子是当有一个用户数据集时,需要显示用户的年龄和性别。因此,我将三个字段合二为一来显示—姓名|年龄|性别。这在没有任何信息损失的情况下创造了一些空间。

2.对比标题栏

使用深色背景的粗体标题。这立即提升了外观,使其看起来更整洁、更清爽。

3.适当的行高和列宽

增加默认的行高和列宽,在每行和每列中留出一些空白。这有助于提高可读性,并为大量文本的数据可视化提供喘息空间。

4.使用绩效指标

绩效指标是一种非常有效的方式,可以一目了然地显示趋势。您可以使用彩色的向上或向下箭头来显示积极和消极趋势*。您还可以添加一些条件格式,根据 KPI 值为特定列的背景着色。甚至迷你趋势图也可以添加到其中一个列中。

*可用性方面-请确保添加的指标不会影响可下载文件的数据质量。确保这一点的最简单方法是自己测试。

5.有意义的工具提示

这是所有类型的视觉化的一个普遍提示,尤其是对一张桌子。 不在工具提示中显示多余信息。 由于表格中的所有列都是文本的,所以在工具提示中显示相同的信息并没有额外的价值。请添加更多信息或禁用工具提示。

这些只是可以用来改善表格可视化的许多事情中的一部分。以下是我最*一次也就是的前后结果。

图像前(作者)

图像后(作者)

希望这些信息对你有用!请让我知道你是否有任何提高数据可视化的技巧。我很想了解更多。

感谢阅读!

下次见……

你应该继续提高你的机器学习模型的准确性吗?

原文:https://towardsdatascience.com/should-you-continue-improving-the-accuracy-of-your-machine-learning-model-5ad9c63c796b

行业笔记

你应该继续提高你的机器学习模型的准确性吗?

知道何时停止改进您的模型,以最大化产品价值

阿菲夫·库苏马在 Unsplash 上拍摄的照片

毫无疑问,机器学习(ML)算法通过在每个可能的领域为我们提供非常有用的产品,改善了我们的生活。在每个案例或产品中,预测并不总是 100%准确,或者不需要如此。要达到接* 100%的预测准确率是极具挑战性的,甚至是不可能的。那么什么是一个好的停止点,或者什么时候我们应该停止改进一个算法呢?

要实现一个好的 ML 模型,有许多众所周知的技术,如数据清洗(最重要的)、正则化、数据工程、降维、特征选择等。使用上述技术可以构建一个具有 80%–85%准确度的良好 ML 算法,但是要实现更好的准确度(85%–95%),需要大量的时间、努力、更深入的领域知识、极端的数据工程、更多的数据收集等等。在许多情况下,它可能不值得花费时间或精力,因为它有两个主要的含义:1 .产品发布的延迟(竞争对手可能领先并获得市场领导地位),以及 2 .增加了开发成本。我们总是可以发布一个精确度很高(75%-85%)的算法,然后在需要的时候即兴发挥。请注意,我不是在谈论错误修复和一些边缘情况的处理,这是模型维护例程的一部分。对于某些领域或产品,实际上达到 95%以上的准确率非常关键,例如自动驾驶系统、面部识别等。,在这种情况下,不断提高预测精度非常重要。

通常,任何 ML 算法(或产品)的开发都遵循以下模型(预测)准确性趋势。

  • 最初, 跳跃到 区域,在该区域中,在分类算法在数据清理和特征选择方面没有或很少努力的情况下,准确度简单地跳跃到> 50%。类似地,对于回归算法的情况,基于所使用的度量,模型精度被落在 0max 之间的分数代替。
  • leap 区域之后是 linear 区域,在该区域中,每花费一个单位的努力或时间,算法都以类似的比例得到改进。模型准确性和成本或工作量或时间之间的简单线性趋势。
  • 线性区域之后是 指数 (预期模型精度)或 渐* (预期成本)区域,其中随着模型精度变得更高,需要越来越多的努力(指数)来实现相同比例的精度增加。例如,假设改进落在线性区域,70%–>75%我们花费 2 个单位的时间或成本。然后,对于在指数/渐*区域的改进,对于 85%到> 90%,我们可能需要花费 10 个单位的时间或成本,而对于从 90%到> 95%的改进,我们可能需要花费 100 到 1000 个单位的时间或成本。

作者图片

为了改进落在指数区域内的算法超过 90%的范围(取决于应用或产品),我们可能需要招募该领域的专家(数据科学家、开发运营、产品经理等),提供昂贵的计算节点,购买或订购更多所需的工具和软件,参与广泛的研究等。这将显著增加算法构建和维护的成本。对于一些产品来说,花大量的钱来提高准确性和性能以超越竞争是有意义的(自动驾驶系统或股票价格预测),而对于一些消费产品(购物和图像分类的项目建议)来说,这可能没有意义。

“产品价值是客户通过使用产品满足其需求而获得的利益,减去相关的成本。复杂性是将这样的产品交付给客户所付出的努力。”休伯特·帕兰

产品价值=收益—成本

用于提高模型准确性的额外成本直接影响产品价值。虽然收益增加了,但随着我们将精度推得越来越高,成本也会成倍增加。在我看来,产品价值和模型准确性之间的不同趋势看起来就像下面的草图所描绘的那样。这些趋势取决于各种因素,如组成领域或领域、产品类型、需求、竞争等。

作者图片

  • 指数趋势:自动驾驶系统、股票预测、文字推荐系统等产品的价值。随着模型预测精度的增加而指数增加。没有人会对驾驶装有大量 FPs 和 fn 的自动驾驶汽车感兴趣,这使得它完全不可靠,并对车上的生命构成重大风险。类似地,如果不准确,文本推荐将被跳过或忽略,如果预测范围很宽,股票预测将被忽略。随着我们推动算法产生准确的结果,产品赢得了客户,并将成为市场领导者。
  • 渐*趋势:对于其他类型产品,如能源预测、需求预测、产品推荐、电影推荐、图像分类等。,产品价值随着我们提高算法的准确性而增加——与我们之前讨论的遵循指数趋势的另一组产品相比,即使准确率为 60–80 %,人们仍会对使用该产品感兴趣。
  • 其他:根据上述因素,肯定会有其他趋势,但这两个趋势是产品价值和模型准确性/成本/时间/工作量之间的常见趋势。

如果你打算通过考虑所有这些因素(产品价值、成本、准确性、时间和努力)来开发算法,你肯定会在短时间内开发出高价值的产品。

感谢阅读!我希望这有所帮助。请留下您的反馈,帮助我们进一步改进内容。

应该用 lambda 定义命名的 Python 函数吗?

原文:https://towardsdatascience.com/should-you-define-named-python-functions-using-lambda-fb5806c0898b

PYTHON 编程

这样做是违背 PEP8 的,那么为什么这么多作者这么建议呢?

在 Python 中,lambda 关键字允许定义匿名函数。由 Saad AhmadUnsplash 上拍摄的照片

许多作者声称,为了缩短你的代码,你可以使用[lambda](/lambda-functions-with-practical-examples-in-python-45934f3653a8) 函数来定义函数并给它们分配一个名字。一些初学者可能想尝试这种方法;说实话,我不仅在某个时候尝试过,而且乐此不疲。

请注意,这与使用未命名的lambda函数作为高阶函数的参数的情况不同,如下所示:

>>> x = [
...     ("Johny Faint", 197),
...     ("Jon Krohn", 187),
...     ("Anna Vanna", 178),
...     ("Michael Ginger", 165),
...     ("Viola Farola", 189)
... ]
>>> sorted(x, key=lambda x: -x[1])
[('Johny Faint', 197), ('Viola Farola', 189), ('Jon Krohn', 187), ('Anna Vanna', 178), ('Michael Ginger', 165)]

我们将讨论给一个名字分配一个lambda功能,如下所示:

>>> sort_fun = lambda x: -x[1]

不同之处在于赋值,用=表示,这意味着我们定义函数sort_fun(x),它在形式上是一个有一个参数x的函数。该函数假设x是一个至少包含两个元素的 iterable。当然,我们知道这个函数是为与上面使用的函数相同的排序而构建的,如下所示:

>>> sorted(x, key=sort_fun)
[('Johny Faint', 197), ('Viola Farola', 189), ('Jon Krohn', 187), ('Anna Vanna', 178), ('Michael Ginger', 165)]

这两种方法不同。当我们在高阶函数内部定义一个lambda函数时,它不能在其他地方使用;并且它严格地专用于这种特殊的用途。当我们定义一个lambda函数并给它指定一个名字(sort_fun)时,它可以在代码的其他地方使用。同样,在 Python 中,函数就是函数,不管它们是如何定义的,是使用def还是lambda;因此,sort_fun()是一个有效的 Python 函数。

本文讨论命名的lambda函数;也就是说,lambda被定义的功能被赋予一个名称。我们将讨论您是否应该这样定义函数,或者更确切地说,您应该避免这样做。我们将会看到 PEP8 对此会说些什么,但是我们也会对def定义的函数和lambda定义的函数进行比较。这将帮助我们决定lambda是否确实提供了一种在 Python 中定义简单函数的替代方式。

PEP8 对此怎么说?

尽管已经 20 多年了,我们仍然使用 PEP8 作为“Python 代码的风格指南”所以,如果你需要检查一些与编码风格相关的东西,PEP8 应该是你的第一来源。

使用分配的lambda函数的想法与 PEP8 的建议相违背,PEP8 使用精确的措辞来声明您不应该这样做:

总是使用 def 语句,而不是将 lambda 表达式直接绑定到标识符的赋值语句…

那么,这个真的意味着使用一个命名的lambda函数确实是违背 PEP8 的吗?我认为有两种可能性:

  1. 不,不是的!这个基于lambda的 PEP8 推荐已经过时了。例如,许多开发人员不同意 PEP8 推荐的 79 个字符的最大行长度,认为这是旧的气息。同样,lambda建议已经过时,我们不应该担心与 PEP8 背道而驰。PEP8 不是在 20 多年前,早在 2001 年就被创造出来了吗?在像 Python 编程语言这样现代的东西中,我们真的应该遵循如此古老的风格指南吗?
  2. 是的,确实如此!建议打破这种基于lambda的推荐的作者,只依赖一个前提:字符数。这就意味着,越短越好。世界没有那么简单,编程也没有什么不同:仅仅是lambda函数的简洁还不够。听 PEP8,不要用命名的lambda函数!

老实说,前一段时间我自己也偶尔使用这样命名的lambda定义。我只是喜欢它们的样子:简洁和与众不同。现在,我认为正是这种差异吸引了我。我知道我倾向于喜欢复杂的解决方案,那些看起来与众不同、非典型的解决方案,因为它们让我觉得我是一个优秀的程序员。但是这太误导人了!写复杂的代码并不意味着成为一名优秀的程序员。现在我知道了这一点,我试着小心并仔细检查我是否做了不必要的复杂。

所以,让我们客观地分析一下,基于命名的lambda的函数定义是否比基于def的函数定义更好——如果是的话,什么时候更好。

请注意,我们讨论使用lambda的一般情况,而只是命名的lambda定义,它将lambda功能分配给一个名称,如下所示:

foo = lambda x, u: f"{x} and {u}"

无论我们想出什么,都与在高阶函数中使用 lambdas 无关。

使用 lambda 和def定义函数

让我们来做一个实际的比较。为此,我将使用四个函数:

  • 没有参数的函数
  • 只有一个参数的函数
  • 有两个参数的函数
  • 有三个参数的函数

对于最后一个,我们将使用其中一个参数的默认值。以下是以传统方式定义的函数:

def weather():
    return "beautiful weather"

def square(x):
    return x**2

def multiply(x, m):
    """Multiply x by m.

    >>> multiply(10, 5)
    50
    >>> multiply("a", 3)
    'aaa'
    """
    return x * m

def join_sep(iter1, iter2, sep="-"):
    return [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

让我们看看这些函数在使用lambda s 定义时是什么样子的:

weather = lambda: "beautiful weather"

square = lambda x: x**2

multiply = lambda x, m: x * m

join_sep =lambda iter1, iter2, sep="-": [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

我们将从几个方面比较这两种方法。

代码简洁和视觉混乱

代码看起来更加密集。是好事吗?注意,分配的lambda函数并没有短多少:它们用= lambda替换了以下字符:def():return。因此,他们使用7而不是12字符,所以只有5字符更少(忽略空白)。那不是很多。

我认为lambda定义更密集的感觉来自于在一行中呈现它们。然而,我们可以使用def做同样的事情:

def square(x): return x**2

这个定义和lambda的定义在长度上没有太大区别,是吗?然而,使用def定义这样的短函数,我们在第一行之后使用新的一行是有原因的——对我来说,不是更多的字符;相反,它有助于避免视觉混乱。

因此,就代码简洁而言,我真的看不出选择lambda定义而不是def定义有什么意义。

就视觉杂乱而言,lambda的定义在我看来更糟糕,意思是更杂乱。再次比较使用lambda定义的multiply()函数:

multiply = lambda x, m: x * m

def:

def multiply(x, m):
    return x * m

后者中的额外空白有助于将功能直观地分为两个部分:

  1. 函数的签名:def multiply(x, m):
  2. 函数体:return x * m

lambda定义中,没有这样的视觉区分。相反,我们得到的是视觉混乱和密度,这需要额外的脑力劳动来区分函数的签名和主体。更糟糕的是,我们做的不是直接看到的签名!我们必须在头脑中创造它。

我不喜欢lambda定义的另一个视觉方面,这也让它们看起来很混乱。也就是说,函数名和它的参数不在一起,而是用“= lambda ”隔开。

清晰,无注释(类型提示)

因此,尽管有些作者说,lambda定义的简洁带来了视觉混乱和代码密度的增加。因此,对我来说,这种简洁是缺点而不是优点。

也许lambda函数的代码更清晰?在下面的代码块中,后面的定义是否比前面的更清晰?

def square(x):
    return x**2

square = lambda x: x**2

虽然我在阅读和理解这个lambda函数方面没有问题,但我不会说它比使用def关键字定义的函数更清晰。说实话,由于我已经习惯了def函数定义,所以前者对我来说更加清晰。

我们上面关于视觉混乱和代码密度的讨论也适用于此。我喜欢两行的def定义,它立即显示了函数的签名和主体。相反,lambda定义要求我在一行中通读整个代码,以便看到函数的签名和主体。这降低了代码的可读性——即使对于这个简单的函数也是如此。

让我们看看它在一个有两个参数的函数中是什么样子的:

def multiply(x, m):
    return x * m

multiply = lambda x, m: x * m

这一次,def版本对我来说似乎更加清晰。这也是由于在视觉上区分函数签名(包括函数参数)和函数体的一些问题。

这不是没有原因的,无论是谁提出了lambda定义,都是为了简短的函数。我们可以在join_sep函数的定义中看到这一点:

def join_sep(iter1, iter2, sep="-"):
    return [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

join_sep =lambda iter1, iter2, sep="-": [f"{v1}{sep}{v2}" for v1, v2 in zip(iter1, iter2)]

有人选择用lambda关键字定义的join_sep()作为更好的吗?对我来说,只有前者可以被认为是可读的,而后者过于困难——尽管我确实理解这个lambda定义。在这种情况下,理解不等于欣赏。

我提供这个例子只是为了强调一点,即使您决定使用lambda定义,您也应该为简单和简短的函数这样做;join_sep()是不是不够简单。

然而,def定义使我们能够做更多的事情来增加它的清晰度:文档字符串。让我重复一下multiply()定义的例子:

def multiply(x, m):
    """Multiply x by m.

    >>> multiply(10, 5)
    50
    >>> multiply("a", 3)
    'aaa'
    """
    return x * m

文档字符串是记录功能和用单元测试丰富功能的强大方法(例如,[doctest](https://docs.python.org/3/library/doctest.html) s )。您可以向命名的lambda函数添加任何类型的文档,除了行内注释。另一方面,我想你不会对需要 docstring 的函数使用lambda。然而,我会说multiply()不需要需要 docstring——但是有了它就更清楚了。

清晰,带注释(类型提示)

我们知道,当明智地使用时,类型提示可以增加函数的清晰度。考虑这两个例子:

from numbers import Number

def square(x: Number) -> Number:
    return x**2

square = lambda x: x**2
def multiply(x: Number, m: Number) -> Number:
    return x * m

multiply = lambda x, m: x * m

我们不能在lambda定义中使用函数注释。所以,每当你有一个函数,它的签名使用了有用的类型提示,基于def的定义总是更好,因为它比它的lambda定义传达了更多关于函数的信息。

现在,做一个简短的心理练习:想象一下既有 docstring 又有 type 提示的multiply(),看看这个定义比简单的命名的lambda定义好多少。

上面的例子表明,具有两个(或者更多)参数的函数可能比单参数函数更缺少类型提示。

结论

我们比较了 Python 中基于lambda和基于def的函数定义。在这样做的时候,我们只使用了两个方面:简洁和视觉混乱;和清晰度。这种对比并不十分富有——因为我们不需要让它变得富有。无论从哪个角度来看这两种定义,def总是比lambda更好。

即使在最简单的函数的情况下,我也没有找到足够的理由来使用lambda定义它们。通常情况下,相应的def定义更清晰,即使稍微长一点,它的代码在视觉上也不那么混乱和密集。

我很难想象一个双参数函数,特别是至少有一个参数的默认值,定义lambda时比定义def时更清晰。

最后,def定义使开发者能够使用lambda函数中没有的三个强大工具:

  • 函数注释(类型提示)
  • 文档字符串
  • 单元测试,通过 doctest 内部的 doctest

多亏了它们,我们可以让def函数提供更多信息。

感谢阅读这篇文章。我希望你喜欢它。

我真的希望我已经说服了你。即使这两种类型的函数定义对你来说都一样好——即使这样,我还是会而不是使用命名的lambda定义。这是因为使用它们,你什么也得不到,同时也冒着别人不同意你的风险。如果连这都不能说服你,记住这样做是违背 PEP8 的。

那么,在给一个函数命名时,为什么要使用命名的lambda定义呢?

资源

https://betterprogramming.pub/pythons-type-hinting-friend-foe-or-just-a-headache-73c7849039c7 https://medium.com/@nyggus/membership

你应该使用熊猫相关函数吗?

原文:https://towardsdatascience.com/should-you-use-pandas-corr-function-af82c454bc0

熊猫 corr()的局限性及对策

克里斯·利维拉尼在 Unsplash 上的照片

什么是相关性?

相关性被定义为两个随机变量之间的关联。在统计学中,它通常指一对变量线性相关的程度。

旁白:在谈论相关性时必须提到的一个强制性警告是“相关性并不意味着因果关系”。查看这篇文章了解更多。

您会经常发现自己在探索性数据分析中使用相关性。在监督设置中,可以查看特征和目标变量之间是否存在高度相关性,从而决定数据集是否可用于预测目标结果。在无监督的设置中,您可能会使用它来查看是否有要素之间具有高度相关性。这种相关性可能指向您可能想要删除的冗余功能。

大多数数据科学新手会发现自己在使用 pandas 内置的关联函数,即 pandas。DataFrame.corr(),如下所示

使用熊猫计算相关性的代码。DataFrame.corr()

使用 pandas 计算相关性的代码输出。DataFrame.corr()

如果您查看该功能的文档,根据设计,它具有最小配置。它允许您更改计算相关性的方法,例如,它提供了 Pearson、Spearman、Kendall Tau 计算相关性的方法。要了解更多关于这些方法的信息,请参阅下面的文章。

然而不管用什么方法。DataFrame.corr()函数仅返回由相关值组成的 DataFrame。

熊猫角的限制()

相关性只是故事的一半。当我们谈论变量之间的相关性时,我们理想情况下想要度量的是整个总体中变量之间的相关性。然而,大多数数据科学家处理的是数据样本。因此,如果我们获得不同的样本,我们可能会有不同的相关分数。因此,我们需要评估我们计算的相关值的显著性,这取决于样本大小。关于显著性检验的理论公式,请查阅这篇文章。

如果不是 corr()还有什么?

从应用角度来说,如果熊猫。DataFrame.corr()没有给出 p 值,如何找到它们?你可以自己计算。或者您可以使用 scipy 来计算相关性!

Scipy 的 stats 库提供了熊猫相关测试的所有三个版本。DataFrame.corr()即皮尔森斯皮尔曼肯德尔τ。使用它很简单,只需传入要比较的数据帧的列。考虑下面的片段

使用 Scipy.stats 计算相关性和 p 值的代码

用于计算相关性和 p 值的代码 Scipy.stats 的输出

从上面的输出中,我们可以看到 p 值,从而知道相关性有多重要。

结论

对于数据样本的相关性测试(这是您经常要做的工作),也要计算 p 值。因此,当处理样本数据时,请使用熊猫的 scipy.stats。DataFrame.corr()

展示求职面试的 GitHub 简介

原文:https://towardsdatascience.com/showcase-github-profile-for-job-interviews-b42d978a8a06

使用 Datapane 和 GitHub API

Adeolu EletuUnsplash 拍摄的照片

GitHub 绝对是协作编码和软件共享使用最多的*台。因此,它是候选人展示其项目和公司筛选潜在雇员的宝贵资源。

无论您是申请人还是雇主,查询 GitHub 个人资料的信息都非常耗时,并且会带来一些挑战。你需要搜索用户的数据,检查存储库,探索活动和语言等。😬 😒此外,通过一个漂亮的仪表板来总结概要文件的内容需要使用拖放工具,如 Power BITableau ,这需要相当大的努力。😰 😵更不用说为多个招聘信息或候选人重复这一点的丑陋感觉了。😫🤕

幸运的是,一些优秀的 API 能够以可定制和可复制的方式实现这一点。在这篇文章中,我们将使用data panePyGithub构建一个仪表板来展示 GitHub 账户的主要特性。

罗曼·辛克维奇·🇺🇦在 Unsplash 上拍摄的照片

设置

设置非常简单。只需克隆这个 仓库 并安装requirements.txt文件中列出的 3 个依赖项。

$git clone [git@github.com](mailto:git@github.com):clissa/github-user-dashboard.git
$cd github-user-dashboard
$conda create --name datapane python=3.9.6
$conda activate datapane
$conda install -c conda-forge --file requirements.txt

使用 GitHub API 提取配置文件数据

首先,让我们获得一些关于用户活动、其存储库和语言的数据。

连接到 GitHub API

为了方便起见,我将使用我的 GitHub 帐户作为例子,但是您可以很容易地修改代码以供自己使用。

尽管对于我们的分析来说不是绝对必要的,但是我们通过一个令牌连接到 GitHub API 来访问公共和私有信息。为此,您可以在这里获得一个令牌并将其作为GITHUB_API_TOKEN环境变量存储在一个.env文件中。剩下的工作由dotenv包来完成!

通过访问令牌连接到 GitHub API。作者要点

检索用户数据

这项任务取决于我们希望在仪表板中显示什么样的信息。我选择了与数量的存储库提交,根据获得的明星追随者,采用的编程语言,以及最重要的存储库(协作项目和最多明星)相关的统计数据。

当然,可能性是无穷无尽的!!请随意修改以下脚本以满足您的需求!

从 GitHub API 中检索用户数据。作者要点

米利安·耶西耶在 Unsplash 上拍摄的照片

数据面板仪表板

Datapane 是一个开源的报告 API,使能够构建交互式报告在其*台上托管,甚至嵌入第三方网站。所有这一切完全通过编程来保证快速再现性和自动化

我们的想法是将我们的仪表板分为 3 个选项卡:

  • 突出显示:显示一些关于用户的统计数据和信息
  • 语言:概述关于所采用的编程语言的见解
  • 存储库:展示用户对协作项目的贡献,总结最流行的存储库

结构块

为了做到这一点,我们可以使用 Datapane 的Page()类来构建每个选项卡。blocks参数收集显示在选项卡内的所有内容,而title决定选项卡的标题。

样本数据面板页面。作者要点

在每个选项卡内,可以通过Group()类设置页面结构。例如,我们可以嵌套几个这样的对象,以便在页面中清晰地表达我们的部分。我们使用block参数来列出组件,使用columns来指定结构,然后从结构中推断出每个元素的位置(默认情况下,内容按行填充)。

样本数据面板组。作者要点

另一个有用的构建块是Select()类,它充当嵌套内容的容器。除了通常的block参数外,type属性允许插入下拉菜单(SelectType.DROPDOWN)或标签(SelectType.TABS)。

样本数据面板选择。作者要点

内容块

Datapane 的伟大之处在于它为您可能想要插入到报告中的所有类型的内容提供了本地类,包括文本、图表、表格、各种媒体类型的嵌入、附件等等!

Text()类允许以纯文本或降价的形式插入文本内容,并自动检测编码和相应显示。对于HTML()Code()Formula()类,也可提供替代格式。

关于数据 vizs,Plot()类支持使用静态和交互式图表,与最流行的绘图框架无缝交互,如 Altair、Plotly、Bokeh、Matplotlib 和 Seaborn。

最常用的编程语言。作者图片

最后,Datapane 允许在报告中插入整个数据表。为此,您只需使用Data()(静态)或DataTable()(交互式)类并传递一个 pandas 数据帧。

用户存储库数据。作者列表

创建报告并上传

一旦我们拥有了仪表板的所有构件,那么我们只需要将它们放在一起创建一个报告。为此,我们只需要实例化一个Report()对象,并传递我们设计的所有块。

最后,我们可以在本地将我们的报告保存为一个 html 文件和/或直接免费上传到 Datapane!✨ 👏 ✌️

报告保存和上传。作者要点

Unsplash 上的窗口处拍照

那么,你准备好看我们 GitHub 用户仪表板的最终版本了吗?

可能最令人兴奋的 Datapane 特性是你可以在任何地方共享你的公共报告,甚至可以将整个仪表板作为交互元素嵌入到 Medium 中!这是结果📊 📈 😎

仪表板预览。作者图片

最后的想法

干得好,伙计们!我们刚刚创建了一个 GitHub 用户仪表板,用于在工作面试中展示我们的项目或总结潜在候选人的活动。

此外, Datapane 确保一切都易于定制和再现,使过程可扩展到多个应用或候选人!😃 😍 🍾 🙌 🎉

如果你想了解更多关于 Datapane 和 GitHub API 的知识,这里有一些有用的阅读资料:

  1. https://blog.datapane.com/
  2. https://www . techgeekbuzz . com/how-to-use-github-API-in-python/
  3. https://martinheinz.dev/blog/25

对于这篇文章来说,这就是全部。现在我真的很好奇你能拿出什么样的报告。欢迎在评论中给我你的数据面板的链接!

淋浴屏幕深潜在一个体面的咖啡机

原文:https://towardsdatascience.com/shower-screen-deep-dive-on-a-decent-espresso-machine-9389cbebbdf1

咖啡数据科学

拆掉淋浴帘

我在我的意式浓缩咖啡机中发现了对淋浴屏幕的兴趣。咖啡机的这一块并没有被谈论太多。淋浴帘确实控制着水进入圆盘,所以它可能和过滤篮一样重要。我深入研究了它是如何工作的,以更好地理解为什么我会在我的实验档案中看到一些奇怪的结果。我从视觉数据开始,在那里我看到了片面的行为,所以我扩展了数据收集,以更好地了解水流是如何沿着圆盘分布的。在分析过程中,视觉数据和流量数据有奇怪的不匹配,但显然输入水有轻微的不均匀性。

让我们从一个顶部滤网开始。我用了一段时间,主要的好处是保持淋浴屏幕更干净。我没有太注意深度清洁它,但我会在拍摄之间翻转它。有一天不小心没用上,看到我的蒸汽预注配置文件有了很大的提升。所以我清理了金属网,我发现简单清理后粘在里面的东西不到 0.01 克。我也试着暂时不用金属网。

所有图片由作者提供

然而,我注意到我的新轮廓在左侧保持同样的通道。不只是几个镜头,而是每一个镜头都表现出这种行为。

六个不同的镜头

所以我拉下淋浴的屏风去看引擎盖下面。我还拆了水分散屏。水孔只在外圈。我注意到水从一边流出的速度比另一边稍快。这可以解释我在照片里看到的。

体面的咖啡机有水进入分散屏幕,然后到 IMS 淋浴屏幕。

左:分散屏,右:淋浴屏。

我想要更好的关于拍摄的信息,因为这可能只是在拍摄开始时发生的,而不是根本原因。

更多空间数据

所以我拿出我的迷你冰块托盘,测量了一下。

我测量了分散筛上方的原材料的水量,然后是分散筛或分配器,最后是淋浴筛。这可以告诉我他们的水是从哪里来的。我惊讶地发现水并没有像我想象的那么均匀。

随着分散屏幕的出现,情况有所好转,但仍然有一个由外部更高的流量引起的甜甜圈。

淋浴屏幕的情况有所改善,但右边的水量更大。

这让我觉得在理解每个单元的淋浴屏幕方面还有很多工作要做,而且有很大的机会来改进浓缩咖啡的拍摄。我敢肯定为什么一些浓缩咖啡比其他的效果更好的部分原因是因为它们弥补了淋浴屏幕设计的不足。所以我相信一个更好的设计也会改善剖面设计。

有一部分仍然缺乏很好的解释。根据数据,淋浴屏幕的右侧有更高的流量,但在上面的拍摄示例中,左侧出来得更快。我不确定为什么会这样,机器是水*的。我有更多的数据来表明,内部提取轨道左侧流量较高。我已经调整了我的个人资料和冰球准备来解释这种奇怪的流动,但我希望我有一个更好的答案。

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

我的进一步阅读:

我的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

浓缩咖啡中的碎布过滤器

原文:https://towardsdatascience.com/shredded-cloth-filters-in-espresso-c06e7e13e876

咖啡数据科学

浓缩咖啡中的碎布过滤器

了解布料过滤器的实验

之前,我发现布过滤器通过在浓缩咖啡的底部或中间放置一个来加速咖啡的流动。我决定做一个疯狂的实验。如果我把一个布过滤器切成小块,混在冰球里会怎么样?

让我们来了解一下!

我把它切碎,弄湿碎片,稍微晾干,以确保它们是潮湿的,但不是浸湿的。

所有图片由作者提供

然后我把它们混合到咖啡里。

设备/技术

浓缩咖啡机:金特快

咖啡研磨机:小生零

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

击球准备:断奏捣固

预灌注:长

输液:压力脉动

过滤篮 : 20g VST

其他设备: Atago TDS 计Acaia Pyxis 秤

结果

这个镜头肯定出现了奇怪的一些主要的通灵。

从圆盘的底部看,这种模式明确表明了缓慢的流动和通道。

左:冰球中间的布。右图:整个冰球上的碎布。

从侧面看,冰球有点易碎。

绩效指标

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

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

用折射仪测量总溶解固体量(TDS),这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

投篮表现

为了对比,我拉了一个没有布的镜头,一个有碎布的镜头,一个中间有布的镜头。味道没有太大的区别,但 EY 是更高的布料在中间,因为流动更好。烤肉已经两个星期了,所以我可以再等一个星期。

****

三个镜头都有相似的拍摄时间,这与烘焙的时间有关,因为我必须做很长的预灌输时间。

这个实验背后的最初想法是改进使用布过滤器的过程。布料过滤器是改善浓缩咖啡效果的最佳补充,但随着时间的推移,它们很难管理。用布滤镜的前两个镜头不是最好的,但接下来的几个镜头更好。但是过滤器开始收缩,这导致了更多的侧沟流。如果你能加入碎布,也许你还能更持续地获得布料的优势。也许还有比我做这件事更好的方法。

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

我的进一步阅读:

我未来的书

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事首页

摄影启动页面

改进浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

浓缩咖啡中的粉末迁移

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡滤纸

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维修

咖啡评论和想法

咖啡实验

Python 函数的副作用

原文:https://towardsdatascience.com/side-effects-of-python-functions-822fae38295e

什么时候一个函数改变了一个定义在它自己作用域之外的变量?

您是否想知道 Python 函数如何改变在其作用域之外定义的变量?这可能会让来自其他语言的开发人员感到困惑,但是一旦您理解了一些基本原则,预测副作用就很简单了。

照片由 Unsplash 上的刻痕拍摄

函数副作用可能很难调试,而且大多数时候都不是有意的。相反,从函数中返回对象最好使用带有单个对象或元组的 return 语句。在 Python 中,副作用可能以两种方式出现:

  • 改变可变对象的值
  • 更改在本地命名空间之外定义的变量的绑定

我们将通过一组例子来研究这些方面。在此之前,我们将重温 Python 如何向函数传递参数。

在 Python 中向函数传递参数

如果您使用过其他编程语言,您可能听说过按值传递和按引用传递这两个术语。按值传递意味着将参数的副本传递给函数,而按引用传递意味着函数接收对参数的引用。您可能会惊讶地听到 Python 没有使用以上两者。当我开始使用 Python 时,我感到完全困惑,因为我认为只有两种选择,而 Python 函数显然都不适合。

在使用 Python 传递函数参数的机制之前,让我们快速回顾一下 Python 中的名称绑定。考虑以下代码:

第一条语句做了两件事。首先,它创建一个字符串对象,然后将这个对象绑定到变量a。第二条语句将另一个变量b绑定到绑定到a的对象。第三条语句创建了另一个字符串对象并将变量b重新绑定到它,而a仍然绑定到字符串'hello'。假设仍然有一个变量绑定到字符串'hello',它不会被垃圾收集。这看似琐碎,但要注意用词。我们没有使用诸如“将内存分配给一个字符串变量a并将字符串'hello'存储在其中”这样的表达式。相反,我们谈论创建对象并将变量绑定到它们。

那么可变对象呢,比如一个列表?以上同样适用于可变对象。然而,有一个警告。请看下面的代码:

那会打印

a -> 2199273128320 [10, 2, 3]
b -> 2199273128320 [10, 2, 3]

改变绑定到a的列表中的一个元素也会改变b。事实上,这种表达是有误导性的。只有一个列表,ab都绑定到它,这可以从id()函数返回的内存位置中看出。

接下来让我们看一个函数:

这张照片

outside function x -> 140731263354656, 5
inside function x_f -> 140731263354656, 5
{'x_f': 5}
inside function x_f -> 140731263354816, 10
{'x_f': 10}
outside function x -> 140731263354656, 5

这是怎么回事?在调用该函数之前,我们创建了一个整数对象并将变量x绑定到它。然后将参数传递给函数。我们可以看到函数参数x_f被初始化到函数外部变量x所指向的相同内存位置。这反映在由locals()函数返回的函数的本地名称空间中。在函数内部,我们创建了一个新的 integer 对象,其值为10,被反弹到局部变量x_f。这不影响在函数外绑定到x的对象。我们可以通过id()函数返回的内存位置和显示变量x继续绑定到整数5的最终打印语句来确认这一点。

这是什么意思?我们显然没有通过引用传递参数,因为函数没有改变绑定到x的值。我们也没有通过值传递参数,因为最初x_fx指向相同的内存位置。向 Python 函数传递参数遵循某种混合模式。我们可以说我们传递了一个引用,但是这个引用是通过值传递的,也就是所谓的通过赋值传递。一旦理解了 Python 中的变量绑定,这有点令人难以置信,但却是可以预见的。

改变可变对象的值

根据上一节,人们可能认为 Python 函数根本不能改变传递的参数。这是部分正确的。Python 函数不能完全改变参数,因为参数不能整体指向内存中的不同位置。如果函数内部的参数被绑定到一个新的对象,那么函数外部的参数就没有关系。然而,参数可以部分或全部改变,这是函数在 Python 中产生副作用的方式之一。让我们看一个例子:

那会打印

outside function x -> 2199273130560, [1, 2, 3]
inside function x_f -> 2199273130560, [1, 2, 3]
{'x_f': [1, 2, 3]}
inside function x_f -> 2199273130560, [1, 10, 3]
{'x_f': [1, 10, 3]}
outside function x -> 2199273130560, [1, 10, 3]

该函数有副作用,因为它改变了传递的参数数组的第二个元素。如果我们记住上面介绍的基本原则,这种行为是完全可以预测的。函数在函数外部定义的同一个数组上操作,正如我们从id()函数的输出中看到的,它总是打印相同的内存地址。

我们能改变数组的全部内容吗?下面的例子表明我们可以:

那会打印

outside function x -> 2199272856256, [1, 2, 3]
inside function x_f -> 2199272856256, [1, 2, 3]
{'x_f': [1, 2, 3]}
inside function x_f -> 2199272856256, [10, 20, 30, 40]
{'x_f': [10, 20, 30, 40]}
outside function x -> 2199272856256, [10, 20, 30, 40]

我们不仅改变了数组中所有元素的值,甚至还改变了它的长度。但是我们没有改变它的地址。当对象作为参数传递时,这是 Python 函数无法做到的。但是这可以通过第二种机制来实现,在第二种机制中,函数会引入副作用,我们将在下一节中看到。但在此之前,我想提一下,有一些方法可以在函数内部创建数组的本地副本,这样可以避免副作用,如下所示:

那会打印

outside function x -> 2199273194880, [1, 2, 3]
inside function x_f -> 2199273194880, [1, 2, 3]
{'x_f': [1, 2, 3]}
inside function x_f -> 2199272856256, [1, 10, 3]
{'x_f': [1, 10, 3]}
outside function x -> 2199273194880, [1, 2, 3]

我们可以看到,在函数中创建数组的副本防止了副作用。通过简单地将x的副本传递给函数,也就是用f(x[:])调用函数,也可以达到同样的效果。

但是在嵌套数组的情况下,事情仍然可能出错:

那会打印

outside function x -> 2199272857344, [1, [-1, -2, -3], 3]
inside function x_f -> 2199272857344, [1, [-1, -2, -3], 3]
inside function x_f -> 2199273194240, ['we guard against this side effect', [-1, 'we do not guard against this side effect', -3], 3]
outside function x -> 2199272857344, [1, [-1, 'we do not guard against this side effect', -3], 3]

上述行为可以用同样的基本原理来理解。在函数中复制数组意味着数组中不可变的元素在被改变时会被反弹。所有的元素都是不可变的,除了数组的第二个元素像以前一样绑定到同一个数组。我们可以用id()函数来验证这一点:

那会打印

outside function x
2199273306608, 1000
2199273249088, [-1000, -2000, -3000]
.. 2199273307216, -1000
.. 2199273307280, -2000
.. 2199273306448, -3000
2199273306384, 3000inside function x_f
2199273306608, 1000
2199273249088, [-1000, -2000, -3000]
.. 2199273307216, -1000
.. 2199273307280, -2000
.. 2199273306448, -3000
2199273306384, 3000inside function x_f
2199272920720, we guard against this side effect
2199273249088, [-1000, 'we do not guard against this side effect', -3000]
.. 2199273307216, -1000
.. 2199273257072, we do not guard against this side effect
.. 2199273306448, -3000
2199273306384, 3000outside function x
2199273306608, 1000
2199273249088, [-1000, 'we do not guard against this side effect', -3000]
.. 2199273307216, -1000
.. 2199273257072, we do not guard against this side effect
.. 2199273306448, -3000
2199273306384, 3000

使用copy模块的deepcopy()功能实际上是避免副作用的最安全的方法。

更改在本地命名空间之外定义的变量的绑定

使用变量时,Python 会在四个名称空间中进行搜索:局部、封闭、全局和内置。按照这个顺序搜索名称空间,直到找到名称,如果没有找到,Python 会抛出一个NameError异常。我们不会涉及名称空间的所有讨厌的细节(如果你好奇的话,请参阅本教程),而是把重点放在全局和局部名称空间上。让我们看一个例子:

印刷品

{'x': 'local'}
global

原因是我们在函数内部创建了一个新的 string 对象,并将一个名为x的局部变量绑定到它,正如locals()函数的输出所示。正如我们所预期的,全局变量x被绑定到字符串'global'。将以上内容与以下内容进行对比:

那会打印

{}
local

我们可以看到,函数外部的变量x现在被绑定到函数内部创建的 string 对象。这是一个副作用,也是应该避免的事情的主要例子。同样可以通过直接访问全局字典来实现。事实上,在调用函数之前,甚至没有必要绑定变量x:

那会打印

{}
local

这实际上是可怕的做法。没有看过函数内部的读者会感到困惑。与global类似,在嵌套函数中使用nonlocal也会产生副作用。

有副作用的函数会导致难以发现的错误。如果副作用是有意的,则需要在函数 docstring 中彻底记录。但即使这样也不理想,因为为有副作用的函数编写测试可能更难。通常,很少需要在局部范围之外修改变量,通常有更好的方法来达到相同的结果。每当传递可变参数时,都要仔细考虑函数可能会无意中对它们做什么,并采取预防措施来防止意外的副作用。

筛选与成像咖啡研磨分布

原文:https://towardsdatascience.com/sifting-vs-imaging-coffee-grind-distributions-f77328e258ac

咖啡数据科学

混合筛选和图像测量

最*,我决定想要一整套克鲁夫筛用于一些实验。我特别感兴趣的是我能从使用不同屏幕尺寸的断奏方法中学到什么。因为我有全套设备,所以我想我可以通过一堆筛子进行筛选,并使用图像处理与颗粒分布测量进行比较。

我开始用一个比*常更大的研磨尺寸来研磨浓缩咖啡,但是它应该会给不同的屏幕提供更宽的分布。

所有图片由作者提供

一旦我达到 500 微米,400 微米和 300 微米,我不得不开始使用搅拌器来加速筛选。对于 200 微米,我不得不使用刮刀推动地面通过。

我用一个冰块托盘来收集粉末。

这种设置也很容易可视化。

初步筛选的累积分布如下所示:

地面分布

然后我使用一种图像处理技术来测量每一层的研磨分布,包括未研磨的地面。名称中的 Z 表示使用放大的图像进行测量。每一个地面水*拍摄两张图像:广角和望远镜。wide 用于校准望远镜的每像素测量值。

使用[Upper() Lower()]托盘设置这种分配,以显示粉末落在两个箱子之间。

[900 800]和> 900um 有一个拖尾,但这两者都有少量样品和大量干扰物。

我使用这些分布和 sift 权重来做出更准确的分布,并将其与未 sift 的分布进行比较。我还覆盖了每个筛选屏幕的重量(用一条红色虚线来线性*似之间的点)。

这表明成像技术低估了高达 300um 左右的微粒数量。然而,当与使用相同技术获得的数据相比时,成像技术仍然表现良好。

我还研究了仓中点,也就是 50%的粒子上下的点。我把这些和筛盘的中间箱做了比较。[400 500]的中间仓是 450 微米。

这种重量调整的研磨分布可用于确定每个箱测量的重量调整参数,以更好地从 2D 图像颗粒测量得到实际重量,这需要考虑密度和体积。

每一杯咖啡可能都有调整,我怀疑事先使用密度测量会更准确。

这对我理解如何通过成像来估算体重是一个很有帮助的练习。目前,我只使用成像测量来比较地面,所以除了粒子密度之外,任何误差都应该是相似的。我可以预先测量整体密度,以改善烘烤过程中的测量。

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

我的进一步阅读:

我未来的书

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页面

改善浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

浓缩咖啡中的粉末迁移

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论和想法

咖啡实验

Sigmoid 和 SoftMax 功能在 5 分钟内完成

原文:https://towardsdatascience.com/sigmoid-and-softmax-functions-in-5-minutes-f516c80ea1f9

机器学习中两个最常用的激活函数背后的数学原理

Unsplash 上由Tomámalík拍摄。作者编辑

T he Sigmoid 和 SoftMax 函数定义了用于机器学习的激活函数,更具体地说是用于分类方法的深度学习领域。

激活函数 :对神经元的加权和进行变换,使输出为非线性的函数

注意。sigmoid 函数也被称为逻辑函数,因为它最初是通过逻辑回归算法引入的

这两个函数都从实数 R 的范围中取一个值χ,并输出一个介于 0 和 1 之间的数,该数代表χ属于某一类的概率

符号 : P(Y=k|X=x) 读作“给定输入 X 为 X,Y 为 k 的概率”。

图一。Sigmoid 和 SoftMax 函数的图示。输出读作“给定输入 X,Y 成为类 k 的概率”。作者图片

但是如果两个函数映射相同的转换(即做相同的事情),它们之间的区别是什么? Sigmoid 用于二元分类方法,其中我们只有 2 个类,而 SoftMax 适用于多类问题。实际上,SoftMax 函数是 Sigmoid 函数的扩展。
因此,两个函数的输入和输出略有不同,因为 Sigmoid 只接收一个输入,并且只输出一个表示属于 class1 的概率的数字(记住我们只有 2 个类,因此属于 class2 的概率= 1 - P(class1) )。而另一方面, SoftMax 是矢量化的,这意味着它采用一个与我们拥有的类具有相同条目数量的向量,并输出另一个向量,其中每个分量代表属于该类的概率。

图二。每个功能的输出图示。一个重要的性质是将每个 Sigmoid 类的所有概率相加,并且 SoftMax 应该等于 1。在 Sigmoid 的情况下,我们得到 P(Y=class2|X) = 1 - P(Y=class1|X)。作者图片

我们已经知道每个函数的作用以及在什么情况下使用它们。唯一剩下的就是数学公式(更多的数学符号!)

Sigmoid 和 SoftMax 函数的数学公式

Sigmoid 函数

假设我们的模型输出单个值 X ,它可以从实数 X ∈ (-∞,+∞)中取任何值,我们希望将该数字转换为概率 P ∈ [0,1],它表示属于第一类的概率(我们只有两个类)。

然而,要解决这个问题,我们必须以相反的方式思考。我怎么把一个概率 P ∈ [0,1]转换成一个值 X ∈ (-∞,+∞)?
虽然看起来不合逻辑,但解决之道在于赌马 ( 数学家向来喜欢游戏)。

在赛马投注中,有一个常用术语叫做 赔率【1】。当我们说 17 号马赢得比赛的几率是 3/8 时,我们实际上是说在 11 场比赛后,这匹马将赢得其中的 3 场,输掉 8 场。在数学上,赔率可以被视为两个独立事件之间的比率,并表示为:

赔率公式

赔率可以取任何正值,因此没有上限限制[0,+∞]。然而,如果我们取对数奇数,我们发现范围值变为(-∞,+∞)。赔率日志称为logit 函数:

Logit 函数公式。将概率从(0,1)映射到整个实数范围(-∞,+∞)

最后,我们寻找的函数,即逻辑函数或 SIGMOID 函数,是 logit 的逆函数(将范围(-∞,+∞)中的值映射到[0,1])

计算 logit 函数的反函数

从而得到公式:

Sigmoid 函数公式。

其中 X 表示输入(在神经网络的情况下,输入是最后一个神经元的加权和,通常由 z = x1 w1 + x2 w2 + … + xn wn 表示)

SoftMax 函数

另一方面,我们已经看到 SoftMax 接受一个向量作为输入。这个向量和我们的类有相同的维数。我们称它为 X(尽管神经网络中另一个常见的符号是 Z,其中向量的每个元素都是倒数第二层的输出)

k 类的输入向量

与 Sigmoid 函数相同,输入属于真实值(在这种情况下是每个向量条目)Xi∑(-∞,+∞)并且我们想要输出一个向量,其中每个分量是概率 P ∈ [0,1]。此外,输出向量必须是所有预测类别的概率分布,即向量的所有条目必须加起来为 1。这个限制可以解释为每个输入必须属于一个类,并且只能属于一个类。

我们可以认为 X 是包含每个类的 P(Y=i|X)的逻辑的向量,因为逻辑可以是任何实数(这里我表示类号)。还记得 logit ∈ (-∞,+∞)吗

给定 x,Y 属于第 I 类的概率的对数

然而,与二元分类问题不同,我们不能应用 Sigmoid 函数。原因是,当应用 Sigmoid 时,我们获得的是孤立的概率,而不是所有预测类别上的概率分布,因此输出向量元素的总和不是 1 [2]。

图 3 。为什么 sigmoid 函数不能用于多类分类?请注意,输出向量元素的总和不是 1。作者图片

要将 X 转换成概率分布,我们可以应用指数函数并获得几率∈ [0,+∞)

记住 X 是一个向量,因此 log(odds)和 odds 也是向量

在此之后,我们可以看到,奇数是一个关于概率的单调递增函数。因此,当概率增加时,赔率也以指数方式增加[2]。

图 4 。奇函数图。来自 Geogebra 的截图

因此,我们可以使用 odd(或其等价的 exp(logit) )作为分数来预测概率,因为 odd 越高,概率越高。

最后,我们可以通过除以所有赔率的总和来归一化结果,这样范围值从[0,+∞)变为[0,1],并且我们确保所有元素的总和等于 1,从而构建所有预测类的概率分布。

SoftMax 函数公式

现在,如果我们举一个和之前一样的例子,我们会看到输出向量确实是一个概率分布,它的所有条目加起来都是 1

图 5 。使用 SoftMax,我们可以获得所有预测类别的概率分布。注:为便于阅读,结果已精确到小数点后 3 位。作者图片

参考资料和资源

logit 函数 。Hartmann,k .,Krois,j .,Waske,B. (2018 年):SOGA 电子学习项目:统计和地理空间数据分析。柏林自由大学地球科学系。

****【2】Youtube。深度学习中的 logit 和 soft max。闵素赫。2019

高级计算机视觉手语识别

原文:https://towardsdatascience.com/sign-language-recognition-with-advanced-computer-vision-7b74f20f3442

使用 MediaPipe 和 Keras 实时检测手语字符

来自 Pexels 的手语“F”的图像

手语是一种主要由重听人或聋人使用的交流方式。这种基于手势的语言使人们能够轻松地传达想法和思想,克服听力问题带来的障碍。

这种便捷交流方式的一个主要问题是,全球绝大多数人缺乏这种语言的知识。正如任何其他语言一样,学习手语需要花费大量的时间和精力,不利于更多的人学习。

然而,在机器学习和图像检测领域,这个问题有一个显而易见的解决方案。实现预测模型技术来自动分类手语符号可以用于为虚拟会议(如缩放会议等)创建一种实时字幕形式。这将大大增加有听力障碍的人获得这些服务的机会,因为它将与语音字幕齐头并进,为有听力问题的人创造一个双向在线交流系统。

Kaggle 上有许多大型手语训练数据集,这是一个流行的数据科学资源。在这个模型中使用的一个被称为 " 手语 MNIST"并且是公共领域免费使用的数据集,具有 24 个美国手语字母中的每一个的大约 1000 个图像的像素信息,不包括 J 和 Z,因为它们是基于手势的符号。

“美国手语字母的各种用户和背景的裁剪图像蒙太奇”来自 MNIST 手语

为训练准备数据的第一步是将数据集中的所有像素数据转换并成形为图像,以便算法可以读取这些图像。

上面的代码从重塑所有 MNIST 训练图像文件开始,以便模型理解输入文件。与此同时,LabelBinarizer()变量获取数据集中的类并将它们转换为二进制,这一过程大大加快了模型的定型速度。

下一步是创建数据生成器来随机实现对数据的更改,增加训练示例的数量,并通过向不同的实例添加噪声和变换来使图像更加真实。

处理完图像后,必须编译 CNN 模型,以识别数据中使用的所有信息类别,即 24 组不同的图像。数据的规范化也必须添加到数据中,用较少的图像来*衡类。

请注意,算法初始化时添加了变量,如 Conv2D 模型,并浓缩为 24 个特性。我们还使用批处理技术让 CNN 更有效地处理数据。

最后,定义损失函数和度量以及将模型拟合到数据将创建我们的手语识别系统。由于构建模型所需的时间长度,在语句末尾识别出 model.save() 命令是很重要的。为每次使用重新训练模型可能需要几个小时的时间。

这段代码有很多东西需要解开。让我们分段来看。

第一行:

model.compile() 函数接受许多参数,其中三个显示在代码中。优化器和损失参数与下一行中的 epoch 语句一起工作,通过递增地改变数据的计算方法来有效地减少模型中的错误量。

除此之外,选择要优化的度量是精度函数,它确保模型在设定的历元数之后具有可实现的最大精度。

第 4 行:

此处运行的函数使设计的模型符合在第一位代码中开发的图像数据。它还定义了模型为提高图像检测的准确性而必须进行的 历元 或迭代次数。这里也称为验证集,向模型引入一个测试方面。该模型使用这些数据计算精确度。

第 5 行:

在代码位的所有语句中, model.save() 函数可能是这段代码中最重要的部分,因为它可能会在实现模型时节省几个小时的时间。

来自 Pexels 的手语“X”的图像

所开发的模型以大约 95%的训练准确度准确地检测和分类手语符号。

现在,使用两个流行的实时视频处理库 Mediapipe 和 Open-CV,我们可以获取网络摄像头输入,并在实时视频流上运行我们之前开发的模型。

展示 Pexels 手语的女性图像

首先,我们需要导入程序所需的包。

开始时运行的 OS 命令只是阻止来自 Mediapipe 使用的 Tensorflow 库的不必要的警告。这使得程序提供的未来输出更加清晰易懂。

在启动代码的主 while 循环之前,我们需要首先定义一些变量,比如 Open-CV 的保存模型和相机信息。

这里设置的每个变量都被分成四类。开头的类别与我们在本文第一部分中训练的模型直接相关。代码的第二和第三部分定义了运行和启动 Mediapipe 和 Open-CV 所需的变量。最后一类主要用于分析检测到的帧,并创建用于图像模型提供的数据的交叉引用的字典。

这个程序的下一部分是主 while True 循环,大部分程序都在这个循环中运行。

该程序的这一部分从您的相机获取输入,并使用我们导入的图像处理库将输入从设备显示到计算机。这部分代码着重于从你的相机中获取一般信息,并简单地在一个新窗口中显示出来。然而,使用 Mediapipe 库,我们可以检测手的主要标志,如手指和手掌,并在手周围创建一个边界框。

来自 Mediapipe 的手绘注释图像,作者

包围盒的概念是所有形式的图像分类和分析的重要组成部分。该框允许模型直接聚焦于功能所需的图像部分。否则,算法会在错误的位置找到模式,并可能导致不正确的结果。

例如,在训练过程中,缺少边界框会导致模型将诸如时钟或椅子等图像的特征与标签相关联。这可能导致程序注意到位于图像中的时钟,并且仅基于时钟存在的事实来决定正在显示什么手语字符。

上一个带有高亮显示的时钟的图像,作者

快好了!该程序的倒数第二部分是根据提示捕捉单个帧,将其裁剪到边界框的尺寸。

这段代码看起来与程序的最后一部分非常相似。这主要是因为涉及边界框的生产的过程在两个部分中是相同的。然而,在代码的这个分析部分,我们利用 Open-CV 的图像整形特性将图像调整到边界框的尺寸,而不是在它周围创建一个可视对象。除此之外,我们还使用 NumPy 和 Open-CV 来修改图像,使其具有与模型训练图像相同的特征。我们还使用 pandas 创建一个 dataframe,其中包含来自保存的图像的像素数据,因此我们可以像创建模型一样对数据进行规范化。

作者修改的手的图像

在代码的顶部,您可能会注意到正在定义的变量的奇数序列。这是由于相机库语法的性质。当 Open-CV 处理和更改图像时,更改是在所用帧的顶部进行的,本质上是保存对图像所做的更改。多个等值变量的定义使得函数中显示的框架与运行模型的画面分离。

最后,我们需要在经过处理的图像上运行经过训练的模型,并处理信息输出。

代码的这一部分包含大量信息。我们将逐一剖析这部分代码。

前两行绘制了手部图像属于 Keras 不同类别的预测概率。数据以两个张量的形式呈现,其中第一个张量包含概率信息。张量本质上是特征向量的集合,非常类似于数组。该模型产生的张量是一维的,允许它与线性代数库 NumPy 一起使用,以将信息解析为更具 pythonic 式的形式。

在这里,我们利用之前在变量 letterpred 下创建的类列表来创建一个字典,将张量中的值与键进行匹配。这允许我们将每个字符的概率与其对应的类别相匹配。

在这一步之后,我们使用列表理解从最高到最低对值进行排序。然后,这允许我们获取列表中的前几个项目,并将它们指定为与所示手语图像最接*的 3 个字符。

最后,我们使用一个 for 循环遍历字典中的所有 key:value 对,创建字典是为了将最大值与其对应的键匹配,并打印出每个字符概率的输出。

手语' A ',作者

如图所示,该模型准确地预测了从摄像机中显示的角色。除了预测字符,该程序还显示了 CNN Keras 模型的分类置信度。

开发的模型可以以多种方式实现,主要用途是为涉及视频通信的呼叫提供字幕设备,如 Facetime。要创建这样一个应用程序,模型必须一帧一帧地运行,随时预测显示的是什么符号。使用其他系统,我们也可以识别一个人什么时候没有显示任何手势,或者在手势之间转换,从而更准确地判断通过美国手语显示的单词。这种实现可以用来将显示的字母串在一起,最终识别单词甚至句子,从而创建一个全功能的手语文本翻译器。这种设备将极大地方便那些听力障碍者享受虚拟交流的好处。

该程序允许通过使用 Keras 图像分析模型,从手语到英语进行简单方便的交流。该项目的代码可以在我的 GitHub 个人资料中找到,链接如下:

mg 343/手语-检测(github.com)

使用深度学习的手语识别

原文:https://towardsdatascience.com/sign-language-to-text-using-deep-learning-7f9c8018c593

端到端管道,包括模型构建、超参数调整和部署

来源:ka ggle 上的手语 MNIST

看看你将要建造的模型

关于本文中使用的代码或模型的更多细节,请参考这个 GitHub Repo

好吧!!!现在,让我们开始构建卷积神经网络模型,将手语转换为英语字母表。

理解问题

问题定义

将手语转换为文本 可以分解为预测与手语中的表示相对应的英文字母的任务。

数据

我们将在 Kaggle 上使用手语 MNIST 数据集,该数据集根据 CC0:公共领域获得许可。

关于数据集的一些事实

  1. 没有字母 J & Z 的情况(原因:J & Z 需要运动)
  2. 灰度图像
  3. 范围从 0 到 255 的像素值
  4. 每个图像包含 784 个像素
  5. 标签用数字编码,从 A 到 Z 的范围是从 0 到 25
  6. 数据来自训练和测试集,每个组包含 784 个像素以及代表图像的标签

评估指标

我们将使用准确性作为评估标准。准确度是正确分类的样本与样本总数的比率。

造型

卷积神经网络(图片由 Sathwick 提供)

  1. 卷积神经网络(CNN)是图像分类问题的首选。
  2. 卷积神经网络是由卷积层组成的人工神经网络。
  3. CNN 很好地处理了图像数据。卷积层与密集层的主要区别在于,在前者中,每个神经元只连接到前一层中的一组特定神经元。
  4. 每个卷积层包含一组过滤器/内核/特征映射,帮助识别图像中的不同模式。
  5. 随着图像通过更深的层,卷积神经网络可以发现更复杂的模式。
  6. 与典型的密集分层人工神经网络不同,使用 CNN 的另一个优势是,一旦它在一个位置学习到一个模式,它就可以识别任何其他位置的模式。

初始端到端工作流

导入所需的模块和包

必要的进口

准备数据

使用 pandas 读取 CSV 文件( sign_mnist_train.csv )并混洗整个训练数据。

分离图像像素和标签允许我们应用特定于特征的预处理技术。

准备数据

标准化和批处理

对输入数据进行归一化非常重要,尤其是在使用梯度下降法时,当数据进行归一化时,梯度下降法更有可能更快地收敛。
将训练数据分组为批次减少了训练模型所需的时间。

标准化和批处理

来自应用预处理后的训练数据的图像

预处理图像(图像由 Sathwick 提供)

二值化标签

标签二值化器(图像由 Sathwick 提供)

Scikit-Learn 库中的LabelBinarizer以一对一的方式对标签进行二进制化,并返回独热编码向量。

二值化标签

分离验证数据

验证数据将有助于选择最佳模型。如果我们在这里使用测试数据,我们将选择在测试数据上过于乐观的模型。

数据分离

建立模型

CNN 模型可以定义如下

构建 CNN 时通常的选择是

  1. 选择一组卷积运算层
  2. 增加较深卷积层中的神经元数量
  3. 在卷积池层之后添加一组密集层

美国有线新闻网;卷积神经网络

模型的架构(图片由 Sathwick 提供)

现在是模型建造的关键选择

  1. loss —指定我们试图最小化的损失函数。由于我们的标签是一个热编码的,我们可以选择 分类交叉熵 作为损失函数。
  2. optimizer —该算法寻找最小化损失函数的最佳权重。亚当就是这样一种算法,它在大多数情况下都运行良好。

在构建模型时,我们可以指定评估模型的任何指标,我们选择这些指标作为准确性。

定义损失、优化器和指标

检查站

ModelCheckpoint —通过访问验证数据上的模型性能,保存每个时期训练期间找到的最佳模型。

EarlyStopping —在指定的次数内没有进展时中断训练。

检查站

寻找模式

拟合数据

回顾模型培训

历史对象包含在模型训练期间获得的损失和指定的度量细节。该信息可用于获得学习曲线,并访问训练过程和模型在每个时期的性能。

保存历史

有关培训期间获得的学习曲线的更多详情,请参考这本 jupyter 笔记本。

最佳模特

检索在训练期间获得的最佳模型,因为在训练结束时接收的模型不必是最佳模型。

最佳模特

在测试台上的表现

测试集上的性能

准确率:94%

超参数调谐

照片由丹尼斯·莱昂Unsplash 上拍摄

你可以在下面的超参数调 这里的 找到代码和生成的模型。

当谈到超参数调谐,有太多的选择,我们可以在给定的 CNN 调谐。需要调整的一些最基本和最常见的超参数包括

卷积和最大池对的数量

这代表了我们堆叠在一起的Conv2DMaxPooling2D对的数量,构建了 CNN。

随着我们堆叠更多对,网络变得越来越深,增加了模型识别复杂图像模式的能力。

但是堆叠太多的层会对模型的性能产生负面影响(随着其深入 CNN,输入图像尺寸迅速减小),并且随着模型的可训练参数的数量急剧增加,还会增加训练时间。

过滤

过滤器决定输出要素地图的数量。一个过滤器作为一个模式,将能够找到图像回旋时的相似性。在大多数情况下,增加连续层中的过滤器数量效果很好。

过滤器尺寸

这是一个约定,把过滤器的大小作为一个奇数,给我们一个中心位置。偶数尺寸滤波器的一个主要问题是它们需要不对称填充。

我们可以使用多个具有较小滤波器大小的卷积层,而不是使用由(7x7,9x9)等较大滤波器组成的单个卷积层,这更有可能提高模型的性能,因为更深的网络可以检测复杂的模式。

拒绝传统社会的人

Dropout 充当正则化器,防止模型过度拟合。脱落层使一些神经元对下一层的贡献无效,而使其他神经元保持不变。辍学率决定了特定神经元的贡献被取消的概率。

在初始时期,我们可能会遇到训练损失大于验证损失的情况,因为一些神经元可能会在训练期间被丢弃,但是在验证中使用了具有所有神经元的完整网络。

数据扩充

数据增强(图片由 Sathwick 提供)

通过数据扩充,我们可以生成稍微修改过的可用图像副本,并将其用于训练模型。这些不同方向的图像有助于模型识别不同方向的对象。

例如,我们可以对图像进行小的旋转、缩放和*移。

数据扩充

要尝试的其他超参数

  1. 批量归一化-对图层输入进行归一化
  2. 更深的网络工作得很好——用两个连续的滤波器大小(3X3)的卷积层替换滤波器大小(5X5)的单个卷积层
  3. 密集层中的单元数和密集层数
  4. 用跨距> 1 的卷积层替换 MaxPooling 层
  5. 优化者
  6. 优化器的学习速率

最终模型的评估

超参数调整后的最佳模型(图片由 Sathwick 提供)

模型评估

准确率:96%

部署模型以简化流程

照片由 Praveen kumar Mathivanan 在 Unsplash 上拍摄

Streamlit 是一个出色的*台,消除了手动部署所需的所有麻烦。
有了 Streamlit,我们所需要的只是一个 GitHub 存储库,其中包含一个指定应用流程的 python 脚本。

设置

使用以下命令安装 Streamlit 库

pip install streamlit

要运行 Streamlit 应用程序,请使用streamlit run <script_name>.py

构建应用程序

您可以在此处找到完整的 Streamlit 应用流程

需要在超参数调整和LabelBinarizer之后获得最佳模型,以将模型的输出转换回相应的标签。

简化缓存

decorator 只运行该函数一次,防止在重新显示页面时不必要的返工。

模型预测

我们应该将上传的图像整形为 28x28,因为这是我们模型的输入形状。我们还必须保留上传图像的纵横比。

然后,我们可以使用预处理的图像作为模型输入,并获得相应的预测,该预测可以使用label_binarizer转换回代表英文字母的标签

预处理和预测

将应用部署到 Streamlit 云

  1. 在这里注册一个 Streamlit 账户
  2. 通过授予访问您的存储库的所有必要权限,将您的 GitHub 帐户与您的 Streamlit 帐户连接起来。
  3. 确保存储库包含指定所有应用程序依赖项的 requirements.txt 文件。
  4. 点击可用的新应用按钮这里
  5. 给出存储库、分支名称和 python 脚本名称,其中包含了我们应用程序的流程。
  6. 单击部署按钮。

现在,您的应用程序将被部署到 web 上,并且将在您更新存储库时得到更新。

摘要

在本教程中,我们了解了以下内容:

  1. 使用深度学习为问题开发解决方案的管道
  2. 预处理图像数据
  3. 训练卷积神经网络
  4. 模型评估
  5. 超参数调谐
  6. 部署

请注意,本教程仅简要介绍了使用深度学习技术开发解决方案的完整端到端管道。这些管道包括大量的培训,搜索更广泛的超参数和特定于用例的评估指标。需要对模型、数据和更广泛的图像预处理技术有更深入的理解,以建立解决复杂问题的管道。

看一下型号

有关本文中使用的代码或模型的更多细节,请参考此 GitHub Repo

感谢阅读!

我希望这篇教程对你构建下一个奇妙的机器学习项目有所帮助。如果你发现文章中的任何细节不正确,请在评论区告诉我。我很乐意听取您对存储库的建议和改进。

信号处理,超越傅里叶变换:线性调频小波变换介绍,使用 Python

原文:https://towardsdatascience.com/signal-processing-beyond-the-fourier-transform-introduction-to-the-chirplet-transform-using-146a00478318

从理论介绍到实际操作:以下是您需要了解的关于 Chirplet 变换的内容

加里·穆勒曼斯在 Unsplash 上的照片

信号处理最常见的操作之一就是对信号进行变换。我们这样做的原因是,对信号执行操作的最简单方法并不总是通过观察信号并在其自然域内进行分析*。

*我将把自然域称为信号的原始空间。例如,如果信号是时间序列,自然域是 2D 域,其中 x 是时间,y 是信号值

我给你举个例子。考虑以下自然域(时间序列)中的信号:

作者图片

在探索信号的过程中,一件有趣的事情是观察信号是否可以分解成分量。之所以有趣,是因为每种成分都有特定的来源。换句话说,我们正在以某种方式对信号进行逆向工程。

为了将上面显示的信号分成多个分量,我们必须改变信号的。这意味着我们必须在频率中查看信号,而不是在时域中查看(更多信息请参见[这里的](http://fourier space vs real space))。

我们在这里建议的是,信号是正弦分量的总和。每个分量都有特定的振幅、相位和频率。为了进行分析,我们使用了傅立叶变换。在我们正在做的例子中,它看起来像这样:

作者图片

现在如果你看上面的图像(所谓的光谱),你会看到它有 4 个峰值:

作者图片

让我们将这 4 个 x 轴值及其对应的 y 值存储在两个列表中:

频率值,按作者分类的图像

振幅值,图片由作者提供

现在,原始信号可以分解成 n_c=4 个分量。每个分量都是一个具有给定振幅和频率的正弦*。我们把每一个组件都叫做\phi,我们把时间数组叫做 t。这是结果

*它也可以是余弦,我认为正弦只是为了让这个例子更容易理解。

所以如果我们画出这些我们会得到:

作者图片

很棒吧?

傅立叶变换(或者更确切地说,它的实际实现也称为快速傅立叶变换,非常有效)在理论上是一种有趣的算法,在信号处理中也是一种非常有用的算法。我们在图像处理、音频处理、时间序列分析和许多其他领域使用它。尽管如此,在这个算法做出的所有假设中,有一个非常大的假设:

傅立叶变换认为频率相对于时间是固定的。

这并不总是正确的。

例如,看一看(或者实际上听一听)下面的声音:【https://www.youtube.com/watch?v=5TGCFGsQIx0

你在开始时听一个低音符(低音),它开始变得越来越高,以至于几乎令人讨厌。在这种情况下,正弦波看起来像这样:

作者图片

在这一点上,如果你试图应用这个信号的傅立叶变换,它将没有任何意义,因为你依赖于一个不真实的假设:频率确实随时间变化! 随时间变化的那种正弦波通常称为啁啾。获得由线性调频脉冲构成的信号分解的方法称为线性调频脉冲变换。

我们开始吧!🙂

1.线性调频小波变换的方程

线性调频小波变换的公式如下:

作者图片

让我描述一下你在上面的等式中看到的所有值:

1.1 测试版

β值是线性调频脉冲的振幅。这是唯一一个不是指数函数自变量的值:

作者图片

作者图片

如果你看上面的图像,固定所有的值,β只改变振幅。

1.2 阿尔法 1

alpha_1 位是唯一一个非正弦位。如果你看上面的表达式,它只不过是一个高斯项。

作者图片

特别地,alpha_1 项是高斯的方差。这意味着 alpha_1 越小,你的铃铛的形状就越宽。该术语也被称为调制术语

1.3 华氏度

f_c 是正弦项。记住!我们只选取了实数部分,所以它只是线性调频脉冲的正弦部分。

作者图片

作者图片

f_c 值越大,在相同的时间内(在这种情况下,500 个数据点)看到的起伏就越多

1.4 阿尔法 _2

α_ 2 是纯粹的啁啾部分。我的意思是,这个组件随着时间上下波动,它具有我们之前在 YouTube 视频中绘制和看到的行为。

作者图片

作者图片

你可以看到,起伏的次数增加了(类似于 f_c 中发生的情况),但这里的次数不是固定的,而是随时间增加的。

1.5 Phi

phi 参数非常容易理解。它只不过是一个相移。正如我们所看到的,它是唯一一个在指数变量中的参数,但是没有时间依赖性。这只会改变正弦波的相位。例如,如果我们考虑这个相移正弦部分(与α1 相关的部分),我们得到:

作者图片

作者图片

1.6 吨

tau 参数可能是最简单的一个。所有的线性调频小波函数都是某种指数函数(虚的或非虚的),并且以时间值为中心。那个时间值是τ。

作者图片

作者图片

2.动手操作数字实施

在这几行代码中,您可以:

A.生成一个线性调频脉冲
B .使用上述参数并注意差异
C .绘制结果

2.1 分析功能定义:

2.2 生成默认线性调频脉冲:

2.3 生成随机线性调频脉冲:

2.4 绘制随机线调频脉冲:

3.结论

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”了,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。

使用这些参数显著增加您的网格搜索结果

原文:https://towardsdatascience.com/significantly-increase-your-grid-search-results-with-these-parameters-b096b3d158aa

使用估计器开关对任何机器学习流水线步骤进行网格搜索

照片由埃克托尔·j·里瓦斯Unsplash 上拍摄

构建机器学习模型的一个非常常见的步骤是使用交叉验证在训练集上对分类器的参数进行网格搜索,以找到最佳参数。鲜为人知的是,您还可以对几乎任何管道步骤进行网格搜索,例如特征工程步骤。例如,哪种插补策略对数值最有效?均值、中值还是任意?使用哪种分类编码方法?一键编码,还是顺序编码?

在本文中,我将指导您使用网格搜索在您自己的机器学习项目中回答这些问题。

要安装本文所需的所有 Python 包:

pip install extra-datascience-tools feature-engine

数据集

让我们考虑下面这个我创建的非常简单的公共领域数据集,它有两列:last_gradepassed_course。“最后成绩”列包含学生在最后一次考试中取得的成绩,而“通过课程”列是一个布尔列,如果学生通过了课程,则为True,如果学生未通过课程,则为False。我们能否建立一个模型,根据学生的最后成绩来预测他们是否通过了课程?

让我们首先探索一下数据集:

import pandas as pd

df = pd.read_csv('last_grades.csv')
df.isna().sum()
OUTPUT
last_grade       125
course_passed      0
dtype: int64

我们的目标变量course_passed没有nan值,所以不需要在这里删除行。

当然,为了防止任何数据泄漏,我们应该在继续之前先将数据集分成训练集和测试集。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
                                    df[['last_grade']],
                                    df['course_passed'],
                                    random_state=42)

因为大多数机器学习模型不允许nan值,所以我们必须考虑不同的插补策略。当然,一般来说,你会启动 EDA(探索性数据分析)来确定nan值是 MAR (随机缺失) MCAR (完全随机缺失)还是 MNAR (非随机缺失)。这里有一篇很好的文章解释了这两者之间的区别:

我们不去分析为什么有些学生的最后一次成绩丢失了,而是简单地尝试在不同的插补技术上进行网格搜索,以说明如何在任何流水线步骤上进行网格搜索,例如这个特征工程步骤。

让我们来探究自变量last_grade的分布:

import seaborn as sns

sns.histplot(data=X_train, x='last_grade')

最后一级的分布(按作者分类的图片)

看起来最后的成绩正态分布,*均值为~ 6.5 ,值在~ 3 和~ 9.5 之间。

让我们看看目标变量的分布,以确定使用哪个评分标准:

y_train.value_counts()
OUTPUT
True     431
False    412
Name: course_passed, dtype: int64

目标变量大致*分,这意味着我们可以使用 scikit-learn 的默认计分器进行分类任务,也就是准确度分数。如果目标变量划分不均,准确度分数不准确,则使用 F1 代替。

网格搜索

接下来,我们将建立模型和网格搜索,并通过优化分类器的参数来运行它,这是我看到的大多数数据科学家使用网格搜索的方式。我们现在将使用 特征引擎 [MeanMedianImputer](https://feature-engine.readthedocs.io/en/latest/api_doc/imputation/MeanMedianImputer.html#feature_engine.imputation.MeanMedianImputer)来估算*均值,使用 scikit-learn[DecisionTreeClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)来预测目标变量。

from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV

from feature_engine.imputation import MeanMedianImputer

model = Pipeline(
  [
    ("meanmedianimputer", MeanMedianImputer(imputation_method="mean")),
    ("tree", DecisionTreeClassifier())
  ]
)

param_grid = [
  {"tree__max_depth": [None, 2, 5]}
]

gridsearch = GridSearchCV(model, param_grid=param_grid)
gridsearch.fit(X_train, y_train)

pd.DataFrame(gridsearch.cv_results_).loc[:,
                                      ['rank_test_score', 
                                       'mean_test_score', 
                                       'param_tree__max_depth']
                                      ].sort_values('rank_test_score')

以上代码的结果(图片由作者提供)

从上表中我们可以看到,使用GridsearchCV我们了解到,只需将DecisionTreeClassifiermax_depth从默认值更改为 5,就可以将模型的精度提高约 0.55。这清楚地说明了网格搜索的积极影响。

然而,我们不知道用*均值来估算缺失的last_grades是否实际上是最佳估算策略。我们能做的实际上是使用extra-data science-tools[EstimatorSwitch](https://extra-datascience-tools.readthedocs.io/en/latest/notebooks/tutorial.html#EstimatorSwitch)对三种不同的插补策略进行网格搜索:

  • *均插补
  • 中位数插补
  • 任意数字插补(默认为 999特征引擎[ArbitraryNumberImputer](https://feature-engine.readthedocs.io/en/latest/api_doc/imputation/ArbitraryNumberImputer.html))。
from feature_engine.imputation import (
                                  ArbitraryNumberImputer,
                                  MeanMedianImputer,
                               )
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier
from extra_ds_tools.ml.sklearn.meta_estimators import EstimatorSwitch

# create a pipeline with two imputation techniques
model = Pipeline(
  [
    ("meanmedianimputer", EstimatorSwitch(
                            MeanMedianImputer()
                          )),
    ("arbitraryimputer", EstimatorSwitch(
                            ArbitraryNumberImputer()
                          )),
    ("tree", DecisionTreeClassifier())
  ]
)

# specify the parameter grid for the classifier
classifier_param_grid = [{"tree__max_depth": [None, 2, 5]}]

# specify the parameter grid for feature engineering
feature_param_grid = [
    {"meanmedianimputer__apply": [True],
     "meanmedianimputer__estimator__imputation_method": ["mean", "median"],
     "arbitraryimputer__apply": [False],
    },
     {"meanmedianimputer__apply": [False],
     "arbitraryimputer__apply": [True],
    },

]

# join the parameter grids together
model_param_grid = [
    {
        **classifier_params,
        **feature_params
    }
    for feature_params in feature_param_grid
    for classifier_params in classifier_param_grid
]

这里需要注意一些重要的事情:

  • 我们在管道中的 extra-datascience-tools 的 EstimatorSwitch中包含了两个估算器,因为我们不想同时使用两个估算器。这是因为在第一个估算器转换了 X 后,将没有nan值留给第二个估算器进行转换。
  • 我们在分类器参数网格和特征工程参数网格之间分割参数网格。在代码的底部,我们将这两个网格连接在一起,以便每个特征工程网格都与每个分类器网格相结合,因为我们想为ArbitraryNumberImputerMeanMedianImputer尝试一个 None25max_tree_depth
  • 我们在特征参数网格中使用字典列表而不是字典,这样可以防止同时应用MeanMedianImputerArbitraryNumberImputer。使用EstimatorSwitchapply参数,我们可以简单地打开或关闭两个估算器中的一个。当然,你也可以运行代码两次,第一次注释掉第一个估算器,第二次注释掉第二个估算器。但是,这将导致我们的参数网格中出现错误,因此我们也需要调整该网格,并且不同插补策略的结果在相同的网格搜索 cv 结果中不可用,这使得比较更加困难。

让我们看看新的结果:

gridsearch = GridSearchCV(model, param_grid=model_param_grid)
gridsearch.fit(X_train, y_train)

pd.DataFrame(gridsearch.cv_results_).loc[:,
                                         ['rank_test_score', 
                                          'mean_test_score', 
                                          'param_tree__max_depth',
                     'param_meanmedianimputer__estimator__imputation_method']
                                           ].sort_values('rank_test_score')

特征工程的网格搜索结果(图片由作者提供)

我们现在看到了一个新的最佳模型,这是一个决策树,它的max_depth2 ,使用了ArbitraryNumberImputer。我们通过实施不同的插补策略将精确度提高了 1.4%!作为一个受欢迎的奖励,我们的树深度已经缩减为 2,这使得模型更容易解释。

当然,网格搜索可能已经花费了相当多的时间,并且不仅在分类器上网格搜索,而且在其他流水线步骤上网格搜索也可能花费更长的时间。有几种方法可以将额外花费的时间减到最少:

  • 首先对分类器的参数进行网格搜索,然后根据情况对其他步骤(如特征工程步骤)进行网格搜索,反之亦然。
  • 使用extra-data science-tools'[filter_tried_params](https://extra-datascience-tools.readthedocs.io/en/latest/notebooks/tutorial.html#filter_tried_params)防止网格搜索的重复参数设置。
  • 使用 scikit-learn[HalvingGridSearch](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.HalvingGridSearchCV.html)[HalvingRandomSearch](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.HalvingRandomSearchCV.html#sklearn.model_selection.HalvingRandomSearchCV)代替GridSearchCV(仍在实验阶段)。

最后

除了使用网格搜索来优化决策树等分类器之外,我们还看到,您实际上可以使用extra-data science-tools'EstimatorSwitch通过对插补策略进行网格搜索来优化机器学习管道中的任何步骤。除了插补策略和分类器本身之外,还有一些值得网格搜索的流水线步骤的例子:

感谢阅读!有任何问题,请随时联系我。我经常写关于机器学习、数据科学和 Python 的文章,所以如果你感兴趣,可以考虑在 Medium 和/或 LinkedIn 上关注我。

如何确定气流中接收到的信号

原文:https://towardsdatascience.com/sigterm-signal-fix-airflow-486ab704b126

修复阿帕奇气流任务中的信号术语

杰里米·珀金斯在 Unsplash 上拍摄的照片

介绍

虽然我最*一直在致力于将 Dag 从气流 1 ( v1.10.15)迁移到气流 2 ( v2.2.5),但我花了很多时间试图找出我在一些 Dag 中遇到的一个错误,这个错误根本不能提供信息。

WARNING airflow.exceptions.AirflowException: Task received SIGTERM signal
INFO - Marking task as FAILED.

尽管我花了一些时间尝试我在网上找到的可能的解决方案,但似乎没有一个对我有效。

在今天的文章中,我将介绍一些针对发送到任务的SIGTERM signal的潜在解决方案,它会导致气流 Dag 失败。根据您的配置和具体使用情况,可能会有不同的解决方案适合您,因此请确保仔细阅读并尝试每个建议的解决方案。

DAG 运行超时

您的任务接收到一个SIGTERM信号的原因之一是由于一个短的dagrun_timeout值。DAG 类接受此参数,该参数用于指定 DagRun 在超时/失败之前应该运行多长时间,以便可以创建新的 DAG run。请注意,超时仅针对计划的 DagRuns。

对于包含许多长时间运行任务的 DAG,有可能超过dagrun_timeout,因此活动运行的任务将接收到一个SIGTERM信号,以便 DAG 可以失败,并执行新的 DagRun。

您可以在 Airflow UI 上检查 DagRun 的持续时间,如果您观察到该持续时间大于创建 DAG 实例时指定的dagrun_timeout值,那么您可以根据您的特定用例将其增加到一个合理的时间量。

请注意,此配置适用于 DAG,因此您需要提供一个值,以便有足够的时间来运行 DAG 中包含的所有任务。

from datetime import datetime, timedeltafrom airflow.models.dag import DAG with DAG(
    'my_dag', 
    start_date=datetime(2016, 1, 1),
    schedule_interval='0 * * * *',
    dagrun_timeout=timedelta(minutes=60),
) as dag:
    ...

内存不足

另一种可能是,当前正在运行气流任务的机器内存不足。根据您部署 Airflow 的方式,您可能需要检查工作线程的内存使用情况,并确保它们有足够的内存。

例如,如果您的部署在云上,您可能必须检查是否有任何 Kubernetes pods 被驱逐。吊舱通常由于资源匮乏的节点而被驱逐,因此这可能是你的气流任务接收到SIGTERM信号的原因。

元数据数据库耗尽了 CPU

另一个常见的可能导致 Airflow 任务接收到SIGTERM信号的问题是元数据数据库上的 CPU 使用率。

默认情况下,Airflow 使用 SQLite,它仅用于开发目的,但它旨在支持 PostgreSQL、MySQL 或 MSSQL 的数据库后端。

数据库上的 CPU 使用率有可能达到 100%,这可能是您的气流任务接收到SIGTERM信号的原因。如果是这种情况,那么您应该考虑增加默认设置为 5 秒的job_heartbeat_sec配置(或AIRFLOW__SCHEDULER__JOB_HEARTBEAT_SEC环境变量)的值。

job_heartbeat_sec

任务实例监听外部终止信号(当您从 CLI 或 UI 清除任务时),这定义了它们应该监听的频率(以秒为单位)。

在气流配置文件airflow.cfg中,确保在scheduler部分指定该配置,如下图所示。

[scheduler]
job_heartbeat_sec = 20

或者,您可以通过相应的环境变量修改此配置的值:

export AIRFLOW__SCHEDULER__JOB_HEARTBEAT_SEC=20

如果数据库级别的 CPU 消耗是一个问题,那么增加上述配置现在应该可以显著减少 CPU 的使用。

禁用“迷你计划程序”

默认情况下,任务管理器进程会尝试调度同一气流 DAG 的更多任务,以提高性能并最终帮助 DAG 在更短的时间内执行。

该行为通过默认为Trueschedule_after_task_execution进行配置。

schedule_after_task_execution

任务管理器进程是否应该执行“迷你调度器”来尝试调度同一 DAG 的更多任务。启用此选项将意味着同一 DAG 中的任务执行得更快,但在某些情况下可能会耗尽其他 DAG。

由于气流中的一个 bug ,任务被LocalTaskJob心跳杀死的几率相当高。因此,一个可能的解决方案是简单地禁用迷你调度程序。

在您的气流配置文件airflow.cfg中,您需要将*schedule_after_task_execution* 设置为 False。

[scheduler]
schedule_after_task_execution = False

或者,可以通过AIRFLOW__SCHEDULER__SCHEDULE_AFTER_TASK_EXECUTION环境变量覆盖该配置:

export AIRFLOW__SCHEDULER__SCHEDULE_AFTER_TASK_EXECUTION=False

如果这是你的问题所在,那么你可能也想考虑将 Airflow 升级到一个修复了这个 bug 的版本。

最后的想法

在今天的教程中,我们讨论了SIGTERM信号的含义,该信号有时会发送到气流任务,导致 Dag 失败。我们讨论了发生这种情况的几个潜在原因,并展示了如何根据您的具体用例来克服这个问题。

请注意,您的配置也有可能遇到本教程中讨论的不止一个问题,因此您可能需要应用我们今天讨论的解决方案组合来消除SIGTERM信号。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

是什么让我们和 AI 不一样?

原文:https://towardsdatascience.com/similarities-differences-between-humans-and-artificial-intelligence-6d2fb3692584

观点:进化中的人工智能

新思想的起源是什么?

新思想的起源是什么?(图片来自稳定扩散)

人脑是目前世界上最好的神经网络。你有没有一觉醒来,对自己身在何处感到困惑,却发现自己不是在自己的家里,而是在你前一天晚上住过的别人家里?这种失去方向感的原因是什么?

这是因为多年来,你的大脑已经了解了当你醒来时,你的房间和世界通常是什么样子。它试图根据这些学习到的信息来预测你应该看到什么,然后当它意识到自己的错误时,逐渐适应新的现实。这正是人工神经网络(ANN)的工作方式。它被输入一些训练数据和该数据的期望输出,并且通过反复试验,当它学会紧密匹配训练数据的期望输出时,它在预测输出方面逐渐变得更加准确,之后它可以以相当高的准确度对以前未见过的数据进行预测。

在过去的一个月里,这个世界,尤其是花了大半辈子时间试图理解 LED 屏幕上由顺序奇怪的英语单词组成的句子的那部分人(也就是程序员),已经为 OpenAI 新推出的基于人工神经网络的聊天机器人 ChatGPT 而疯狂。人们担心它会夺走他们的工作,YouTube 上充斥着大量视频,讲述它如何只用一个提示就能为复杂的全栈应用程序(如待办事项列表)编写代码。不仅仅是编程,ChatGPT 还能写诗:

随着数字键的每一次敲击,

文字像河流一样流淌,永无止境。

没有写作障碍,不需要睡眠,

ChatGPT 是有效的,它确实保存了数据。

随着人工智能的不断进步,它很可能会越来越多地融入广泛的领域和行业,例如医学,在医学中,它已经被发现能够比医生更准确地诊断;战争,在战争中,它被用于开发自主武器系统;音乐,在音乐中,它能够根据指定的风格或参数生成原创作品;甚至艺术(本文中的所有图像都是使用名为“稳定扩散”的人工智能生成的)。

那么,人工智能会让人类过时吗?这个问题的答案在于另一个问题:我们的大脑能做什么人工神经网络永远做不到的事情?这样的能力将是我们的未来,休息将是自动化的。

创造性思维的能力

我想到的第一个答案是:“人类可以创造性地思考,而人工神经网络不能”。

事实上,ChatGPT 不就是一个模型吗?它可以对互联网上的小知识子集进行无限排列和组合(有点像谷歌的更好版本)?人工神经网络只能结合(或受其影响)已有的知识和想法(他们已经接受过培训)来产生新的想法。这将意味着未来的人类只需要成为有创造力的思想家,他们的想法将由他们的人工智能“奴隶”来实现。我已经坦然接受了。

但是有一天我读了奥斯汀·克里昂的书:《像艺术家一样偷窃》。这本书讨论了有创造力的个人如何通过借鉴和结合先前存在的想法来产生想法。作者用了大量的例子来说明这一概念,认为所有的创新思想都只是多个旧思想的变体,而新思想是由它们的前辈形成的。

我开始研究人类从哪里获得新的想法。在阅读数十篇关于科学发现的文章时,我注意到了历史上的一种模式,即许多人同时独立做出如此多的里程碑式的发现。这些包括电话、蒸汽机、氧气、微积分、灯泡和大爆炸理论。关于这个话题的有趣阅读:独立发现有多普遍?——作者马特·克兰西。以下是它的摘录:

一旦一项新的发明或发现“接*”现有的知识,那么多人很可能同时产生这种想法。这也意味着科学和技术的进步有一些内在的冗余:如果爱因斯坦在童年时就死了,其他人就会提出相对论。

在我看来,这表明人类的大脑就像人工神经网络一样,将现有的想法结合起来(或者受其影响很大)以形成新的想法。事物不能从无到有。创造新想法的能力不是人类独有的,也不是人工智能永远学不会的。

自我意识

我接下来的回答是:“人类是有意识、有自我意识的生物”。

什么是自我意识?它是一种甚至无法证明存在的东西。我能找到的这个问题的最佳答案是:思考(内省)的能力。但是我想得越多,我意识到 AI 也可以被训练思考它的思维过程。它可以被训练向外投射意识,以第三人称的方式看到自己。

人类的自我意识是一个神话吗(我们是在欺骗自己说我们是有知觉的吗)?为什么我在过去的两个小时里一直盯着我的电脑,疯狂地敲打着键盘?也许我们只是预编程的“进化”,不断学习的神经网络,旨在相互分享他们的想法,以便产生更新的想法,关于一切,包括我们自己的思维过程。

如果没有感知这种东西,那么自由意志也不存在,一切都是确定的,就像人工神经网络的输出一样。

我不完全确定有没有生物特有的自我意识。它可能只是我们大脑中的电信号,接收输入(例如:这篇文章)和产生输出(这些想法和你在别处读到的一些想法的混合,每个人都是独特的)。这个神经网络在不断学习(这意味着阅读这篇文章可能会微小地改变称为大脑的网络隐藏层内的参数,因此这将从此以小的方式影响你在生活中所做的任何事情,而这可以以大的方式影响世界(蝴蝶效应))。当前的人工神经网络技术还没有实现持续学习的能力,这意味着人脑的能力仍然远远超出人工神经网络的能力。

关于人工智能意识的有趣阅读。

询问人工智能

自己找不到满意的答案,就问 ChatGPT,人工智能永远做不到的事,人类能做什么?

它列出了这 5 件事:

  1. 创造力:人类有能力创造性地思考,提出新的和创新的想法,而不仅仅是现有想法和经验的简单组合。
  2. :人类有理解和表达情绪的能力,也有识别和回应他人情绪的能力。这让我们能够建立有意义的关系,并与他人有效沟通。
  3. 伦理决策:人类有能力在做决策时考虑道德和伦理影响,这是人工智能系统所不具备的。
  4. ****物理能力:人类有能力以人工智能系统无法做到的方式移动并与物理世界互动。这使我们能够执行需要身体灵活性或操纵物体的任务。
  5. ****上下文理解:人类有能力理解单词和短语的上下文和含义,以及理解对话或消息的潜在意图。

在我看来,ChatGPT 低估了自己(和其他 AI)。

我已经在上面讨论了第一点。#4 和#5 很容易被忽略,人工智能现在可能不如人类有能力完成这些任务,但经过足够的训练,它可以做到(就像它现在不如人类有能力提出新的思想和想法,但在未来,通过以正确的方式构建和训练,它可以做到)。

现在我们来看第二点:情绪。无论我使用什么样的提示,我都无法从 ChatGPT 那里得到情感上的回应:

问:你既可悲又愚蠢,永远比不上人类。

A.很抱歉让你有这种感觉。作为一个人工智能,我没有感觉或情绪,我的主要功能是根据…

但是 ChatGPT 对情感有什么用呢?例如,它会因为感到愤怒或嫉妒而做什么?据我所知,它的目标是成为一个人工智能助手和知识库。

我们来分析一种情绪,比如“贪婪”。如果你仔细想想,那么这种情绪(即使我们被告知要克制)在历史上可能是进化的最大驱动力之一(其他两个是恐惧和懒惰)。所以人类体内有它是有道理的。类似的论点也适用于其他感觉,如愤怒、快乐、幸福、尴尬,甚至味觉。

我认为,说 AI 永远不可能像我们一样拥有情感是不正确的,我们只需要以正确的方式,以正确的目标训练它,成为一个“进化”的神经网络(后面会有更多介绍),它将能够感受到类似人类的情感。

第三点(伦理):人工智能可以不接受伦理困境及其答案的训练吗(就像我们小时候接受的关于好与坏的训练一样)?有人说人工智能可以从它的训练数据中学习偏见和刻板印象,我问我们不也是这样做的吗?

人类有能力根据情况和我们的愿望忽略和肯定偏见。我相信同样的事情也可以教给人工智能:如果偏见实现了它的预期目标,就肯定它们,否则就忽略它们。

在所有这些分析之后,我找不到对最初问题的满意答案:我们的大脑能做什么,而人工神经网络不能做什么?

前进的道路

AI 还不能做上面提到的大多数事情。****

前进的道路(图片来自稳定扩散)

它不能有效地产生想法,也不能像人类一样对想法采取行动。它也不能不断地从新数据中学习,它需要高度的复杂性和对人类情感的理解,才能创建一个能够以与人类相同的方式体验情感的人工智能系统。T-800 仍然遥不可及。

它对语境的理解仍然不如人类,因此它不擅长解释幽默:

你的人类动物园什么时候开始?

A.作为一名人工智能助理,我没有能力创建或组织活动。我没有一个物质的身体,因此我不能参加像人类动物园这样的活动…

ChatGPT 写的故事大多遵循一定的模式,包含相似的词。人工智能也不像人类那样有身体能力。目前(也许是未来几十年),人工智能似乎只会充当人类的助手,通过自动化我们认为*凡的任务,让我们的工作变得更容易,并为更多创新留出空间。

我认为要让人工神经网络像人脑一样好,需要两个主要步骤:

  1. 量子计算:使用量子计算机的处理能力来训练人工神经网络可能会减少优化损失函数所需的时间,这可以允许快速训练更深层次的神经网络。
  2. 进化中的人工智能:目前,人工智能发展的目标是创建能够满足特定需求的系统,例如诊断病人或根据提示生成图像。然而,最终的目标应该是创造能够自我进化和学习的人工智能,以及与其他人工智能系统分享想法。这个新的人工智能应该也能够像人类一样,通过将自己融入人类世界并体验真实世界来学习。事实上,人类可以被认为是不断学习的神经网络,递归地自我反馈,因为我们的思想和行动以循环的方式相互影响。

人工智能可能最终会让人类过时,但这一结果取决于我们选择如何以及带着什么目标来训练它。如果人工智能最终超越人类的能力,这可能会随着时间的推移而逐渐发生。

你认为人类最终会发现自己处于和弗兰肯斯坦相似的境地吗,弗兰肯斯坦创造了一个他无法控制的怪物?

视觉艺术中基于相似性的图像搜索

原文:https://towardsdatascience.com/similarity-based-image-search-for-visual-art-750c038283c7

从人类感知和定量判断两方面评价视觉艺术的相似性

基于相似性的图像搜索,也称为基于内容的图像检索,在历史上一直是一项具有挑战性的计算机视觉任务。这个问题对于视觉艺术来说尤其困难,因为对于“相似性”的度量应该定义为什么以及谁应该为艺术设定标准,这个问题并不明显

例如,当我上传一张壁画的照片到谷歌寻找相似的图片时,谷歌在“视觉相似图片”部分给了我一系列选项。大多数图像是壁画,壁画中突出描绘了一张脸;其他的都是画了一张脸的纯画。所有的图像跨越了各种各样的配色方案和风格纹理。

左图:丁满·克劳泽( Unsplash )拍摄的壁画照片。
右图:谷歌认为与这张照片相似的图片截图。

来自 Geirhos 等人[1]的 2018 年论文揭示了在 ImageNet 上训练的卷积神经网络(CNN)偏向于图像的风格纹理。为了迫使 CNN 学习基于形状的表示,研究人员在 ImageNet 上应用了风格转移,以创建一个“风格化-ImageNet”数据集。

我决定以他们的发现为基础,调查在纹理偏好与形状偏好模型上训练来自相同艺术风格的艺术作品对的效果。在比较文森特·梵高(Vincent van Gogh)和乔治亚·奥基夫(Georgia O'Keeffe)的绘画作品时,我发现,与形状偏好的风格化图像网络训练的 AlexNet 模型(图 2)相比,纹理偏好的图像网络训练的 AlexNet 模型在关联同一艺术家的作品方面做得更好(图 1)。

我从这个实验中得出的主要结论是在评估视觉艺术的相似性时,如果我们将来自同一艺术家的艺术作品作为相似性的标准,那么 寻找和比较风格纹理远比形状表征更重要。然而,对“风格”的评价似乎是一个非常主观和人类感性的过程。这一发现让我更加好奇,在确定艺术相似性时,什么样的技术方法可以结合人类和定量判断。

图 1:在 ImageNet-trained AlexNet(一个纹理偏向模型)上训练时的相关性得分。注意,相同艺术家图像对 vangogh1-vangogh2 和 okeeffe1-okeeffe2 的相关分数远高于不同艺术家图像对 vangogh1-okeeffe1 和 vangogh2-okeeffe2 的相关分数。

图 2:在风格化图像网络训练的 AlexNet(一种形状偏向模型)上训练时的相关性得分。注意,相同艺术家图像对 vangogh1-vangogh2 和 okeef f1-okeef F2 以及不同艺术家图像对 vangogh 1-okeef f1 和 vangogh 2-okeef F2 的相关分数都相当接*。

Hughes 等人[2]在 2011 年发表的一篇论文结合了定量和心理学研究,得出结论认为将人类感知信息与 art 的高阶统计表示相结合,对于解决 art 的基于相似性的搜索问题极其有效。人类对艺术风格的感知通常基于元素的质量,如线条、阴影和颜色,这些元素很难使用低阶统计来捕捉。因此,这些研究人员利用了高阶空间统计数据,并将他们的发现应用于比较视觉艺术。然后,他们进行了心理物理学实验,要求参与者判断成对艺术作品之间的相似性,并将这些结果与他们的预测模型结合使用。

量化过程和结果

Hughes 等人对 308 幅高分辨率图像数据集进行了研究,这些图像涵盖了各种艺术家的作品。他们使用两种图像分解方法从图像中提取特征:

  • Gabor 滤波器,对特定方向和空间频率的线条和边缘敏感
  • 稀疏编码模型,学习一组与图像的高阶统计特征相关的基函数

提取特征后,他们通过以下四个指标对这些艺术图像进行比较和评估:

  • 峰值方向,其查看峰值振幅出现在从稀疏编码模型学习的基函数的 2D 傅立叶变换中的什么方向
  • 峰值空间频率,查看峰值振幅出现的空间频率
  • 方向带宽,测量基函数对于该首选方向的选择性
  • 空间频率带宽,其测量基函数对于该优选空间频率的选择性

然后,研究人员探索了不同的距离度量(例如 KL 散度),以比较上述四种度量的分布,从而导出距离矩阵。值得注意的是,由于没有风格相似性的基本事实,研究人员通过真实艺术家标签来比较艺术作品,即毕加索的所有画作都被赋予相同的标签,因此距离矩阵是根据真实艺术家标签构建的。使用不同的距离度量执行 k-means 聚类揭示了对视觉艺术图像使用这些高阶统计表示的总体成功(见下图)。

来源:Hughes 等人

心理物理知觉相似性实验

除了开发一种定量表征视觉艺术作品风格的方法,研究人员还进行了两项心理物理学实验,以利用人类的感知信息。他们要求参与者判断抽象艺术、风景和肖像中的艺术图像对之间的相似性,汇总他们的答案,为这三个类别中的每一个创建一个相似性矩阵。

实验 1 试图比较感知判断在预测艺术作品之间的风格关系时的功效。研究人员为每一类图片展示了两张图片;然后,他们使用基于特征的距离训练了一个回归模型,根据它们感知的相似性来预测两幅图像之间的距离。通过学习的模型,他们预测了保留图像和训练图像之间的距离。最后,他们将预测的距离与图像之间的真实感知距离进行了比较。

研究人员发现,来自抽象和景观艺术品的感知信息能够进行统计意义上的预测,这告诉我们有用的统计信息不仅存在于感知相似性数据中,还可以用于模拟视觉艺术作品之间的差异

实验 2 测量了三类图像有限感知信息在多大程度上可以预测更大图像集合中的风格差异和关系,这与基于相似性的图像搜索问题直接相关。这个过程与实验 1 相似,只是这次他们拿出了三个类别中的 51 张图片,并使用剩余的图片来创建一个感知距离矩阵。他们的预测距离矩阵表明,即使只有有限的感知信息,这些信息也有助于“指导我们结合统计特征来理解风格感知的方式。”

最后的想法

总之,Hughes 等人的论文“比较艺术风格计量分析中的高阶空间统计和感知判断”向我们展示了将人类感知信息与高阶统计信息结合起来评估视觉艺术相似性的重要性和必要性

在评价艺术风格如何被感知、定义和评价相似性方面,仍需要进行更多的心理学研究。在他们的论文中,他们提到“目前…对支配人类风格感知的因素的定量研究屈指可数。”

考虑到计算机视觉的更广泛背景,思考艺术风格的高阶统计表示的需要与 CNN 中更深卷积层的需要类似也是有趣的。

总而言之,在考虑如何为视觉艺术品开发更好的基于相似性的图像搜索系统时,将判断植根于人类感知,同时优化并利用所有可用的定量信息是关键。

参考

[1] Geirhos,r .,Rubisch,p .,Michaelis,c .,Bethge,m .,Wichmann,F. A .,和 Brendel,w .“ImageNet 训练的 CNN 偏向于纹理;增加形状偏差可以提高准确性和鲁棒性。”ICLR 2019。arXiV 预印本:【https://arxiv.org/abs/1811.12231】T2。

[2] Hughes,J. M .、Graham,D. J .、Jacobsen,C. R .和 Rockmore,D. N .,“比较艺术风格分析中的高阶空间统计和感知判断”,2011 年第 19 届欧洲信号处理会议。【https://ieeexplore.ieee.org/abstract/document/7073967】T4。

Catherine Yeo 是哈佛大学计算机科学专业的本科生。你可以在推特@ catherinehyeo找到她。

这篇文章的灵感来自哈佛大学心理学 1406,“生物和人工视觉系统:人类和机器如何表现视觉世界。”感谢乔治·阿尔瓦雷斯教授的反馈和指导。

相似度量和图与集合的邻接

原文:https://towardsdatascience.com/similarity-measures-and-graph-adjacency-with-sets-a33d16e527e1

考古学中的数据科学,第二部分

为什么我的考古里有数学?…或者考古学在我的数学里?(图片由作者提供)

在我的上一篇文章(第一部分)中,我向您介绍了用数据科学分析考古遗址的过程。我谈到了“太空中的旧东西”令人沮丧的复杂本质,以及人工制品和位置的网络是如何构成二分图的。

然而,当我们创建一个双模式图并将其投射到其组成的单模式图的过程中,我们在某种程度上陷入了一种悬而未决的状态……所有的最终都连接到了所有其他的

嗯,算是吧。我不得不承认我故意把你带进了一个死胡同。当然,这是我狡猾的计划的一部分,为了达到更有趣的主题,思考两个事物“相似”的含义以及集合的相似性度量。

在我的辩护中,我确实简要地提到过igraph没有最好的工具来真正地用二分图做很多有趣的事情。问题是,它在bipartite_projection中使用的方法只是简单地将关联矩阵中的两两事件相加。事实证明,这在很多情况下没什么用。

相反,我们需要探索一些更健壮的方法来评估集合之间的相似性。与计算相关性或距离的数值方法不同,在比较集合之间的对应性和相似性时,需要考虑一些概念上的特性。在本文中,我们将看看一些最常用的基于集合的相似性度量,并通过推理如何为我们的目标选择合适的度量。

对于那些刚刚加入的人来说,这是考古数据科学系列的第二部分,包括:

  • 第一部分 —创建和探索二分图和单模图,
  • 第二部分-集合和图邻接的相似性度量,
  • 第三部分—图结构和社区检测方法,
  • 第四部分——地理空间网络

我将继续使用 R 编写代码,但是所有这些都可以用 Python 轻松完成。

介绍

在我们进入度量、方法和组合学(即的数学)的杂草之前,让我们停下来想一想,当我们说两件事相似时,我们到底是什么意思。

一般来说,当事物既不完全相同也不完全不同时,我们称之为相似。相似的东西都是相似的——ish。这意味着共享特征或属性的某种度量暗示着关联,但不一定是共性同一性。它表示相似或对应,但它有一定的模糊性,即相似性意味着主观或定性的评估。

因此,我们的目标是双重的。首先,我们需要确定什么集合相似性度量最接*地反映了集合元素之间的关系。其次,我们必须确定一种合适的方法来确定这些实体之间相似性的阈值。换句话说,我们需要为相似性选择标准,并为我们的集合之间被认为相似(或不相似)的内容设置限制

在这一期中,我们将通过探索一些度量相似性的常用算法来解决第一个目标。我将讨论它们背后的直觉,它们的优势,以及选择合适度量的标准。因为这涉及到一些冗长的讨论,所以当我们开始研究网络结构和社区检测时,我将在下一篇文章中讨论阈值。

集合和相似性

与距离或相似性可以很容易计算的实值向量空间不同,集合由唯一成员或元素的无序集合组成。这些成员元素可以是任何东西,例如数字、字母、单词、对象、类别,因此并不总是有明显的数字解决方案,例如可用于元素比较的向量范数或坐标。

相反,我们必须比较集合之间元素的隶属度。更具体地说,我们比较集合上的 or 运算的某些属性的大小或基数,比如它们的交集和并集。幸运的是,有许多度量标准可以用来比较集合之间的相似性。出于我们的目的,我将只讨论几个最常见的:

  • 重叠或 Szymkiewicz-Simpson 系数
  • 雅克卡德相似系数(又名谷本系数)
  • 索伦森-骰子系数

这些相似性度量中的每一个都是专门为处理集合的特性而导出的。它们之间的区别只是它们权衡共同元素(即交集)与差异的方式。

然而,在选择一种方法之前,考虑您试图捕捉的集合之间的确切关系是很重要的。如果您的样本都包含大致相同数量的元素,那么与每个样本的成员数量相差很大的情况相比,您可能会做出不同的选择。完整子集的可能性有多大,这些子集对获取很重要吗?差异比共性更重要,还是相反?

与任何数据分析一样,有必要明确考虑你的方法论以及你的方法。选择方法的基本原理和理由是该过程的关键部分。毕竟,这就是我们称之为数据科学的原因。

重温二部图和投影

首先,让我们回去重新创建我们的采样位置(称为“proveniences”)和在这些位置发现的工件类型的二分图,就像我们在第一部分中所做的那样。

同样,我们有工件类型和位置之间的连接图,并且已经可以在每个顶点类型中看到一些粗略的分组(一个类型在图的顶部,一个在底部)。这个图是密集连接的,有许多节点和边。

人工制品-provenience 连接的二分图(图片由作者提供)。

现在,我们从图中检索来源和工件类型的二元关联矩阵,我们将使用它作为我们的相似性度量的数据。请记住,我们只考虑在每个产地存在(1)或不存在(0)一种文物类型。

根据我们定位关联矩阵的方式,我们现在有了在每个产地发现的工件类型集合(按行)或者发现每个工件类型的产地集合(按列)。

目标是找到这个集合系统的一个划分,使得每个普罗旺斯子集包含一个连贯的文物类型集合(即考古学家所说的集合),并且每个文物类型子集都在一个连贯的普罗旺斯空间集合中被发现(我们称之为上下文)。

我们现在要做的是,看看我们在二分图的投影模式中计算“一致性”的方法。为了将我们的二分节点类型投影到两个独立的单模图中,我们想要评估这些集合的成对比较之间的相似性度量。

考古目标

在我上一篇文章中,我向你介绍了几个考古学概念以及一些关于集合和图形的概念。现在我想停下来解释一下为什么我们对寻找这些子集和分区如此感兴趣。你看,考古学家并不真正解释文物本身。

我们对解释跨越时间和空间的人类活动感兴趣,这给了我们一个了解更抽象概念的窗口,如行为文化。我们这样做的方法是,因为我们没有时间机器回去观察人们实际上在做什么,通过寻找这些活动留下的物理残余物(即碎片或垃圾)之间的关联模式。我知道这很奇怪,但是效果出奇的好。

想一想你的垃圾可能会显示出你的日常行为!

无论如何,我们对寻找人工制品(物品)和产地(三维空间中的位置)的分组如此感兴趣的原因是,这些只是我们收集的样本。我们想知道这些样本是从哪个群体中抽取的。在这种情况下,这些群体就是集合上下文。我们想看看我们是否能把一个集合或环境与另一个区分开来,然后看看揭示了什么样的模式。

例如,一个被识别的人工制品的集合是否有一个特定的日期范围,以便它唯一地识别一个地点被占据的时间或时间?一个集合与家庭活动有关,而另一个反映经济活动,这些空间(即它们的背景)是分开的还是混合的?如果场地被多次占用或被多个家庭占用,它们是连续的还是间歇的?如果不同的集合是连续的,它们可能会混合到相同的上下文中。

有这么多问题,但是将工件和证据数据解析成集合和上下文是第一步。我们想确保我们的分析方法是恰当的。

投影的相似性度量

我们在第一部分的中发现igraph中的bipartite_projection方法只对两两巧合的数量求和。这意味着,即使集合之间有一个共享元素也足以将它们联系起来。它根本不考虑集合中有多少元素不同或被考虑的元素的总数

最后,每个投影中的每个节点都与几乎所有其他节点相连,没有好的方法来评估这些连接的强度。不是特别有用。

现在,我们可以开始更仔细地研究基于集合的相似性度量,以及如何使用它们来投影我们的二分图。我们可以使用现有的包来实现这些,但是我更喜欢构建我们自己的函数,以便更好地理解“引擎盖下”正在发生的事情。

请注意,这些函数是针对二元关联矩阵的。对于具有多重性或计数的集合(即多重集合或 m-集合),将需要不同的方法。

重叠或 Szymkiewicz-Simpson 系数

这个相似性度量就是两个集合之间交集的公共元素的数量除以两个集合中较小的中的成员总数。记住,组成一个集合的唯一成员或元素的数量就是它的大小或者(更正式地说)它的基数。

比如说我们有两个集合:X={a,b,c}和 Y={b,c,d,e,f}。它们的交集是 X∩Y={b,c}并且这些集合的基数|X|=3 并且|Y|=5。它们的重叠系数为:

重叠系数示例

很简单,对吧?只是较小集合的成员同时也是较大集合的成员的比例(因此,重叠)。

但是,请注意,如果较小集合中的所有元素都在较大的集合中——也就是说,如果我们从集合 X 中删除元素 a,使其成为 Y 的完整的子集——那么不管 Y 中有多少额外的成员,系数都等于 1

因此,这个相似性度量以一种非常具体的方式定义了相似性。它包括一个假设,即任何子集都有效地等同于它的超集。这个小小的定义可能非常有用,这取决于您的应用程序。

重叠系数是一个通用的相似性度量,用于从基因测序到主题建模的所有事情。这也很容易通过我们的二元关联矩阵来计算。

Jaccard(或 Tanimoto)相似性指数

Jaccard 指数可能是集合相似性度量中最著名和最常用的。你可能还会看到这一指数被称为 Tanimoto(甚至 Jaccard-Tanimoto)指数,因为它在不同的研究领域随着时间的推移而独立形成。然而,所有版本都引用基于两个集合的交集和它们的并集之间的基数比率的相似性度量。

Jaccard(或 Tanimoto)相似性指数

与重叠系数不同,您可以看到 Jaccard 索引在分母中考虑了两个集合的总成员数。这意味着这个指数将总是 T21 惩罚集合间的差异,即使一个是另一个的真子集。

为了演示,让我们回到我们的玩具示例集 X={a,b,c}和 Y={b,c,d,e,f}来计算 Jaccard 索引值。

Jaccard 示例

通过考虑集合的总基数,Jaccard 索引比重叠系数(≈0.67)低一点。接下来,让我们把元素 a 去掉,让 X 成为 Y 的真子集,看看会发生什么。鉴于重叠系数为 1,我们发现

Jaccard 子集示例

这是一个显著的差异!即使 X 是 Y 的真子集,Jaccard 索引也强调了集合之间的差异超过了它们的共性。集合之间的差异越大,索引值越低。同样,根据您的用例,差异的额外惩罚可能对您有利。

正如已经提到的,Jaccard 相似性指数可能是集合相似性度量中最常见和最通用的。它通常用于比较分类数据,经常出现在生态或社会科学应用中。

我们计算指数的函数与计算重叠系数的函数非常相似,只是用求和代替了外积中的最小值,并改变了分母。

索伦森-骰子相似系数

与 Jaccard 指数相似,s rensen–Dice 相似系数比较交集与两个集合的总隶属度的比率。不同之处在于,索伦森-骰子的分母是基数之和,而不是 Jaccard 的集合并的分母。另一个不同之处是,索伦森-骰子将分子中的交集增加了一倍,其效果是“加权”集合之间的共性比差异多一点。

索伦森-骰子相似系数

让我们再次看看索伦森-骰子相似度与我们的玩具示例 X={a,b,c}和 Y={b,c,d,e,f}的比较。

索伦森-骰子相似系数示例

正如所料,我们计算的相似性 DSC=0.5 略高于 Jaccard,但低于重叠系数。如果我们再让 X 成为 Y 的真子集(,掉元素 a),现在会发生什么?

索伦森-骰子相似系数子集示例

像 Jaccard 一样,s rensen–Dice 相似性系数并不将真子集等同于同一性,而是将其计算为比 Jaccard 相似性指数更强的相似性。不同之处在于,Jaccard 是交集与并集的比率,而 s rensen–Dice 是交集与两个集合的不相交并集(,并集减去交集)的比率。

实际上,它是匹配与不匹配的比率,而不是 Jaccard 的匹配与整体成员的比率。和以前一样,这可能更适合您的特定应用。

二元关联矩阵的函数设置现在应该很熟悉了。

选择相似性

我们现在有三种非常具体的方法来度量相似性,那么我们如何选择使用哪种度量呢?嗯,没有“放之四海而皆准”的答案,所以我们必须决定对于我们的特定场景,我们所说的“相似性”是指什么。

我们必须考虑我们的数据生成过程,并根据我们认为的内在结构,决定我们对我们试图捕捉的相似性进行什么样的排序。更重要的是,我们需要从数据中证明那个决定。

重新审视数据分布

回想一下第一部分,当我们查看程度和边权重分布时,对于产地和工件类型,我们发现由相对低强度的边形成了高密度的连接。我们正在寻找相似性度量来纠正这一点,但是我们希望我们的单模投影有多“稀疏”呢?哪种方法最有可能揭示我们的数据是否有真实的结构,哪种方法可能会无意中掩盖联系?

当观察各种相似性度量和它们的直觉时,我们发现共性对差异的相对权重产生了显著的差异。然而,每次计算涉及的基数完全取决于每个样本中可能有多少成员。这也意味着样本量的多样性会影响我们的相似性度量。让我们看看那些是什么样子的。

证据和人工制品类型的样本量分布

在左边,我们有一个密度分布,显示了在一个地区内有多少种艺术品。在右边,我们有找到任何单一工件类型的产地的数量。

有一件事立即引起了人们的注意——我们相当大一部分的产地包含了各种各样的人工制品类型,但是这些个体类型中的大多数只能在很少的产地中找到。同样值得注意的是,一些艺术品类型似乎在很高比例的省份出现。请记住,我们的样本仅包含 152 个产地和 251 种独特的人工制品类型。

然而,这两种分布都是右偏的,这对于我们试图发现隐藏的网络结构是有希望的。为什么?这意味着右边尾部的所有值都不太可能提供特别的信息。

这种偏差在工件类型中更加突出。另一方面,普罗旺斯的倾斜程度较低,这表明整体空间结构不太分散。

随处可见的工件类型很少告诉我们其他工件类型之间的联系,也几乎没有告诉我们产地之间的差异或相似之处。

类似地,包含一点点所有东西的产地不能帮助我们找到空间差异,并且很可能是不同集合混合在一起的地方。

这并不是说它们不重要,一旦我们找到了用于进一步分析的一级结构,我们可以将它们连接起来,但目前它们没有任何信息。我们正在寻找的内在结构不太可能在那些样本中找到。

幸运的是,我们现在知道样本的主体(当被投影为单模图时)应该具有比我们之前在“一切到一切”投影中发现的更小的图节点

比较相似性度量

记住我们的样本分布,我们可以通过选择相似性度量来进行推理。从我们上面讨论的三种方法中,我们要考虑的是:

  • 对我们的目标来说,适当的子集有多大的可能性和重要性?
  • 我们想要对集合的交集进行多强的加权?
  • 我们想要多强烈地惩罚这些差异?

当然,我们也想考虑我们选择的数据样本的度量看起来是什么样的,以及这对它的图形结构有什么影响。我们将确定关联矩阵g_assemblages_bpg_inc的方向,以捕捉适当的维度(来源或工件类型),并将其传递给我们的相似性函数。

每一个都代表了我们的单模图投影的一个潜在的加权邻接矩阵。出于比较的目的,我们将拉出这些正方形矩阵中每一个的下面的三角形(不包括自对自对角线,它将全是 1)。

首先,让我们看一下 provenience 维度的三个相似性度量。

现在,我们将值列表堆叠到一个数据框中,并绘制每种方法的结果相似性值的密度分布图。

三种产地相似性度量值的密度分布图

很容易看出不同的相似性度量是如何遵循我们之前讨论过的关于集合的直觉的。

重叠系数(ssoc顶行)几乎是寻找子集的,因此将*一半的省被计算为 50%(或更好)匹配。这意味着,至少通过设置重叠,我们的大部分位置样本将被认为更相似,而不是不相似——其中许多被认为是相同的。

相反,Jaccard 相似性指数(jacc中间一行)计算出大多数相同的产地为不同的。与重叠系数不同,Jaccard 优先考虑而不是交集,因为两个集合的并集总是大于它们的交集,除非集合相同

类似地,索伦森-骰子相似性指数(sd底行)被视为对我们的样本之间的差异加权更大——但只是差异,而不是连接集的完整基数。密度曲线与 Jaccard 非常相似,但是您可以看到,它对集合交集的强调将相似性值推向了 50%的更高水*。

但是,请注意,我们的 provenience 数据分布如何反映在相似性度量配置文件中。尽管每个 provenience 的工件类型的数量有些右偏,但是大多数 provenience 展示了相对较高的基数集。这导致了我们相似性度量的奇怪的多模态分布。

更大的集合意味着更有可能在成对的产地之间至少有一些交集。不幸的是,这也增加了微小但虚假相似性的可能性。

接下来,让我们看一下沿着工件类型的相似性的投影。

同样,我们将堆叠相似性值,以便查看它们的分布。

三种伪影类型相似性度量值的密度分布

这些看起来与各省相似性的密度分布非常不同,但也注意到现在相似性度量之间似乎有相当大的小于的差异。这是为什么呢?

因为每个工件类型出现的产地数量的分布比每个产地的类型更加右偏。换句话说,我们*均比较了小得多的组。这使得交叉点更小,更不容易开始。然而,这也使得集合之间的任何对应更有意义。

如果您仔细观察,您会注意到我们的三个相似性度量之间的差异在右尾巴中仍然可见。如果我们排除零值,我们可以看得更清楚。

三种伪影类型相似性度量的非零值的密度分布

当我们从密度分布中去掉零值“尖峰”时,您可以看到三种方法之间相同的加权模式,即重叠寻找子集,Jaccard 寻找差异,s rensen-Dice 寻找它们之间的比例。

有趣的是,这三者都在确认我们确实有至少一些出现模式相同的工件类型。

选哪个?

我一开始就说,这个问题没有简单明了的答案,我们的探索应该已经展示了为什么会这样。每种方法都突出了我们数据中交互作用的不同方面,所以真正的问题是……嗯……无论你试图解决的是什么问题!

**我们的问题(即考古问题)是,样本中是否存在分区或子图,表明社区的存在。我们想找出这些社区是否映射到我们可以解释的离散集合和上下文。我们确实想要捕获适当的子集,但是我们也想要避免虚假的关联。

在这里介绍的三种方法中,你认为哪一种能满足这些限制?

用图表的术语来说,我们试图找出数据是否表明类似于无标度网络的东西。这些是其中(即,连接到节点的边的数量)的分布接*幂律曲线的图。如果我们可以找到,那么我们很有可能看到一个“小世界”网络,它具有非常明确的分析和社区检测属性。

如果不是,那么我们仍然有明确的和经验的证据,观察到的现象是随机过程的产物。我们将会知道关于这个特定站点的推断的极限。换句话说,对于许多人可能认为是主观或定性的评估,我们有一个适当的假设检验。

接下来的步骤…

不幸的是,我们还没有准备好选择我们的相似性度量。我们仍然需要做一些探索,看看它们对产生的单模投影图的影响,但是现在我想你可以开始看到它们中的一些可能会导致什么。

事实是,他们中的任何一个都可能在不同程度上满足我们的标准。不同之处在于分配给所形成的连接的权重,这些权重将在我们接下来的几个步骤中被证明是非常重要的。过滤阈值和一些社区检测方法都依赖于边缘权重。

这基本上是数据科学汇合的地方。虽然有许多方法和算法会产生一个结果,但是数据和方法的选择的基本原理和合理性允许我们制定和测试假设。

没有这种推理,我们可以让数据以各种方式跳舞和旋转,以获得看起来合法的输出。我会在某个时候写一篇关于方法方法论之间区别的文章。

我们的下一个任务将是探索相似性度量和阈值对产生的单模图的结构的影响。我们将讨论一下无标度和小世界网络、一般的图形分析,并开始研究社区检测方法。

使用 IVFPQ 进行相似性搜索

原文:https://towardsdatascience.com/similarity-search-with-ivfpq-9c6348fd4db3

了解如何实现倒排文件索引(IVF)和乘积量化(PQ ),以实现快速有效的*似最*邻搜索

Voronoi 细胞。除非另有说明,所有图片均为作者所有

在上一篇关于 相似性搜索的产品量化 的文章中,我们解释了什么是产品量化,并详细介绍了产品量化如何用于相似性搜索。

乘积量化将数据库中的每个向量转换为短码(PQ 码),这种表示形式对于*似最*邻搜索来说非常节省内存。

正如文章中提到的,产品量化是高度可扩展的,但是单独实现产品量化对于大规模搜索来说并不是最有效的方法。

在本帖中,我们将研究和学习如何将产品量化与倒排文件索引相结合,以形成一种高效快速且记忆灵敏的搜索方法。

在研究论文中,IVFPQ 通常也称为 IVFADC。

倒排文件索引

一个倒排文件是一个索引结构,用于将内容映射到它们的位置

你可能会问,内容位置是什么,倒排文件索引如何应用于带有产品量化的相似性搜索?

在我们的上下文中,内容指的是数据库向量,位置指的是这些向量所在的分区。

是的,你没听错,我们需要对数据库向量进行分区。对数据库向量进行分区的原因是为了只对特定分区的向量而不是所有向量进行搜索。

粗略量化器

首先,数据库向量被分成k'个分区。分割是通过 k 均值聚类完成的,产生了我们所说的粗量化器。

这里,构建了一个倒排文件索引,它将用于将向量列表(即倒排列表)映射到相应的分区。

每个分区由一个分区质心来表示和定义,每个向量只能属于一个分区。这种结构有时被称为 Voronoi 单元,因此基于分区的搜索策略也被称为单元探测方法

Voronoi 单元的例子

剩余向量的乘积量化

接下来,对于训练和编码,我们采用与 相似性搜索的产品量化 中描述的相同的过程,除了这次训练和编码是在剩余向量而不是原始向量上完成的。

什么是剩余向量

剩余向量只不过是向量从其分区质心的偏移,即原始向量与其关联的分区质心之间的差。

为了计算残差,我们只需从原始向量中减去质心。

计算残差,向量从其分区质心的偏移量

为什么是剩余向量

为什么我们要使用残差向量,还要经历额外的计算残差的步骤?

编码残差向量背后的直觉是提高准确性,因为编码残差比编码原始向量更精确。

为了理解这意味着什么,我们用一组三维向量来说明。注意,在现实生活中,对这种低维向量应用乘积量化是不实际的。三维向量在此仅用于在计算残差之前和之后解释和可视化它们。

在这个例子中,向量可以清楚地分成两个分区,分区质心用红圈表示。还有一个用橙色十字符号表示的查询向量。如下图所示,这个查询向量非常接*Partition B

第一个图-具有两个分区的原始向量

计算完剩余向量后,让我们看看它们在下图中的样子。

第二幅图——剩余向量

第一次观察

通过获取残差,来自两个分区的数据点实际上已经重新定位到以原点为中心的相同空间,并且彼此重叠。这与第一个图非常不同,在第一个图中,可以看到两个分区彼此隔离。

取残差类似于将质心移动到原点,这样所有的数据点现在都聚焦在原点上。

第二次观察

让我们注意第一个图中的查询向量和第二个图中的查询向量的残差。

在第二个图中,Query Residual A是查询向量从分区 A 的质心的偏移,而Query Residual B是查询向量从分区 b 的质心的偏移。

在残差计算之前和之后,查询记录到相应分区和数据点的距离保持不变。

从所有这些观察中我们能得出什么结论?

令人惊讶的是,通过获取残差,我们设法减少了来自所有分区的数据点的分布,并将它们压缩到相同的区域,同时保持了到查询记录的相同距离。

由于数据集中的方差较低,当使用乘积量化进行*似最*邻搜索时,这将转化为较小的误差,并最终导致更好的搜索质量。

现在,在理解了剩余向量以及为什么使用它们之后,让我们回到乘积量化过程

概括地说,从乘积量化训练中学习码本,并且从编码过程中生成 PQ 码。

产品量化培训和编码流程

随着倒排文件索引的出现,PQ 代码现在作为倒排列表条目的一部分被包括在内。如下所示,倒排表的条目将由向量标识符(向量 Id)和编码残差(PQ 码)组成。

由向量 Id 和 PQ 代码组成的反向列表条目

然后,这些条目被添加到标记为相关分区的相应倒排表中。

使用 IVFPQ 搜索

粗略量化器保存关于分区列表和分区质心的信息。给定一个查询向量q,使用粗量化器来寻找最接*q分区质心

在获得离q最*的分区质心后,计算查询向量的残差。

类似于在 相似性搜索的乘积量化 中描述的搜索过程,我们使用码本和查询向量的残差来预先计算部分*方欧几里德距离

这些部分*方欧几里德距离被记录在具有k行和M列的距离表中,其中M表示向量段的数量,而k表示在训练期间被选择来执行 k 均值聚类的值。

现在,有了倒排文件索引,我们可以选择性地查找部分距离并进行求和,只针对倒排列表中那些被标记为分区质心最接*q的分区的条目。

倒排文件索引是非穷举搜索方法的关键组件。

为了找到并返回*K*最*的邻居,一种有效的方法是使用固定容量的 Max-Heap 。这是一个基于树的结构,其中根节点总是包含最大值,每个节点的值都等于或小于父节点的值。

在每次距离计算之后,只有当向量标识符的距离小于最大堆中的最大距离时,向量标识符才被添加到最大堆结构中。

改善搜索结果

早些时候,对于倒排文件索引,我们讨论了编码残差而不是原始向量来改善搜索结果。

然而,如果只从一个分区进行搜索,我们可能会遇到比没有倒排文件索引的基本乘积量化更差的结果。

虽然搜索速度快得令人难以置信,只需探测一个分区,但结果会受到影响,因为搜索范围现在确实局限于很小的记录子集。

根据作者的 为最*邻搜索【1】、

查询向量及其最*的邻居通常不被量化到相同的分区质心,而是被量化到附*的分区质心

如下例所示,尽管查询向量最接*顶部分区的质心,但附*分区中还有其他向量也是查询向量的潜在最*邻居。

如果搜索仅限于查询向量最接*分区质心的分区,那么我们可能会错过位于附*分区的许多潜在的最*邻居。

当查询向量非常靠*单元或分区的边界时,这种影响会更加明显。

为了避免错过那些潜在的最*邻居,可以在更多分区上执行向量搜索。

特别地,我们将在W分区上执行搜索,其中来自这些W分区的质心最接*查询向量。W通常是一个可配置的参数。

拥有w > 1有什么寓意?

嗯,随着更多分区的加入,我们将需要使用每个分区质心分别计算查询向量的残差。并且对于查询向量的每个残差,需要计算单独的距离表。

最终,在W分区上执行搜索的意图将导致计算W剩余查询向量和W距离表。

尽管构建倒排文件索引会带来开销,但对于大型数据集,拥有更多分区并从更多分区进行搜索通常会提高效率。这是一条可行之路,并且在现实中被证明非常有效。

然而,对于小数据集,如果分区的数量太大,粗量化器的复杂性可能成为瓶颈。

摘要

下图总结了使用反向文件索引和产品量化(IVFPQ)进行相似性搜索的过程和步骤。

具有倒排文件索引和产品量化的相似性搜索(IVFPQ)

使用倒排文件索引(IVF) ,可以在相关分区上执行相似性搜索,将搜索范围限制在高度相关的一个小子集中。

另一方面,乘积量化(PQ) 能够用极其节省存储空间的压缩表示对矢量进行编码。

利用乘积量化(PQ)实现倒排文件索引(IVF)提出了一种新方法(IVFPQ ),该方法对于大规模相似性搜索非常有效。

将两者结合在一起,我们可以从两个世界中获益。结果呢?对于大规模*似最*邻搜索,搜索时间快,精度好。

詹姆斯·巴尔茨在 Unsplash 上的照片

处理十亿级向量数据集?点击下面的链接,了解 HNSW 以及它如何与 IVFPQ 一起使用,以形成十亿级相似性搜索的最佳索引方法。

参考

[1] H. Jégou,M. Douze,C. Schmid,最*邻搜索的乘积量化 (2010)

[2] C. McCormick,用于 k-NN 教程的产品量化器第 2 部分 (2017)

[3] J. Briggs,产品量化:将高维向量压缩 97%

***Before You Go...****Thank you for reading this post, and I hope you’ve enjoyed learning about similarity search with IVFPQ.**If you like my post, don’t forget to hit* [***Follow***](https://peggy1502.medium.com/) *and* [***Subscribe***](https://peggy1502.medium.com/subscribe) *to get notified via email when I publish.**Optionally, you may also* [*sign up*](https://peggy1502.medium.com/membership) *for a Medium membership to get full access to every story on Medium.*📑 *Visit this* [*GitHub repo*](https://github.com/peggy1502/Data-Science-Articles/blob/main/README.md) *for all codes and notebooks that I shared in my posts.*© 2022 All rights reserved.

有兴趣阅读我的其他数据科学文章吗?查看以下内容:

https://pub.towardsai.net/building-a-product-recommendation-engine-with-aws-sagemaker-321a0e7c7f7b Peggy Chang

张佩琦

掌握动态编程系列

View list2 storiesArticle cover for “Mastering Dynamic Programming — Understanding the fundamentals and knowing when and how to apply this optimization technique”. Author: Peggy ChangArticle cover for “Mastering Dynamic Programming II — Manual tabulation and workout is a great way to start grokking, analyzing, and spotting patterns, as well as strengthening our understanding and intuitions”. Author: Peggy Chang

使用 Google Vision API 进行简单的计算机视觉图像创意分析

原文:https://towardsdatascience.com/simple-computer-vision-image-creative-analysis-using-google-vision-api-50cc42737a00

使用标签检测、对象检测、面部表情检测、文本检测和主色检测创建您的第一个计算机视觉项目

凯文·Ku 在 Unsplash 上的照片

介绍

计算机视觉可以用来从图像、视频和音频中提取有用的信息。它允许计算机看到并理解从视觉输入中可以收集到什么信息。收到视觉输入后,它可以收集图像中有价值的信息,并确定必须采取的下一步措施。

Google Vision API 是一项 Google 云服务,支持使用计算机视觉从图像输入中提取有价值的信息。作为初学者,您可以使用此服务来获得对图像的有意义的见解。下图显示了 Google vision API 的工作方式。

来源:https://cloud . Google . com/architecture/creative-analysis-at-scale with Google-cloud and-machine-learning

上图展示了谷歌视觉 API 的能力。Google Vision API 可以识别广告图像中的面部表情、文本和主色。面部表情清楚地显示了这个人喜悦的表情,文本描述了单词“了解更多”,主色显示了图像中的前 10 种主色。

我们可以看到,通过利用 Google vision API 功能,我们可以从图像中获得很多洞察力。例如,假设我们想知道广告图像中的哪些因素导致客户点击并查看我们的广告。这可以通过利用 Google vision API 服务来发现。

本文将主要关注如何获得图像中的洞察力因素,以及我们可以从特定图像中获得什么样的洞察力。我们将不会使用广告图像的例子,因为它不能发表由于公司的保密性。相反,我们将使用 Kaggle 数据集中可用于数据分析的产品图像。

资料组

这个项目的数据集图像基于 Kaggle 的时尚产品图像数据集。因为数据集包含大量来自电子商务网站的产品图像,所以我们将只选取一小部分可用于我们的创意分析的图像。这个数据集许可允许你复制、修改、分发和执行工作。

设置 Google cloud vision API:循序渐进

在开始之前,我们必须首先从 Google Cloud services 配置 vision API 服务。一步一步的说明可以在这里找到。但是,为了让事情变得更简单,我们将向您展示如何一步一步地从 Google Cloud Services 设置 vision API。

(注意:您必须从您自己的 Google Cloud 帐户配置此 Google Cloud services vision API 我们不会在本教程中向您提供包含机密 Google Cloud 密钥的文件)。

第一步:登录谷歌云项目,从主页选择“转到 API 概述”。

(图片来自作者)

步骤 2: 选择“启用 API 和服务”,然后搜索并启用 Cloud Vision API。

(图片来自作者)

(图片来自作者)

第 3 步:转到凭证,然后单击“创建凭证”,再单击“服务帐户”。

(图片来自作者)

步骤 4: 输入您的服务帐户信息(您可以跳过可选部分),然后单击完成。

(图片来自作者)

步骤 5: 导航到您创建的服务帐户。转到密钥,然后“添加密钥”和“创建新密钥”。

(图片来自作者)

(图片来自作者)

第六步:创建一个 JSON 键类型,然后下载 JSON 文件,放在 Python 脚本的工作目录下。

(图片来自作者)

安装必要的库

在我们开始计算机视觉建模之前,我们必须首先安装所需的库。我们将安装的第一个库是 google-cloud-vision,用于计算机视觉模型检测。我们可以在获得对 Google Cloud Vision API 的访问权后使用这个库。

下一个库是 webcolors,当我们需要将十六进制颜色数从颜色检测转换为我们知道的最接*的颜色名称时,它就派上了用场。

导入必要的库

安装完必要的库之后,我们将把它们导入到我们的脚本中。我们将从谷歌云库导入视觉,用于视觉建模检测目的。对于数据预处理,使用了 Ipython、io 和 pandas 等其他库。

Webcolors 用于将十六进制颜色格式转换为我们熟悉的颜色名称。KDTree 用于在 CSS3 库中查找最接*的颜色匹配。 KDTree 提供了一组 k 维点的索引,可用于快速找到任意点的最*邻居。

在我们的 python 脚本上激活 Google vision API

将 JSON 文件放入我们的目录后,我们必须在 Python 脚本中激活 Google Cloud Vision API 服务。

标签检测

可以使用标签检测来检测图像中的任何标签。LabelAnnotation 可用于标识图像中的一般对象、位置、活动、产品和其他事物。下面的代码描述了我们将如何从时尚数据集的图像中提取标签信息。

从这张图片中,我们可以看到谷歌视觉 API 已经检测到几个通用标签,如:

  • 面部表情(微笑)
  • 人体(脸、关节、皮肤、手臂、肩膀、腿、人体、袖子)
  • 物体(鞋)

尽管视觉已经识别了许多标签,但是一些一般的物体被错误地识别并且没有被提及。视觉将凉鞋图像误认为是一只鞋。它也无法识别上图中的衣服、叶子植物、杯子和椅子。

目标检测

对象检测可用于检测图像中的任何对象。与标记不同,对象检测主要与检测的置信度有关。LocalizedObjectAnnotation 扫描图像中的多个对象,并显示矩形边界内的对象位置。

从该图中,我们可以看到 google vision API 检测到了如下几个对象:

  • 太阳镜(置信度:90%)
  • 项链 1(置信度:83%)
  • 项链 2(信心:77%)
  • 超短裙(信心:76%)
  • 衬衫(信心:75%)
  • 服装(信心:70%)
  • 项链 3(信心:51%)

在上图中,我们可以看到大部分的视觉已经识别出了一件衣服。由于它们的高置信度,水晶般透明的物体已经被识别为几个物体,例如太阳镜、项链 1、项链 2、迷你裙、衬衫和衣服。项链 3 具有最低的置信度,因为视觉认为右下角的图像也是项链。因为项链 3 对象更像手镯而不是项链,所以它的置信度比其他对象低。

人脸表情检测

人脸检测可以检测图像中的任何人脸和情感。FaceAnnotation 是一种扫描图像中人脸位置的技术。在扫描人脸的同时,还可以扫描各种面部情感表情。

从上面的这张图片中,我们可以看到谷歌视觉 API 已经检测到人脸的各种表情,例如:

  • 乔伊:很有可能
  • 悲伤:不太可能
  • 愤怒:不太可能
  • 惊喜:不太可能

从上图我们可以看出,表情是微笑,vision API 将其识别为喜悦的表情。悲伤、愤怒和惊讶的其他表情似乎与上图不符,因为这个人没有表达这些情绪。结果,快乐被评分为非常可能,而另一个被评分为非常不可能。

文本检测

TextAnnotation 可用于从图像中检测和提取文本。矩形边界内的单个单词和句子包含在提取的文本中。

从这张图片中,我们可以看到谷歌视觉 API 已经检测到各种文本,如:

  • 文化
  • 这是
  • 什么
  • 可怕的
  • 相貌
  • 喜欢

For some reason, the vision has identified what appears to be a Japanese word of 文化. This can happen if the vision inadvertently detects any word from the rattan carving behind the clothing picture and somehow translates it into a Japanese word.

图像中的文本显示它检测大写和非大写单词。它还在上面的文本中找到了“THISIS”这个词,应该是“THIS IS”。因此,vision API 的局限性在于,它会将单词“THISIS”检测为“THIS IS ”,因为该单词太窄。

主色检测

主色检测是图像属性标注的特征之一。它可以使用主色检测来检测图像中的前十个主要特征颜色及其颜色分数。

Google Vision API 已经检测到十六进制格式的前十种不同颜色,如上图所示。为了获得真实的颜色名称,我们必须使用 CSS3 库将十六进制颜色格式转换为颜色名称格式。然后,我们利用 KDTree 获得我们在 CSS3 库中熟悉的最接*的颜色。

我们使用十六进制颜色 A41B24 作为第二主色的示例。使用上面的函数,我们发现 CSS3 库中最接*的颜色是耐火砖。上图中运动鞋的红色表明了这一点。

结论

我们已经使用标签、物体、面部表情、文本和上面创造性分析中的主色检测完成了计算机视觉建模。在我们运行 vision API 之后,每个检测注释仍然有许多限制。

  • 标签检测:它可以检测图片上的许多一般对象和面部表情,但有些对象会被误识别(例如在我们的分析中,它将一只凉鞋误识别为一只鞋)。
  • 物体检测:在这些因素中也存在误识别的情况,但是我们可以通过查看物体检测置信度来预测它(例如在我们的分析中,它将手镯误识别为另一条项链)。
  • 面部表情检测:图像清晰地显示出男孩喜悦的表情。但是,如果您尝试使用从视觉建模中排除的表情的图像,它会将所有表情检测分类为非常不可能,因为视觉建模无法确定它是哪个表情。
  • Text detection: The texts can be detected using the text annotate, but there is an unwanted text that is included in our vision modeling (for example, in our analysis, it detects the Japanese word “文化” even though there are no Japanese words on the picture).
  • 主色检测:可以检测上图中的多种主色,但目前只能转换成 RGB 或 hex 颜色格式。要转换成我们熟悉的颜色,必须添加一个将十六进制颜色转换成颜色名称的函数。

承认

如果你想获得更多关于这个建模方法中使用的代码的细节,你可以查看我的 Github 库

(注意:Google 云密钥在存储库中不可用,您必须按照上面的步骤自己创建)。

Github:https://github.com/nugrahazikryhttps://github.com/nugrahazikry

【领英:https://www.linkedin.com/in/zikry-adjie-nugraha/

Statsmodel 线性回归模型概述

原文:https://towardsdatascience.com/simple-explanation-of-statsmodel-linear-regression-model-summary-35961919868b

Statsmodel 库模型摘要说明

作者图片

简介

回归分析是许多统计学家和数据科学家的饭碗。我们执行简单和多元线性回归以进行预测,并总是希望获得一个没有任何偏差的稳健模型。在本文中,我将使用一个简单的示例来讨论 python 的 statsmodel 库的摘要输出,并稍微解释一下这些值是如何反映模型性能的。

典型模式总结

出于演示的目的,我将使用 kaggle 的工资数据集 ( Apache 2.0 开源许可)。这个数据集有两列:年资和薪水。我还有两个专栏:项目和人员管理。

抽样资料

当我们使用 statsmodel 使用所有三个变量来预测工资时,我们会得到以下汇总结果。

我将在下面的总结中解释所有这些参数。

副变量

“工资”是数据中唯一的因变量。

模型和方法

OLS 代表普通最小二乘。该模型试图找到数据集的线性表达式,该表达式使残差*方和最小化。

测向残差和测向模型

我们总共有 30 个观察和 4 个特征。在 4 个特征中,3 个特征是独立的。因此,DF 模型为 3。测向残差是根据总观测值-测向模型-1 计算的,在我们的例子中是 30–3–1 = 26。

协方差类型

协方差类型通常是非稳健的,这意味着没有消除数据来计算要素之间的协方差。协方差显示两个变量如何相对移动。如果该值大于 0,则两者向同一方向移动,如果该值小于 0,则变量向相反方向移动。协方差不同于相关性。协方差不提供关系的强度,只提供运动的方向,而相关值是归一化的,范围在-1 到+1 之间,相关提供关系的强度。如果我们想获得稳健的协方差,我们可以声明 cov_type=HC0/HC1/HC2/HC3。然而,statsmodel 文档并没有丰富到可以解释所有这些。HC 代表异方差一致性,HC0 实现了所有版本中最简单的版本。

R *方

r *方值是决定系数,它表明如果数据由所选的独立变量解释,变异性的百分比。

可调 R *方

随着我们向模型中添加越来越多的自变量,R *方值会增加,但实际上,这些变量不一定对解释因变量有任何贡献。因此,添加每个不必要的变量需要某种惩罚。当引入多个变量时,调整原始的 R *方值。本质上,在执行多元线性回归时,我们应该始终寻找调整后的 R *方值。对于单个自变量,R *方值和调整后的 R *方值是相同的。

在讨论 F-统计量之前,我们需要先了解 t-统计量。下表提供了 t 统计数据。

系数和标准误差

coef 列表示每个自变量的系数以及截距值。Std err 是所有数据点上相应变量系数的标准偏差。当仅使用一个预测变量时,可以从如下所示的二维空间获得标准误差

作者图片

t 值和 P 值> |t|

t 列提供对应于每个自变量的 t 值。例如,这里的项目、人员管理和工资都有不同的 t 值以及与每个变量相关的不同 p 值。t 统计用于计算 p 值。典型地,当 p 值小于 0.05 时,它表示反对零假设的有力证据,该零假设声明相应的自变量对因变量没有影响。项目的 p 值 0.249 告诉我们,有 24.9%的机会项目变量对工资没有影响。似乎 YearsExperience 的 p 值为 0,表明 YearsExperience 的数据具有统计学意义,因为它小于临界极限(0.05)。在这种情况下,我们可以拒绝零假设,并说多年的经验数据在很大程度上控制着工资。

年资与薪水显示出很强的相关性

F-统计

f 检验提供了一种检查所有自变量的方法,如果其中任何一个与因变量相关的话。如果 Prob(F-statistic)大于 0.05,则没有证据表明任何自变量与产出之间存在关系。如果它小于 0.05,我们可以说至少有一个变量与产量显著相关。在我们的例子中,p 值小于 0.05,因此,一个或多个自变量与输出变量薪金相关。我们之前已经看到,工作经验与薪水有很大的关系,而其他的则没有。因此,f 检验数据支持 t 检验结果。然而,在某些情况下,prob(F-statistic)可能大于 0.05,但其中一个独立变量显示出很强的相关性。这是因为每个 t 检验都是用不同的数据集进行的,而 f 检验检查的是包括所有变量在内的综合效应。

对数似然

对数似然值是模型与给定数据拟合程度的度量。当我们比较两个或多个模型时,这是很有用的。对数似然值越高,模型就越符合给定的数据。它的范围可以从负无穷大到正无穷大。

包含所有三个变量时的对数似然

仅包含“项目”时的对数可能性

当所有三个独立变量都包含在模型中时,对数似然值为-310.21,高于仅包含项目数据时的-334.95。这意味着第一个模型更符合数据。如上所述,它还与 R *方值密切相关。

AIC 和 BIC

AIC(代表由日本统计学家 Hirotugo Akaike 开发的 Akaike 信息标准)和 BIC(代表贝叶斯信息标准)也被用作模型稳健性的标准。目标是最小化这些值以获得更好的模型。我在另一篇文章中讨论了这些话题。

综合与 Prob(综合)

一旦部署了模型,综合测试将检查残差的正态性。如果该值为零,则意味着残差完全正常。这里,在示例中,prob(综合)为 0.357,表明残差呈正态分布的概率为 35.7%。对于一个稳健的模型,除了检查 R *方和其他量规,残差分布还要求在理想情况下是正态的。换句话说,当对拟合值作图时,残差不应遵循任何模式。

偏斜和峰度

偏斜值告诉我们残差分布的偏斜度。正态分布变量的偏斜值为 0。峰度是与正态分布相比的轻尾或重尾分布的度量。高峰值表示分布太窄,低高峰值表示分布太*。介于-2 和+2 之间的峰度值有助于证明正常状态。

德宾-沃森

Durbin-Watson 统计提供了残差中自相关的度量。如果残差值是自相关的,则模型会变得有偏差,这是不期望的。这仅仅意味着一个值不应该依赖于任何先前的值。该测试的理想值范围是从 0 到 4。

雅尔克-贝拉(JB)和普罗布(JB)

Jarque-Bera (JB)和 Prob(JB)类似于测量残差正态性的 Omni 测试。

条件号

高条件数表示数据集中可能存在多重共线性。如果只有一个变量用作预测值,则该值较低,可以忽略。我们可以像逐步回归一样进行,看看当包含额外的变量时,是否有任何多重共线性。

结论

我们已经讨论了 statsmodel 输出中的所有汇总参数。这将对那些有兴趣检查健壮模型的所有规则的读者有用。大多数时候,我们寻找 R *方值,以确保模型解释了大部分的可变性,但我们已经看到还有更多。

感谢阅读

Github 页面

Youtube 频道

https://mdsohel-mahmood.medium.com/membership

浅谈决策树算法如何决策

原文:https://towardsdatascience.com/simple-explanation-on-how-decision-tree-algorithm-makes-decisions-34f56be344e9

阿达什·库姆穆尔在 Unsplash 上拍摄的照片

决策树算法背后的直觉

决策树是一种非常流行的机器学习算法。它适用于线性和非线性数据。此外,它还可以用于分类和回归。有了 Python 和 R 中的优秀库和包,任何人都可以轻松地使用决策树和其他机器学习算法,甚至不知道它是如何工作的。但是了解算法的直觉或机制有助于决定在哪里使用它。

在这篇文章中,我将尝试给你一个关于决策树算法如何工作的直觉。

本文将包括:

决策树算法工作流程的高层介绍:如果这个高层介绍不是那么清楚,也不用担心。接下来的部分是一个例子的详细解释。那就更清楚了。

决策树中决策背后的一个例子和数学计算:这部分将展示决策树算法利用数学计算做出的真实决策。

决策树介绍

决策树算法基于对特征条件的判定来工作。节点是对属性的条件或测试,分支代表测试的结果,叶节点是基于条件的决策。

作者图片

正如您在图中看到的,它从一个根条件开始,基于该根条件的决策,我们得到三个分支,C1、C2 和 C3。它不一定是三个分支。分支可能更多,这取决于根节点特性中的类的数量。一根树枝最后变成了一片叶子。叶节点意味着最终的决定或预测。根节点的 C1 和 C3 以条件 1 和条件 2 结束。

条件 1 和条件 2 是两种不同的功能。我们将根据条件 1 和条件 3 中的类别进一步拆分数据。上图显示条件 1 和条件 3 各有两个特征。和往常一样,类别可以不止这些。这是一个关于决策树如何进行预测的高级概念。我们将更详细地了解我们实际上是如何为根节点和其他条件选择特性的。

在深入研究之前,需要明确以下一些术语:

根节点:正如我已经讨论过的,根节点是决策树的起点。这是开始拆分数据的第一个功能。

决策节点:看上面的‘条件 1’和‘条件 3’节点。我们在基于根节点分裂之后得到那些节点,并且我们基于决策节点进一步分裂。

叶节点:这些是决策树做出最终决策的最终节点,不能再进一步拆分。在上图中,决策 2 和决策 3 是叶节点。

决策树中决策背后的示例和数学计算

这是我为这个例子创建的一个合成数据集。该数据集有一个二进制类和三个变量 N1、N2 和 N3。这三个变量都是绝对的。我采用了所有的分类变量,因为这样更容易解释。一旦你有了直觉,也就更容易理解连续变量的用途。

作者图片

我们有数据集。根据前面的讨论,我们需要一个根节点,它基于我们将要开始拆分的数据。但是我们有三个变量。如何决定根节点?我是否应该简单地从 N1 开始,因为它在数据集中排在第一位?

不完全是。为了理解如何做出关于哪个特征应该是根节点的正确决策,您需要了解信息增益。

基于称为信息增益的统计测量来选择根节点或第一测试属性。

总体而言,测试属性的选择取决于“纯度测量”。这些是纯度指标:

  1. 信息增益
  2. 增益比
  3. 基尼指数

在本文中,我们将详细讨论“信息增益”。

信息增益

想象一下 10 个朋友在一起。他们中的 5 个人想看电影,5 个人想出去。在这种情况下做决定真的很难。如果他们中有 8 到 9 个人想看电影,而只有 1 到 2 个人想出去,那就更容易做出决定。对吗?最好的情况是他们 10 个人都想要同样的东西。决策非常容易。一点都不混乱!。

同样在决策树中,如果所有的类都属于 yes 或 no,则数据集是最纯的。另一方面,如果类的 50%属于是,另 50%属于否,那么数据集是极不纯的。因为很难做决定。决定很不确定!

信息增益有助于衡量某一特征的不确定性的减少。这也有助于决定哪一个特征适合作为根节点。

信息增益的计算取决于信息。那也叫

信息的公式:

让我们看一个例子。

我将研究上面的虚拟数据集,找出哪个要素将成为该数据集的根结点。

现在,让我们计算一下虚拟数据集的信息。这个数据集总共有 14 行数据,8 个是类和 6 个否类。

所以,信息是:

信息出来是 0.985。这是母熵。

接下来,我们需要逐一计算三个变量 N1、N2 和 N3 的信息。

从 N1 开始:

我们需要通过分割 N1 数据集来计算分类样本所需的信息量:

首先,让我们分析一下 N1 的数据。有:

4 '雨天',其中 2 'Y '和 2 'N '类

4 'Sunny ',其中 2 'Y's 和 2 'N's class

6 '多云',其中 4'Y '和 2'N '级

基于 N1 对数据集进行分类所需的信息如下:

它给出 0.964。

Info _ gain(N1)= 0.985–0.964 = 0.02

因此,如果 N1 成为根节点,熵将减少 0.02。

同理,我们可以计算出 Info(DN2)和 Info (DN3)分别为 0.972 和 0.983。

所以,N2 和 N3 的信息增益是:

****信息 _ 增益(N2)= 0.985–0.972 = 0.012

****信息 _ 增益(N3)= 0.985–0.983 = 0.001

如果我们将 N2 或 N3 作为我们的根节点,熵将分别减少 0.012 和 0.001。****

根据上面的计算,信息增益最高的属性是 N1。所以,N1 将是根。

在这个阶段,根节点已经固定,决策树将如下所示:

作者图片

我们选择了根节点,并根据根节点分割数据。图中显示了数据与相应类别的精确划分。

接下来呢?

如你所见,当“下雨”或“晴天”时,熵值非常高。因为 Y 和 N 的数量相同。但是“多云”时熵更低。所以,在‘多云’的时候,类可以是 Y。我们可以根据 N2 和 N3 再次分割数据。为此,我们必须计算每个子节点“雨天”、“多云”和“晴天”的信息增益,以决定下一个特征出现在哪里。如您所见,对于每个部门,我们现在都有更小的数据集。

我不会在这里展示任何进一步的计算。本文的目的是给出一个关于决策树算法如何工作的概念。

哪里停止分裂

我们致力于根据特征分割数据。这个虚拟数据集只有 3 个要素。但是真实世界的数据集可能有更多的特征需要处理。如果我们有大量的特征,并且我们想要使用所有的特征来构建树,树将变得太大。这可能导致过拟合和长计算时间。

为了处理这个问题,在 scikit-learn 库的决策树函数中有一个 max_depth 参数。如果最大深度参数很大,树会更大。所以,我们可以选择树的深度。尽管你可以使用任何你选择的特征选择方法来为算法选择更少的特征。****

还有一个参数, max_features 。参数的名称说明了它的作用。您可以指定要用于树的最大要素数。

其他参数请参见文档

决策树算法的主要优势

  1. 从我们处理的例子中可以看出,在某种程度上,它给了你一个关于预测如何发生的清晰的可视化。因此,更容易向利益相关者或客户解释。
  2. 不需要特征缩放。
  3. 因变量和自变量之间的非线性关系不会影响决策树算法的性能。
  4. 决策树算法可以自动处理异常值。
  5. 自动处理缺失值。

决策树算法的主要缺点

  1. 如前所述,存在过度拟合的主要风险。这也可能导致高方差,从而导致预测中的许多错误。
  2. 决策树算法很容易不稳定。数据中的一点点噪声或添加一个额外的数据可能会使整个树不稳定,或者可能会重新构建树。
  3. 决策树算法不适合大型数据集。如示例部分所述,如果数据集太大,算法可能会变得太复杂,并可能导致过拟合问题。

结论

Python 和 R 对于所有流行的机器学习算法都有非常好的包。所以,现在工作很轻松。但是了解背后的机制会让你更好地为你的项目选择正确的机器学习算法。

更多阅读

** https://pub.towardsai.net/dissecting-1-way-anova-and-ancova-with-examples-in-r-a3a7da83d742 **

统计学基本概念的简单解释(第一部分)

原文:https://towardsdatascience.com/simple-explanations-of-basic-statistics-concepts-part-1-bba66a1761d7

简单明了地解释不同的统计概念

马库斯·温克勒在 Unsplash 上的照片

介绍

我的教授曾经提到过,如果你能向一个完全不熟悉的人解释一个术语,并让他们理解这个概念,那么你就被认为对这个学科很了解。这种方法也是我每当学习新东西时采用的方法。当我在复习面试问题时,我意识到实践这种积极的学习方式是多么重要。

统计学有很多定义和很多知识要涵盖。这就是为什么我认为做评论的最好方法是写下来并向人们解释。带着这个想法,本文将讨论一些基本的统计学概念,我相信任何统计学学习者都应该熟悉这些概念。

人口和样本

假设我家拥有一个柠檬农场。当收获季节到来时,我们想知道柠檬的*均大小。农场里有成千上万的柠檬,当然,我们无法测量或称量所有的柠檬。因此,我们决定对它们进行采样,并从每个样本中获取*均值。

农场里所有的柠檬被称为群体,每一组被采摘的柠檬就是一个样本。

样本量

这是你的样品中柠檬的数量。样本量根据不同的标准确定,如研究范围、人口可变性或研究方法。

作者图片

样本错误

从样本中,我们可以得出关于人口的结论。这样的发现被称为推论。然而,我们可能知道,样本永远不会是总体的完美代表,每个样本都会有不同的结果。

抽样总是会有变化。例如,在同一个农场采摘的三组柠檬可能有三种不同的*均重量。这叫做抽样误差。

样本框

样本框架可以理解为将从中抽取样本的群体个体的列表。理想情况下,这个列表应该包括你想研究的全部人群。样本框目标人群的区别在于人群广,框窄。

例如,我想研究 BC 公司的工作满意度。BC 公司的员工总数是研究的总人数。理想情况下,我的抽样框架将包括所有员工的姓名和信息。

作者图片

样本空间

一个样本空间包含了一个实验的所有可能结果。假设您有一个 BC 公司员工的抽样框架,用于选择最佳支持团队的调查。样本空间,在本例中,是您的调查中所有可能的回答:销售团队 1、销售团队 2、人力资源、财务等。

取样方法

同时,从人群中抽取样本的行为被称为抽样

抽样方式有两种:概率抽样&非概率抽样。然而,在这篇文章中,我将只讨论概率抽样,因为它更为人们所熟悉,也更倾向于在研究中使用。

简而言之,概率抽样是一种技术,研究人员会预先定义一些标准,然后从人群中随机选择个体。按照这些标准,所有成员都有*等的机会被纳入样本。概率抽样主要有四种:

作者图片

1.简单随机抽样

这是最好的采样方法之一,因为它有助于节省时间和资源。在简单的随机抽样方法中,抽样框应该包含整个群体,这样群体中的每个个体都有*等的机会被选中。

例如,BC 公司希望从他们的 1000 名员工中选择 50 名员工进行调查。他们给每个雇员分配一个从 1 到 1000 的数字;然后,他们从 1000 个号码中随机选择 50 个号码。

2.系统抽样

这种抽样方法很简单,类似于简单的随机抽样,但不同的是,个体是以固定的间隔挑选的,而不是随机产生的。

继续使用前面的示例来选择调查的员工。假设数字按降序排列,我们首先随机选择一个起点,例如,雇员号 2。然后,从数字 2 开始,选择大于先前选择的 2 个单位的数字。这意味着员工编号 2,4,6,…将被选中,直到有足够的 50 人。

3.巢式抽样法

现在,假设 BC 公司在全球有 50 个分支机构,拥有数千名员工;公司的高层管理人员不能访问每一个办公室。于是,他们决定随机选择十个城市进行走访,这十个城市对应十个集群。

这种方法产生样本误差的可能性更大,因为在不同的聚类之间可能存在显著的差异。此外,样本聚类不一定能很好地代表整个群体。

4.分层抽样

根据收入、性别或受教育程度等类似特征,总人口被分为不同的亚组(阶层)。然后,我们决定从每组中抽取多少人的样本,并进行取样。

分层抽样的一个明显的缺点是,在确定阶层时,选择偏差的机会更大。这种偏差的发生是因为我们在划分子群之前可能已经有了群体共享模式的先验知识或信息。

置信区间

正如我上面所说的,在抽样中总会有样本误差,因为没有样本可以完美地代表整个人口。那么,我们如何得到总体参数的结果呢?这时术语置信区间就出现了。

置信区间有助于表达对总体参数的估计。换句话说,它描述了我们的参数准确的可能性。

作者图片

在统计学中,信心是概率的另一种表达。例如,假设您建立了一个置信度为 95%的置信区间。在这种情况下,您确信估计值在 100 次中有 95 次位于置信区间给出的上限值和下限值之间。

让我们回来计算农场里柠檬的*均重量。假设我们取 10 个柠檬样品,并计算样品的*均重量。结果是 20 克。

再取一个样品,继续计算样品的*均重量。重复该过程,直到我们计算出许多*均重量,比如 100 个*均重量。在 100 次计算中,有 95 个数值在 20g-40g 之间。因此,我们有 95%的把握认为柠檬的*均重量在 20 克到 40 克之间。

作者图片

长话短说,95%的区间就是覆盖均值 95%的区间。所以,任何超出置信区间的事情只发生不到 5%。这也意味着真实均值落在这个区域的概率是< 5%。

什么影响置信区间的宽度

影响置信区间宽度的因素有两个:总体内的变异样本量。

想象一下柠檬农场,那里所有的柠檬都有相同的重量,我们从这个群体中抽取的样本应该有相似的重量。因此,样本之间的*均权重相似,导致置信区间变窄。然而,在另一种情况下,当农场中柠檬的重量之间存在大的差异时,样本的*均重量之间可能存在巨大差异,导致*均重量的宽置信区间

作者图片

如果我们取一个小样本,信息将是不准确的,因为从几个值中没有多少信息可以反映。因此,样本之间产生较大差异的几率会更高,从而导致更宽的置信区间。

另一方面,在大样本的情况下,获得样本误差的影响减少了,因为从中获得了更多的信息。因此,置信区间将会更窄。

如何计算置信区间

有几种方法可以计算置信区间:

  • 拔靴带
  • 非正式的
  • 传统的基于正常的

不过每种方法的细节会在后面的帖子里提到。

结论…

以上是对统计学中一些基本概念的一些解释。当然,还有更多的内容需要介绍,我会在接下来的文章中继续分享。我希望我已经把它们说得更清楚了。谢谢你一直读到最后。

参考

https://www . scribbr . com/statistics/confidence-interval/#:~:text = A % 20 confidence % 20 interval % 20 is % 20 the,another % 20 way % 20 to % 20 description % 20 probability

用 Python 实现安德鲁斯曲线的简单介绍

原文:https://towardsdatascience.com/simple-introduction-to-andrews-curves-with-python-e20d0620ed6b

一种有效的多维数据可视化方法

Armand Khoury 在 Unsplash 拍摄的图片

背景

安德鲁斯曲线,也称为安德鲁斯图,是一种可视化技术,用于识别多维数据中的潜在结构。与主成分分析和 T 分布随机邻居嵌入类似,安德鲁斯曲线将多个特征减少到低维空间,以显示与目标变量或分类的关系。安德鲁斯曲线的工作原理是将单次观察或一行数据的所有特征映射到一个函数上。该功能定义为:

按作者分类的图像-f(x)表示函数输出的总和,而 x1、x2、x3、x4、x5、xn…表示单个观测值或数据行的 n 个要素或列以及相应的映射函数

生成的函数输出创建了一条唯一的曲线,保留了每个函数系数的所有要素之间以及观测值之间的相对距离和方差。思考安德鲁斯曲线的另一种方式可能是*行坐标图的变换版本。安德鲁斯曲线结合了多种观察结果,并通过目标可变颜色映射进行区分,突出了类别之间的潜在聚类或类别之间的分离。

按作者分类的图像-带有 Iris 数据集的示例 Andrews 曲线,其中要素以-pi 到 pi 的比例映射在 x 轴上,生成的函数输出值绘制在 y 轴上。注意类内的相似性和类间的差异。在一个分类模型中,哪一个最能准确地区分?

要了解更多关于安德鲁斯曲线和上面提到的其他多维可视化工具,请查看下面的链接

https://en.wikipedia.org/wiki/Andrews_plot https://www.mikulskibartosz.name/looking-for-structure-in-data-andrews-curves-plot-explained/ https://www.geeksforgeeks.org/difference-between-pca-vs-t-sne/

在下面的段落中,我们将讨论如何为安德鲁斯曲线准备数据,并测试一些绘图方法。

准备数据

如上图所示,本教程使用 Iris 数据集。我们可以用熊猫来读取 UCI 机器学习库中的数据。

让我们来看看我们正在使用的一些功能。

为了快速有效地为安德鲁斯曲线准备数据,我们可以使用 acdata 库。acdata 函数需要的三个参数是:

  • 数据=虹膜
  • class_column = 'Class '
  • 样本= 1000

acdata 函数输出一个熊猫数据帧,该数据帧包含创建完整的安德鲁斯曲线图所需的所有组件,同时保留原始特征数据中的信息。点击了解更多关于 acdata 库的信息。

绘制安德鲁斯曲线

可视化我们的数据的一个有用的方法是通过 Plotly 的交互式绘图库。通过 Plotly,我们创建了带有 x = 't'y = 'f(t)' 的曲线。为了展示类分布,我们设置 color = 'Class' 。对于特征信息,我们设置 hover_data = ['Sepal_Length ',' Sepal_Width ',' Petal_Length ',' Petal_Width'] 。x 轴是从-pi 到 pi 的范围,并且线性间隔 t,f(t)表示 y 轴上 t 的系数。

Seaborn 的数据可视化库提供了另一种创建高质量安德鲁斯图的方法。对于 Seaborn,我们设置了 x = 't'y = 'f(t)'hue = 'Class' 。我们的 seaborn 图创建了误差带来清理观察值之间的分布,并更好地区分类别。

绘制安德鲁斯曲线的其他方法包括 pandas 绘图函数和 hvplot,它们内置于库中。您可以查看以下选项:

结论

安德鲁斯曲线提供了一种简单而有用的方法来可视化与目标变量或分类相关的高维数据中的结构。本教程提供了安德鲁斯曲线的简要背景,并展示了准备和创建有效的绘图简单方法。希望这对您的数据科学之旅有所帮助!

感谢您的阅读!

https://medium.com/membership/@cole-hagen

作者的类似文章—

R 中二分变量的简单 Logistic 回归

原文:https://towardsdatascience.com/simple-logistic-regression-for-dichotomous-variables-in-r-8befbe3e95b4

R 系列中的统计

图片来自 Unsplash

一、简介

L 逻辑回归是一个基本的统计概念,通过它可以在分类变量之间进行回归分析。很多时候,变量的序数值不一定代表任何数字,而是代表一个类别。我们以前讨论过简单线性回归和多元线性回归。在本文中,我将介绍 R 中逻辑回归的实现,并解释结果。

有时,我们的变量只能采用二进制类型的值,例如性别、就业状况和其他是/否类型的回答。此外,我们可以有两个以上的类别。在简单逻辑回归中,我们有一个二元因变量和一个连续或分类自变量。我们可以利用线性回归来预测二元因变量,但有几个限制。我们将在这里讨论这些假设。

假设违反

线性回归的第一个假设是数据的正态性。在简单线性回归中,我们假设因变量在*均值与中值重叠的地方呈正态分布。在逻辑回归的情况下,因变量有二分输出。这意味着它远非正态分布。事实上它遵循伯努利分布。这意味着我们不能利用最*的创造来预测一个二元变量。

使用线性回归来预测二元变量的另一个限制是违反了同方差假设。在这种假设下,只有预测变量的值的误差方差被认为是一致的。但事实并非如此。在这个逻辑回归中,误差的方差取决于捕食者变量的值,这个变量被称为异方差。

最后,我想指出的是,用这种方法做逻辑回归,也违反了线性假设。我们无法获得二分变量和线性连续变量之间的线性关系。事实上,这种关系遵循 S 形曲线,这是逻辑回归的标志,如下所示。该曲线表明,响应变量只能取两个级别的值。如果这里应用线性回归,有时结果可能小于 0 或大于 1,这违反了概率论的基本假设。

解决方案

为了避免上述违规,当响应变量为二元时,我们需要使用逻辑回归而不是线性回归。我们需要对结果变量的成功概率进行逻辑转换。简单逻辑斯谛方程的最简单形式是

其中 p 是结果变量等于 1 的概率。Logit(p)表示成功概率的逻辑转换。在右边,形成非常类似于线性回归。此逻辑转换方程和简单线性回归之间的基本区别在于,我们不是直接计算响应变量,而是测量响应变量的成功概率。由于这也产生了与成功几率相同的氛围,等式的左边可以改写如下。

因此,“logit”是“成功几率的自然对数”。

逻辑回归背后的理论已经在上面简要讨论过了。接下来我将在 r 中实现一个逻辑回归的例子,并解释所有的输出以获得洞察力。

数据集

为了进行演示,我将使用 2016 年收集的综合社会调查(GSS)数据。这些数据是从宗教数据档案协会下载的,由 Tom W. Smith 收集。该数据集收集了* 3,000 名受访者的回答,并包含与若干社会经济特征相关的数据。例如,它有与婚姻状况、教育背景、工作时间、就业状况等相关的数据。让我们深入这个数据集,以便更好地理解它。

GSS 2016 年数据[图片由作者提供]

学位列提供每个人的教育水*值,MADEG 提供每个母亲的教育程度。我们的目标是找出母亲的本科教育水*是否是孩子本科教育水*的良好预测指标。数据集中的分类数据按顺序编码。

学位数据[图片由作者提供]

MADEG 数据[图片由作者提供]

让我们导入 R 中的数据,并利用 glm()命令来回答我们的问题。我们需要稍微修改一下我们的数据集。对于所有不到学士学位的参与者,标记为 0,其他标记为 1。同样的特征工程是在母亲的教育水*上完成的。新列被重命名为 DEGREE1 和 MADEG1。

r 码[图片由作者提供]

R 中的输出窗口[图片由作者提供]

在这里,上面的输出窗口非常类似于下一篇文章中讨论的线性回归,我们使用了 lm()函数。

结果解读

如前所述,要实现逻辑回归,我们需要将输出成功的概率转换为对数度量,然后可以确定预测变量和截距的系数。解释如下。

  1. 从输出窗口中,我们观察到存在类似于线性回归情形的残差。截距的估计值为 0.257,MADEG 的系数估计值为 0.316,这基本上告诉我们,预测变量(即母亲的教育水*)每增加一个单位,孩子的教育水*值为 1 的 logit 概率就增加 0.31598(这仍然是一个正斜率,表明响应变量随着预测变量的增加而增加)。换句话说,母亲的学士学位增加了孩子获得学士学位的可能性。
  2. 相关的 p 值小于 0.05,这也告诉我们拒绝零假设。这里的零假设是“预测变量的系数为 0,基本上不影响响应变量”。因此,我们可以得出结论,母亲的学士学位教育显著影响孩子的学士学位。至少数据是这么告诉我们的。
  3. t 值的计算方法是将估计值除以标准误差。最后,零偏离值显示了零模型的偏离,其中我们只有截距。剩余偏差是指偏差定义为

剩余偏差= -2(当前模型的对数似然性-饱和模型的对数似然性)

零偏差和剩余偏差之间的差异用于确定当前模型的显著性。

结论

我们已经讨论了简单的逻辑回归及其在 R 中的实现。我们还浏览了 R 的输出并解释了来自一般社会调查的结果。预测变量的正系数表明,随着母亲的学士学位值从 0 增加到 1,孩子的学士学位变为 1 的概率增加了 0.31598,或者换句话说,可以得出结论,在我们的数据集中,母亲的教育显著影响孩子的教育。

感谢阅读。

网站:从数据中学习

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

R 中序数变量的简单逻辑回归

原文:https://towardsdatascience.com/simple-logistic-regression-for-ordinal-variables-in-r-1d95fa278c5e

R 系列中的统计

苏珊·Q·尹在 Unsplash 上的照片

简介

当我们处理序数变量时,逻辑回归变得相当有趣。与二元变量不同,现在我们有顺序预测变量和响应变量。我们可以有这样的情况,预测变量是有序的,响应变量是二进制的,反之亦然。有序变量是指具有有序数据的变量。例如,我们可以将教育水*数据分为“高中”、“学士”、“硕士”和“博士”。这些数据以有序的方式显示了几个级别的可能分类值。在本文中,我们将深入研究序数变量的简单逻辑回归。

在以前的文章中,我们已经经历了简单的和多重的逻辑回归。读者可以看看这些文章,以便更好地理解逻辑回归。

https://mdsohel-mahmood.medium.com/multiple-logistic-regression-in-r-aa65c2a91e22

数据集

这里,我们将使用来自 UCI 机器学习知识库的 成人数据集 。这个数据集有超过 30000 个人的人口统计数据,包括种族,教育,职业,性别,每周工作时间,收入水*等。

成人数据集 来自 UCI 机器学习资源库

为了进行有序逻辑回归研究,我们需要对给定的数据稍加修改。但首先,我们提出一个学习问题。

受教育程度对收入水*有什么影响?

要回答这个问题,我们需要教育和收入水*的标签编码数据。给定数据集的教育水*从一年级一直到博士学位。收入水*是二元的,提供了个人收入是否超过 50000 美元的信息。因此,我们有序数预测变量和二元反应变量。让我们在 r 中执行分析。

链接到 github 中的 excel 文件(成人— v2 —适用于 github.xlsx)

在 R 实施

我们首先完成了“教育标签编码”专栏。由于这是一个序数数据,我们需要设置适当的顺序,并设置数值。顺序和设定值如下所示。我们将使用 Education_code 专栏,并通过利用它来尝试回答手头的问题。我们将看看这种教育水*对收入是否有任何影响。

我们的收入数据是二进制的。这意味着我们只有两个级别:收入> 50000 美元和收入≤50000 美元。我们还将相应的数据编码为 1(收入> 50000 美元)和 0(收入≤ $50000 美元)。

在 R 中,我们将首先读取修改后的数据,并将所需的列传递给 clm()函数。

这里,我们使用了 clm()函数来代替 glm()。clm()代表累积链接模型,我们需要安装“顺序”包来使用 clm()函数。

结果解读

为了阐明研究顺序,我们用不同的顺序变量组运行了三个相似的模型。

  1. 模型 1:如上所述,它具有代表不同教育水*的序数预测变量“教育代码”。它还有二进制响应变量“Income_greater_than_50k_code ”,我们通过将最低值分配给收入类别≤$50000,最高值分配给收入类别> $50000,使其成为序数。
  2. 模型 2:该模型有二元预测变量“单身汉”(如果个体有单身汉,赋值为 1,否则为 0)。响应变量与模型 1 相同。
  3. 模型 3:该模型具有连续的预测变量“Education _ yrs ”,这是一个数字变量,响应变量与之前的模型相同。

我们将运行我们的数据集,解释每个案例的结果,并进行比较。

模型 1 结果

模型 1 结果

clm()的输出窗口与 glm()函数略有不同。第一部分显示了链接函数、阈值选项、观察次数、对数似然值、AIC 统计、迭代次数、对数似然函数的最大绝对梯度和 Hessian 条件数。

“教育 _ 代码”的系数估计值为正,值为 0.562。其次,个人教育水*每增加一个单位,我们就有相关的 p 值< 0.05 and it indicates statistically significant data for the predictor variable.

Based on these figures, it can be concluded that there is a 0.562 increase in the logit or log odds of income level being 1 (i.e. income >【50000 美元】。

模型 1 结果

关于伪 R 值,我们的麦克法登值为 0.098,稍后我们将比较其他模型。还将比较 AIC/BIC 的统计数据,因为单个模型的单个值在逻辑回归中没有太大意义。

模型 2 结果

模型 2 结果

“单身汉”列数据是二元的,它类似于我们之前做的简单逻辑回归。根据这些数字,我们可以得出结论,个人教育水*每增加一个单位,收入水*的对数或对数优势就增加 1.567。因此,拥有学士学位比仅仅提高教育水* 1 级(来自模型 1)有更大的影响。如果个人拥有学士学位,相关的 p 值也是< 0.05.

Model 2 result

Regarding the pseudo R² value, we have McFadden value of 0.087 which is smaller than model 1. So, model 2 implies lesser significance for the predictor variable. What I mean is Bachelor column’s data have less significant impact on the income data than the ‘Education_code’ data which represents the ordinal values for education levels.

AIC/BIC statistics are also higher in model 2 which also indicates more robustness for model 1.

Since both the predictor and the response variable are binary, it makes more sense to use glm() function here. But this glm() should be run before factoring the variables.

Model 2 result using glm()

Model 2 result using glm()

Using glm() function, the coefficient becomes 0.314 which implies logit increase of 0.314 for income having >50000 美元。麦克法登的伪 R 值是 0.0978。AIC/BIC 值高于模型 1(类似于模型 2 的 clm()结果)。

模型 3 结果

模型 3 结果

“Education_yrs”列数据在模型 3 中是连续的。根据这些数字,我们可以得出结论,个人受教育年限每增加一个单位,收入水*的 logit 或 log 比值就会增加 0.351。相关的 p 值也是< 0.05.

Model 3 result

Regarding the pseudo R² value, we have McFadden value of 0.106 which is higher than model 1 and model 2.

AIC/BIC statistics are also smaller in model 3 which also indicates more robustness for this model.

结论

我们已经经历了三个模型,其中包括顺序预测变量和二元反应变量的逻辑回归。第一个模型有顺序教育变量和二元收入变量。第二个模型包含二元教育变量和二元收入变量。第三个模型有连续教育年限变量和二元收入变量。所有这些模型都根据伪 R 和 AIC/BIC 统计进行了比较。读者也可以将模型扩展到序数反应变量。

数据集的确认

Dua,d .和 Graff,C. (2019)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

感谢阅读。

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

基于 Yelp 开放数据集的 TF-IDF 主题建模的简单方法

原文:https://towardsdatascience.com/simple-method-of-targeted-tf-idf-topic-modeling-using-yelp-open-dataset-298e019d6c09

制造环境中的简单概率推理

原文:https://towardsdatascience.com/simple-probabilistic-inference-in-a-manufacturing-context-b0f22f7b9a9a

TL; 本文将概率推理应用于一个由来已久的机械工程问题。如果你不太关心理论和认识论(我们是如何来到这里的),那么只需阅读问题,并着手将先验知识拟合到贝塔分布。如果你是在更大的冲题,只要看完题 打开模型 就能算出来。

一个人如何从数据中学习?随着如此多的业务环境中数据的爆炸式增长,数据科学不再是一门可选的学科。随着先进的统计方法打包在漂亮的库中,并被商品化用于机器学习,我们很容易错过 概率论 的基础,这是我们如何从数据中学习的核心。那些基础有 250 多年的历史了,既直观又有哲理。了解这些基础有助于我们成为数据科学、机器学习和实验的更好的实践者。

为了做到这一点,我将利用我最*在我的母校班加罗尔理工学院主持的 R. Venkatram 博士纪念讲座系列下讲授的题为“概率的魔力”的研讨会(英语和卡纳达语的公共领域双语幻灯片此处)。参与者大多是工程所有学科的高级教师。作为深入研究的一部分,我要求有人自愿回答一个推理问题。机械工程的教授给了我一个很好的例子,这篇文章的目标是用这个例子来说明简单的概率推理。您可以轻松地用数据丰富的上下文替换制造示例。当将其扩展到 AB 测试时,参见本文。

问题:制造的组件不合格

正在制造的特定组件有一个验收公差,大约高于或低于 25 毫米 0.5 毫米。如果超出此范围,该组件被称为不合格,并从该批次中拒收。管理制造车间的人认为,他们将在每批中看到 4%到 6%的不合格零件。当他们运行下一批并计算不合格零件的数量时,他们应该如何更新他们对不合格零件的看法?制造的下一个零件不合格的概率是多少?他们认为低于不合格水*上限的概率是多少?为了获得低于不合格水*上限的 90%置信水*,应该运行多少批次?此外,他们应该预测什么样的年营业利润,达到目标营业利润的概率是多少?

经典概率论来拯救

1763 年,一段伟大的友谊改变了人类推理的方式。托马斯·贝叶斯牧师两年前去世了,留下了他未发表的关于概率的著作。他亲爱的朋友、数学家理查德·普莱斯发表了这部著作,名为《解决机会主义中的一个问题的论文》。

(左)托马斯·贝叶斯牧师,公共领域 |(右)贝叶斯的朋友托马斯·普莱斯,公共领域

这项工作取得了两项重要进展。第一个是从条件概率推导出贝叶斯定理。第二个是 Beta 分布的引入。皮埃尔·西蒙·拉普拉斯独立地提出了同样的定理和贝塔分布,还提出了很多我们今天称之为概率论的东西。

贝塔分布以掷硬币的比喻开始,它只有两种可能的结果,正面或反面。因此,在 n 次试验中 k 次成功的概率由二项式分布给出(bi =“二”)。这在贝叶斯和拉普拉斯出现之前就已经知道了。他们从离散的二项式分布中获得灵感,并通过应用微积分,把它带到了连续分布领域。他们保留了 k 和 n,但将其称为 alpha(或成功次数=k)和 beta(或失败次数= n-k),这成为了这种新分布的形状参数,他们将其称为“Beta”分布。

设置为(α= 1,β= 1)的β分布产生均匀分布。作者制作的图像。

关于这种分布的惊人之处在于,它正在以一种符合我们常识的方式改变形状。如果你开始你的概率推断,相信你在两次试验中只看到一次成功,那么α= 1,β= 2–1 = 1。β(1,1)实际上是均匀分布。改变α和β会改变分布,帮助我们表达不同的信念。

变形贝塔分布,公共域

贝叶斯“带着很大的疑问”提出均匀分布是先验概率分布,以表示对正确先验分布的无知。拉普拉斯没有这样的犹豫,并断言当我们觉得每种结果都有相同的可能性时,这就是要走的路。此外,拉普拉斯提供了一个继承规则,基本上是说我们 必须 使用分布的*均值来为下一个硬币落地头(或者下一次试验被认为是成功的)设定一个概率。

拉普拉斯的巨著《事件起因的概率回忆录》对他的影响如此之大,以至于大多数西方人不再关注概率论,认为在那里已经没有太多东西可以推进了。俄罗斯人没有得到那个备忘录,所以他们继续思考概率,产生了像马尔可夫过程这样的基本进展。

然而,就我们的目的而言,经典概率的下一次大飞跃是 E. T .杰恩斯的工作,随后是罗纳德. a .霍华德的工作。在我们去那里之前,你注意到一个重要的细节了吗?本部分第一个图表的 x 轴表示“长期人头比例”,而不是“人头概率”。这是一个重要的细节,因为不可能有一个概率的概率分布——那是不可解释的。这种想法从何而来?

像贝叶斯一样,杰恩斯的开创性工作在他有生之年从未发表过。他的学生拉里·布雷特索斯特在杰恩斯去世后出版了他的著作《概率论:科学的逻辑》。杰恩斯的课堂笔记对我的老师罗纳德·a·霍华德的工作产生了巨大的影响,他是决策分析的共同创始人。

杰恩斯介绍了推理机器人的概念,它将使用我们会同意的逻辑原理。他在上述书中写道:“为了将注意力引向建设性的事物,远离有争议的不相关事物,我们应该发明一个虚构的存在。它的大脑是由我们设计的,这样它就可以根据某些明确的规则进行推理。这些规则将从简单的欲望中推导出来,在我们看来,这是人类大脑所需要的;也就是说,我们认为,一个理性的人,在发现他们违反了这些欲望之一时,会希望修正他们的想法。”

“我们的机器人将对命题进行推理。如上所述,我们将用斜体大写字母{A、B、C 等}来表示各种命题。},目前我们必须要求所使用的任何命题必须对机器人具有明确的意义,并且必须是简单、明确的逻辑类型,必须是真或假。”

杰恩斯的机器人是霍华德的“千里眼”的祖先,这是一种想象中的生物,它不理解模型,但可以回答关于未来的实际问题。言外之意:我们只能把概率放在清晰的区别上,而其中没有一丝不确定性。在一些早期的著作中,你会看到贝塔分布被公式化在“正面概率”上“正面概率”的概率分布无法以任何有意义的方式解释。因此,罗纳德·霍华德在他 1970 年的开创性论文《推理的视角》中提供的编辑是将这种区别重新定义为长期的分数(或成功),这是一个千里眼可以回答的问题。

贝塔分布有一个非常有趣的性质。当我们发现更多的证据时,我们可以简单地更新α和β,因为它们对应于成功的次数和失败的次数,以便获得关于感兴趣的区别的更新的概率分布。下面是 alpha 和 beta 不同配置的简单示例(S =成功次数,N =投掷次数):

根据观察更新 Beta(1,1)先验,图片由作者创建。

我们可以用这个分布来做我们的推论。我准备了一个公共领域 Google sheet ( 美版印度英文版印度卡纳达语版),你复制后可以玩玩。我将用这张纸来解释其余的理论。

最后,决策分析推理需要的最后一个主要进步是霍华德的个人概率概念。他在 1966 年的经典论文决策分析:应用决策理论中介绍了这一点。[2]虽然大多数人认为这篇论文是决策分析领域的开山之作,也是“决策分析”这个术语的第一次出现,但它对概率论的贡献并没有得到广泛的重视。在那之前,概率是一种学术追求,而不是用于专业决策。

这篇论文提出了一个关键的观点——概率的构造应该用来捕捉你对不确定性的“感觉”。在第 104 页,Howard 讨论了为什么人们以前没有听说过决策分析。其中之一是“概率是一种精神状态,而不是事物状态,这一概念直到现在才在思想世界中重获其应有的位置。”在论文中,霍华德花了很大的篇幅展示了构建良好先验的严谨性,他称之为“几乎是心理分析的先验测量过程”,当决策者说“是的,这是我真正相信的”时,它的有效性就成立了。在最*与他的一次谈话中,他更喜欢“个人概率/先验”这个术语,而不是“精神分析先验”,我也是这样。

用先验知识拟合贝塔分布

还记得我们从不符合项的分布(4%到 6%)开始的吗?作为读者的练习,请参考贝塔分布的均值和方差,并使用均值和方差推导出阿尔法和贝塔的公式。

前两个来自维基百科,第三个和第四个来自基础代数,由作者创建的图像。

我们如何找到我们先前评估的均值和方差?我们评估了来自机械工程专家的以下百分位数/概率对(个人先验):

来自电子表格的快照,图片由作者创建。

上面的解释是,不符合率低于 4%的几率只有 10%,高于 6%的几率有 10%。高于或低于 5%的可能性各占一半。经验法则是将 25%/50%/25%分配给第 10/50/90 百分位。如果你想了解更多关于它背后的理论,请看[1][3][4]。这个快捷方式使我们可以很容易地计算*均值:

*均值= 25% x 4% + 50% x 5% + 25% x 6% = 5%

快照来自电子表格,图片由作者创作。

类似地,我们可以使用标准公式计算方差,得出以下 alpha 和 beta 形状参数。

正如您所看到的,工作表显示了相同数量的成功和失败。提供一个 4%-5%-6%的输入作为我们的先验信念,就等于说,“我们有一个信念强度,相当于在 949 次投掷中看到 47 次成功。”这个框架允许我们的专家反复检查 949 次投掷中的 47 次是否对他们有直观意义。

我们还可以将拟合的 beta 分布离散化,并与原始输入进行比较,如下所示。

将拟合的分布与原始输入进行比较,快照来自电子表格,图片由作者创建。

用观察值更新

现在我们有了先验,我们可以很容易地用我们的观察来更新它。我们提出以下问题:

观察输入,来自电子表格的快照,由作者创建的图像。

新的 alpha(成功)和 beta(失败)参数分别是前一个 alpha 和新的成功以及前一个 beta 和新的失败的总和。这在下面的部分中单独显示:

模型中显示 beta 版更新的部分,来自电子表格的快照,由作者创建的图像。

这现在可以用不同的方式来形象化。首先,我们可以看到离散化的后验分布,并将其与输入进行比较:

将后验与前验进行比较,快照来自电子表格,图片由作者创建。

我们看到后验分布左移。我们也可以在下面的图像中看到这一点:

快照来自电子表格,图片由作者创作。

回答概率问题

首先,通过拉普拉斯继承规则,我们可以回答这个问题:下一个组件不符合的概率是多少?

快照来自电子表格,图片由作者创作。

这是通过简单地将后验成功次数(后验α)除以后验试验次数(后验α+后验β)或后验分布的*均值得出的。

接下来,我们想知道低于目标不合格水*的概率。由于我们有了后验累积分布,我们可以很容易地阅读它来回答概率问题。让我们通过读取相对于目标水*的累积分布函数来实现。在下面的例子中,我们可以对先验和后验进行读出。

低于不合格目标的概率,测试当前批次之前和之后,来自电子表格的快照,由作者创建的图像。

如我们所见,我们的观察使我们对低于不合格水*更有信心。注意,这个推论就其本身而言是好的。这里可以提出的一个批评是,我们从表面上看所有的数据,而没有考虑这些数据可能出现的更广泛的背景(例如,一年中我们会看到多少批次?)使用这些信息会让我们引入一种后验标度能力(也称为数据标度能力),这种能力会缓和我们从数据中得出的推论。

后验规模功效或数据规模功效可以被认为是以下问题的答案:“我需要在该测试/批次中看到多少次试验(成功)才能被认为是一次试验(成功)?”默认情况下,工作表已将数据比例设置为 1,这意味着所有数据都被视为真实值并得到充分利用。这样做的问题是,我们可以太快下定决心。数据标度幂为 10,这意味着我们将每 10 次试验视为 1 次试验,每 10 次成功视为 1 次成功,这将立即改变我们的结论。正如我们在下面看到的,指针几乎不会从先前的位置移动,因为我们现在将 1000 次试验中的 30 次成功视为 100 次试验中的 3 次成功(除以 10)。

从分布中读取概率,从电子表格中获取快照,由作者创建图像。

看了上面,我们会很快意识到,为了获得更多的信心,我们需要运行更多的批次,这是应该的。假设我们运行了 5 批,每批 1000 个组件,在 1,000 次试验中,我们看到了 30 次成功的相同比例,只是在 1,000 次试验中,我们看到了 30 x 5 = 150 次成功。我们现在看到接* 90%的置信度,我们将低于 5%的目标不合格水*。

快照来自电子表格,图片由作者创作。

现在,一个关键问题是:什么是设定数据规模功效的原则性方法?假设我们希望预测每年都有效。我们可以使用的一个原则是用于推断的生产批次在一年中生产的总批次中所占的比例。假设我们的计划是制造 50 个批次,我们已经使用了 5 个批次进行推断。然后,我们可以将数据比例功效设置为 50/5 (=10)。另一种解释数据规模威力的方法是,我们必须将数据稀释 10 倍,以便对全年进行解释。

现在让我们转向关于运营经济学的最后一个预测问题。

经营经济学

在我们已经完成的预测工作的基础上建立一个经济模型是非常容易的。通过将每个组件的价格和成本、一年中要处理的批次数量以及每批中的组件数量作为输入,我们可以通过将制造的组件总数(例如 50,000)乘以我们在上一节中生成的 NC 后验分布中的每一项来获得不合格组件数量的分布。然后,我们可以通过将 NC 预测乘以每个组件的成本来直接计算损失分布。通过计算每个合规组件的净收入并减去不合规组件的损失,也可以很容易地计算出营业利润。

预测营业利润。还有:美版卡纳达版。快照来自电子表格,图片由作者创作。

此外,如上面的屏幕截图所示,我们可以计算超过目标运营利润的概率,这与低于该利润目标的隐含不符合率的概率相同,这是我们从上一节中的后验(不符合)累积密度函数中读取的。

其他想法

这是一个简单的模型,展示了我们如何开始在预测中使用概率。这种方法有局限性,其中一个重要的局限性是,我们考虑的是价格、成本、批量运行次数以及固定生产的组件数量。这些可能都是不确定的,当这种情况发生时,模型必须变得更加复杂。读者可以参考龙卷风图工具来制作更复杂的经济模型,处理多因素的不确定性。

此外,贝塔-二项式更新模型只有在我们假设零件制造过程稳定时才有效,这意味着没有漂移。统计过程控制领域[5]陷入了困境,这超出了本文的范围。

感谢 Brad Powley 博士审阅本文,并感谢 Anmol Mandhania 提供的有益评论。错误是我的。

参考

罗纳德·霍华德,《决策分析的基础》IEEE 系统科学与控制论汇刊 4,第 3 期(1968):211–219。

[2]霍华德,罗纳德 A. 决策分析:应用决策理论 。斯坦福研究所,1966 年。

[3]米勒三世、艾伦 c .和托马斯 r .赖斯。"概率分布的离散*似值."管理科学 29,第 3 期(1983):352–362。参见第 8 页。

[4]麦克纳米、彼得和约翰·农齐奥·塞罗纳。专业决策分析。SmartOrg 公司,2007 年。免费在线 PDF 本书。参见第 36 页“编码概率”一章。

[5]《走出危机》惠勒,D . J .和钱伯斯,D . S .(1992)理解统计过程控制

简单随机抽样:实际上简单吗?

原文:https://towardsdatascience.com/simple-random-sampling-is-it-actually-simple-71014e58e0d1

如何为您的数据项目创建抽样计划

无论你多么努力地试图忘记你的 STAT101 课程,当你的膝跳接*时,你很可能倾向于默认简单随机抽样(SRS)。毕竟,这是你被告知为每一个家庭作业问题所做的假设。我不怪你——在可行的情况下,SRS 是一个很好的选择。

但我发现令人悲喜交加的是,每当我问一批新学生他们如何建议应对数据收集挑战时,我听到的部分答案是“只是”。比如,“只是完全随机地选择它们。”

让我们在现实世界里呆一会儿,好吗?

数据科学家的第一天工作

假设你是一名数据科学家,被雇佣估算下图森林中松树的*均高度,并描述分布

这片森林里的树有多高?照片由丹·奥蒂斯Unsplash 上拍摄

鉴于你可以在互联网上找到的树高信息的自助餐,很明显你不会是第一个勇敢的树木测量员来处理这种工作。很多人都测量过高大的树木…这能有多难呢?

(注意:本文中的链接会带你到我对出现的任何行话术语的轻松解释。)

事实还是统计?

如果你完美地测量了每一棵树,你就不需要统计数据了;你已经掌握了事实。但是你需要事实吗?还是愿意满足于统计?

即使你没有你想要的所有数据,统计学也给你一个前进的方法。测量几棵树(样本)而不是整个受祝福的森林(人口)会导致对你感兴趣的信息的不太完美但希望更便宜的观点。这是一种解脱,因为你甚至不知道在这片巨大的森林里有多少棵树。

让我们测量足够好的树木样本,这样我们就不必测量所有的树木了!

从统计学的角度来看,你的老板让你对 20 棵树随机样本进行精确到英尺的测量,所以你遵循了我们以前的文章中的建议,并确认这些规格对你的项目有意义。他们有;舞台已经搭好了!

STAT101 告诉你下一步做什么?

照片由蒂姆·莫斯霍尔德Unsplash 上拍摄

我已经给 100 多个班级的学生讲过这个例子,当我问他们我们应该如何挑选树木时,我每次都会听到人群中有人的一个或两个(相当的)回答:

“随便选[全部/全部]。”

和/或

“随便拿个简单的随机样本。”

采取一个简单的随机样本

我不怪你默认简单随机抽样(SRS)作为你的膝跳反应。在可行的情况下,这是一个绝妙的选择。那不是我反对的一点。

令我感到悲喜交集的是,每一次,我都会听到“只是这个词作为答案的一部分。

只是……没有。

无论是谁告诉你,对这些树进行简单随机抽样的方法是“完全随机地选择它们”……他都不知道如何正确使用英语单词“just”。这件事没有“只是”!当现实世界露出丑陋的一面时,数据设计会变得异常棘手。

再看看我们的森林吧!丹·奥蒂斯在 Unsplash 上的照片

想象一下,你非常讨厌户外,所以你偷偷把测量树木的工作外包给一个能忍受新鲜空气的人。你雇佣了一个没有技术背景的狂热的徒步旅行者,他渴望遵循你给的任何指示,所以你告诉这个人,呃,“只是”完全随机地选择 20 棵树?!

如果我是徒步旅行者,我会“只是”抓住看起来很方便的前 20 棵树“只是”给你上一课,告诉你要小心你的指示。

取样程序

简单随机抽样简单随机抽样完全随机SRS都是专业术语。它们指的是一种抽样程序,其中每个抽样单位(树)与任何其他树被选中的概率相同

只有来自相同选择概率的样本才是真正的简单随机样本。否则只是闪闪发光的废话。

SRS 是我们教给统计学新手的第一个(有时也是唯一的)抽样程序,这是有原因的,因为它…简单。就计算而言很简单。还有其他抽样程序,但它们需要调整计算,这通常超出了你第一年统计课程的范围。

还有其他采样程序,但它们需要更高级的计算。

如果它来自一个对每棵树都有相等选择概率的森林,那么它只是一个真正的简单随机样本(SRS ),否则当你对它使用 SRS 计算时,它只是一派胡言。

不幸的是,如果你按照 STAT101 教你的方法分析数据,但你实际上没有使用真正简单的随机抽样程序来获得数据,那么从技术上讲,你的结果将是错误的。

永远努力给出万无一失的指令,因为你永远不知道一个野傻子什么时候会出现。

如果你的徒步旅行者选择了靠*森林边缘的更方便的树,那绝对不是一个简单的随机样本。这是一种叫做 便利样本 的东西——这是一种你应该像躲避瘟疫一样避免的程序——在以后的文章中会有更多的介绍。用 SRS 数学分析这样的数据在统计学上是不合适的…如果那些树木得到的阳光量不同,因此不能代表整个森林呢?以此为基础进行推论会使你得出错误的结论。

那么,专业统计学家的回答会是什么样的呢?要找到答案,请前往第二部分

https://kozyrkov.medium.com/how-to-create-a-sampling-plan-for-your-data-project-3b14bfd81f3a

你有没有试过在 Medium 上不止一次按下拍手按钮,看看会发生什么? ❤️

喜欢作者?与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeSubstackLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格联系。

简单正则化套索回归

原文:https://towardsdatascience.com/simple-regularized-lasso-regression-31ef20eb3c21

L1 正则化

作者图片

简介

当我们从使用训练数据拟合的模型中观察到测试数据中的高方差时,我们知道需要在模型中引入一些偏差。否则,从长期来看,它将始终产生很高的方差,因此被认为是一个弱模型。实现这一点的几种方法之一是套索回归。套索回归与岭回归非常相似,但也有一个非常重要的区别。读者可以查看关于岭回归的文章,因为我将使用相同的例子来实现套索回归。

山脊 vs 拉索

先说简单的线性回归。在这种情况下,我们尝试最小化残差的*方,从而得到一条生成最小误差*方和的线。线性拟合的示例如下所示。这是根据以下字典创建的合成数据集。

以下示例中的均方误差为 0.055,这是所有其他可能的线中最小的。

线性拟合的训练数据[图片由作者提供]

在岭回归中,我们以训练数据中的方差为代价引入了一个小的偏差,但它减少了测试数据中的长期方差,这是偏差-方差权衡的一个典型场景。岭回归的核心是最小化残差*方和以及λ乘以斜率的*方。然而,套索的目标是相同的,但程序是不同的。这里,要最小化的项是残差的*方和加上λ乘以斜率的绝对值。就是这样。这是核心区别。这些回归模型中要最小化的项总结如下。当然,lambda 项可以通过交叉验证来确定,并针对给定数据集取最佳值。

简单线性回归 vs 岭回归 vs 套索回归

套索在行动

为了便于比较,让我们使用用于演示岭回归的同一个数据集。蓝点是训练数据,红点是测试数据。测试数据集也是从以下字典创建的合成数据集。

训练(蓝色)和测试(红色)数据[图片由作者提供]

在上面的例子中,当 lasso 回归被实施并且模型被拟合在训练数据上时,测试数据的均方误差是 5.41,这略小于岭的情况,在岭的情况下,我们获得的值是 6.88 的均方误差,并且也明显小于简单的线性回归结果,简单的线性回归结果以 8.41 的均方误差结束。同样,alpha 值越高,自变量对回归模型的影响越小。这基本上意味着当我们增加α值时,模型的系数接*零。对于非常高的α值,它将是完全水*的,对独立变量没有依赖性。因此,需要优化以通过交叉验证来确定 alpha 值。

套索回归的交叉验证

我们可以从很低到很高随机选择α的值。这里,我们扫描了特定数据集的 alpha 值,并确定了每种情况下的 MSE(均方误差)值。最小的 MSE 是α= 0.1,然后当使用 LassoCV 时获得相同的结果。

结论

岭回归和套索回归都是非常有用的工具,可以有效地消除训练数据集的过度拟合。这两种方法都惩罚模型系数,并试图最小化误差*方和加上惩罚项。只有这个罚项在山脊法和套索法之间有所不同。随着我们对高维回归分析的深入研究,我们将会看到,当存在大量不必要的独立变量时,岭方法可以降低不必要变量的系数,而 lasso 可以有效地使它们为零,从而完全消除它们对结果的影响。

https://mdsohel-mahmood.medium.com/membership

简单正则化线性和多项式回归

原文:https://towardsdatascience.com/simple-regularized-linear-and-polynomial-regression-37d0d634ece3

L2 正则化

作者图片

简介

显然,线性和多项式回归在数据分析领域被广泛用于医药、金融以及工程等部门的未来趋势预测。不存在完美的回归,但我们可以通过调整多项式的次数等参数使其接*完美。对于线性回归,我们不能增加次数,但我们可以根据现有的训练数据进行最佳拟合。然而,问题是这种调整对测试数据会有什么影响。模型会像训练数据一样完美还是会有更多的变化?如果我们能在模型中引入一个偏差呢?这就是我们要讨论的。

线性回归

执行线性回归时,最佳拟合线往往会使*方误差最小化。误差可以是正的,也可以是负的,这取决于数据点在哪一边。因此,*方项被认为具有*方误差,并且拟合线是对应于最小误差量的线。

回归中的正负误差[图片由作者提供]

用多项式拟合数据时也会发生同样的情况。最小二乘误差线/曲线可以通过 R *方值来解释,R *方值基本上告诉我们独立变量可以描述多少变化。

通常,在 ML 应用程序中,我们有训练和测试数据集,我们使用训练数据集拟合数据,并对剩余数据进行测试,以了解模型的有效性。为此,整个数据集通常分为两组:训练数据集和测试数据集。我们需要找到最符合训练数据的直线/曲线。为了实现这一点,我们可能会过度拟合数据,模型在训练数据上变得非常有经验,最终,它在任何测试数据或任何尚未看到的数据上都表现不佳。

这是偏差-方差权衡现象的本质。当我们在开发的模型中有较低的偏差时,在部署模型时,测试数据中会有较高的方差。通过引入偏差,我们最终可以最小化方差。

偏差-方差权衡解释简单

假设我们有以下训练数据,这些数据符合线性模型。该数据集的均方误差为 0.095,很小。

线性拟合的训练数据[图片由作者提供]

还假设红点是测试数据集。很明显,当我们扩展线性拟合时,会有持续的误差。换句话说,会有很高的方差。该数据集的均方误差为 8.41,与来自训练数据集的剩余均方误差相比更高。

训练(蓝色)和测试(红色)数据[图片由作者提供]

让我们给这个模型加上一个偏差。岭回归根据 alpha 值给模型引入了偏差。阿尔法值可以通过脊交叉验证来确定,脊交叉验证使选择最佳阿尔法值的过程自动化。因此,Alpha 是向模型引入惩罚的度量。alpha 值越高,模型对模型系数的惩罚越大。这是因为它想减少自变量对因变量的影响。如果 alpha 值太高,它会使线性拟合成为一条完全*坦的线,从而使系数实际上为 0,并消除自变量的影响。

让我们对 alpha = 0.1 的同一数据集应用岭回归来可视化影响。

alpha = 0.1 的岭回归[图片由作者提供]

正如所料,系数的值已经减少,我们可以看到惩罚的效果。该数据集的均方误差为 6.88,与来自没有惩罚的模型的剩余均方误差相比较小。因此,通过在模型中引入偏差,我们减少了测试数据的剩余误差,也实现了更低的方差。当我们有高 alpha 值时,模型变成一条扁*的线,如下所示。

alpha = 100 的岭回归[图片由作者提供]

外卖

  1. Alpha 值是惩罚的度量。可以使用交叉验证进行优化。
  2. alpha 值越高,模型系数的值越小。因此,它通过引入偏差减少了自变量的影响。
  3. 在模型中引入偏差实质上减少了测试数据中的方差。

多项式回归

岭回归也可以应用于多项式模型。多项式回归也有同样的发现。让我们在 GPL 2.0 许可下处理“鱼”数据集。有 5 个独立变量和一个响应变量。当我们增加多项式的次数以找到数据集上的最佳拟合时,它增加了一些系数的值,这使得它比其他自变量更占优势。从下图(使用重量与长度数据)可以明显看出,x 轴代表多项式次数,y 轴代表系数值。它实际上意味着什么?高系数值意味着我们更加强调某个特征,使其成为响应变量的一个非常好的预测器,同时抑制其他特征。这导致对训练数据的过度拟合,这是不希望的。

多项式高次的高系数值[图片由作者提供]

我们可以利用核脊来惩罚模型系数。但是我们先用一个次数=5 的多项式来拟合数据。

用次数= 5 的多项式拟合的鱼体重与长度数据[图片由作者提供]

具有小α值的核脊模型产生类似的拟合曲线。

应用 alpha = 0.5 的核脊后,用次数= 5 的多项式拟合的鱼体重与长度数据[图片由作者提供]

当αin 增加时,模型的复杂性降低。从下图可以明显看出,α= 5e 15。

应用 alpha = 5e15 的核脊后,用次数= 5 的多项式拟合的鱼体重与长度数据[图片由作者提供]

外卖

  1. Scikit-learn 的核岭用于多项式岭回归。
  2. 较小的 alpha 值模拟具有常规多项式拟合的相同模型。
  3. 较高的 alpha 值降低了模型的复杂性。

结论

岭回归(L2 正则化)只是一种减少模型过度拟合对训练数据集的影响的方法。它可以在线性和非线性模型上工作来惩罚系数。需要交叉验证步骤来确定α的最佳值,α的最佳值定义了惩罚系数的水*,因此,当应用于测试数据集时,使模型的方差减小。简而言之,通过引入一个小的偏差(取决于 alpha 水*),我们可以调整模型在测试数据集上的方差,以实现增强的性能。

简单逐步加权回归模型

原文:https://towardsdatascience.com/simple-stepwise-and-weighted-regression-model-53a31d9e4746

逐步加权回归

作者图片

简介

简单线性和多元线性回归是在工程、医学和其他科学领域广泛使用的统计技术。我们可以对模型进行很好的线性拟合,但同时我们需要找出模型可以解释多少变化。R 是这种变化测量的度量,但是如果我们增加独立变量的数量,R 的值也会增加。然而,我们的 R 的增加并不一定意味着模型变得更加健壮。如果数据集中存在多重共线性,我们需要找出调整后的 R。我们之前已经讨论了如果模型在训练数据上过度拟合,惩罚对模型的影响。特别是这些被称为岭回归和套索回归。

在这篇文章中,我将通过逐步回归和加权回归分析,这不过是常规回归的扩展。此外,我将扩展度量标准,以检查除 R 以外的 AIC 和 BIC 回归模型的稳健性。

逐步回归

逐步回归与常规回归相同,但处理方式不同。回归模型的主要目标之一是用自变量尽可能多地解释因变量的变化。为此,我们要增加 R 值。如前所述,向模型中添加越来越多的变量将有助于增加 R 值的过程。我们还可以使用其他一些指标来表明模型的稳健性。例如,AIC(代表由日本统计学家 Hirotugo Akaike 开发的 Akaike 信息标准)和 BIC(代表贝叶斯信息标准)也被用作模型稳健性的标准。

AIC 和 BIC 公式[1]

这里,SSE 是误差的*方和,n 是记录数,k 是模型中包含的变量数。实质上,AIC 和 BIC 惩罚了给模型增加更多变量的行为。当开发一个模型时,目标是最小化 AIC 和 BIC 的值,而如果我们使用 R 作为度量,目标是增加它的值。

面临的挑战是找到一个最小化 AIC/BIC 或增加 R 的模型。如果数据集很小,我们可以找到所有可能的模型,但如果数据集很大,这种方法就不可行。它在计算上也很昂贵。我们只需要找出那些具有最高 R 值或最低 AIC/BIC 的模型。出于这个目的,我们可以从整个模型开始,随着我们一个接一个地去除独立变量,计算这些指标的值。如果数据中存在多重共线性,包含所有这些变量将导致较低的调整后 R 值。随着我们移除越来越多的变量,我们可能会发现某个模型产生了最高的 R 值。这种方法被称为逆向逐步回归。我们也可以用向前的方式进行逐步回归。这意味着我们可以从一个预测器开始,随着我们建立越来越多的模型,并同时确定这些指标,添加越来越多的预测器。

出于演示的目的,我们将使用 CC0:公共领域许可下的二手车数据集。我们将使用 4 个数字列(“里程”、“税”、“mpg”、“引擎大小”)来拟合汽车价格。

很明显,当我们从一个预测值开始并添加三个剩余的预测值时,R 值增加,AIC/BIC 值减少,表明模型更稳健。这意味着所有这些变量都是汽车价格的良好预测指标。如果有人只想使用 AIC/BIC,有 python 库可以做到这一点。“RegscorePy”是一个能够执行该任务的 python 库。使用该库的 AIC 和 BIC 的值分别为 109256.10676454737 和 109283.36883824323,这与我们使用 statsmodel 得到的值不同。然而,使用 AIC/BIC 的公式,我们获得了与“RegscorePy”相同的值。读者可以利用完整的笔记本并运行来检查这一点。

加权回归

有时,在开发一个模型时,我们需要给某些数据更多的权重,以引入该数据对模型的重要性。例如,在我们的数据集中,我们可能会发现小的 mpg 值可能是不正确的。数据集中的最小 mpg 值是 2.8,我们将从所有数据点中减去该值,这将有效地抑制那些不正确的值。在某些情况下,早期阶段收集的数据可能不正确。在这些情况下,我们可以对那些不正确的值给予较小的权重,而对较新的数据给予较大的权重。对于我们的数据,让我们应用这个加权模型。

无权重线性回归()模型

加权线性回归()模型

有和没有加权的系数的值将是不同的。

结论

在本文中,我们讨论了逐步回归和加权回归,并在 Python 中实现。目标是优化稳健性标准度量。一些可能使用 R 或调整后的 R,而另一些可能使用 AIC/BIC,对新术语进行更多的惩罚。无论标准是什么,这个度量都不是模型健壮性的唯一标准。还有其他检查也需要做。例如,检查残差分布和异方差在数据集中也很重要。读者可以查看我以前关于这些主题的文章。

感谢阅读。

我的代码页

https://mdsohel-mahmood.medium.com/membership

参考

  1. 信息标准和新闻,STAT 462,宾州州立大学(网址:【https://online.stat.psu.edu/stat462/node/199/ )

用熊猫操纵日期时间变量的简单方法

原文:https://towardsdatascience.com/simple-ways-to-manipulate-datetime-variables-with-pandas-cfe9e8d36d24

Icons8 团队Unsplash 上的照片

熊猫的提示和技巧

用熊猫操纵日期时间变量的简单方法

在 Python 中使用时间相关变量的基础知识

当分析数据、特征工程和数据争论时,能够容易地操纵日期时间变量是很方便的。在本帖中,我们将看看在 pandas 中操作日期时间变量的一些简单而有用的方法。

照片由埃琳娜·科伊切娃Unsplash 上拍摄

📦 0.准备数据

让我们导入熊猫并准备要使用的样本数据:

import pandas as pd
df = pd.DataFrame({'flight_start': ['2019-02-04 13:15:00', 
                                       '2020-01-01 21:30:00', 
                                       '2021-10-28 02:00:00'],
                   'flight_length': [7, 21.5, 30],
                   'return_start': ['01/03/2019 10:00:00', 
                                   '11/01/2020 20:50:00', 
                                   '05/11/2021 08:20:00']})
df

以下是列的数据类型:

df.dtypes

将这些数据类型转换成更合适的数据类型将使操作变得更加容易。在下一节中,我们将学习将这些列转换为适当的与日期时间相关的数据类型。

📍 1.转换为 datetime 或 timedelta 数据类型

我们将用[pd.to_datetime()](https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html)flight_startreturn_start转换为日期时间,用[pd.to_timedelta()](https://pandas.pydata.org/docs/reference/api/pandas.to_timedelta.html?highlight=pandas%20to_timedelta#pandas.to_timedelta)flight_length转换为时间增量

df['flight_start'] = pd.to_datetime(df['flight_start'])
df['flight_length'] = pd.to_timedelta(df['flight_length'], 'h')
df['return_start'] = pd.to_datetime(df['return_start'], 
                                    format='%d/%m/%Y %H:%M:%S')
df

在这个代码片段中需要注意一些事情:在第 2 行中,我们使用代码'h'将 timedelta 单位指定为小时。其他代码及其含义记录在日期时间单位文档中。在第 4 行,我们使用format参数指定了日期时间格式。如果我们不指定它,熊猫会聪明地在这个例子中正确地转换它。我们使用format参数的原因是为了熟悉它,以便我们知道如果将来需要它,如何指定格式。如果您有兴趣了解更多关于这些格式的信息,strftime 的这份备忘单很好地总结了每个代码的含义。现在,让我们再次检查数据类型:

df.dtypes

太棒了,数据已经可以操作了!

在我们继续之前,我想展示一个在转换脏数据时有用的技巧。有时,我们可能会处理像这样的坏数据:202Y-01–01 21:30:00包含字符串或其他无效的日期时间。尝试像下面这样转换它会触发一个错误:

pd.to_datetime(pd.Series(['2019-02-04 13:15:00', 
                          '202Y-01-01 21:30:00']))

在这种情况下,我们可以在函数中指定errors='coerce'来只转换有效案例,并将无效案例替换为 missing:

pd.to_datetime(pd.Series(['2019-02-04 13:15:00', 
                          '202Y-01-01 21:30:00']), errors='coerce')

同样,在将脏数据转换为 timedelta 时,我们会得到一个错误:

pd.to_timedelta(pd.Series([7, 'T']), unit=’H’)

我们也可以使用errors='coerce':

pd.to_timedelta(pd.Series([7, 'T']), unit='H', errors='coerce')

如果您对处理无效数据的其他方法感兴趣,而不是用丢失的数据替换它们,请查看本文档。

📍 2.提取日期时间部分

通过.dt访问器,用 pandas datetime 提取 datetime 部分很容易。例如,下面是我们如何使用访问器提取日期:

df['flight_start'].dt.date

下面是常用属性的总结,示例基于第一个flight_start:

完整列表,请查看这份熊猫文档

📍 3.查找日期时间差异

让我们看看return_start已经过去多长时间了。在熊猫中,我们可以用pd.Timestamp.now()得到当前的当地时间:

pd.Timestamp.now() - df['return_start']

产生的新系列已经在timedelta数据类型中。由于广播,我们能够在不同长度的两个对象(一个标量和一个数组)之间进行这个操作。现在,让我们计算两个航班开始日期时间之间的持续时间,并将其保存为duration:

df['duration'] = df['return_start'] - df['flight_start']
df

像以前一样,我们可以使用.dt访问器提取 timedelta 部分。我们可以提取如下日期:

df['duration'].dt.days

下面是常用属性的总结:

如果我们想得到更精确的持续时间,我们可以如下计算:

df['duration'].dt.days + df['duration'].dt.seconds/(24*60*60)

这里有另一种方法来计算两个日期时间之间的差异:

df['return_start'].dt.dayofyear - df['flight_start'].dt.dayofyear

另一个:

df['return_start'].dt.date - df['flight_start'].dt.date

从这些不同的方法中,我们得到了略有不同的答案。正确的方法取决于计算的应用和目的。

📍 4.从 datetime 和 timedeltas 派生 datetime

最后,让我们看看如何使用 datetime 和 timedelta 变量计算 datetime:

df['flight_end'] = df['flight_start'] + df['flight_length']
df[['flight_start', 'flight_length', 'flight_end']]

Datetimes 和 timedeltas 完美地结合在一起,使它们更容易操作。

这就把它带到了这篇帖子的结尾!虽然我们只是触及了 pandas 功能的皮毛,但希望这些基本的数据时间操作技巧将在您的下一个涉及数据时间的数据操作任务中有用。⌚️:如果你想了解更多,这和是熊猫提供的额外学习资源。

Malvestida 杂志Unsplash 上拍摄的照片

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用 我的推荐链接成为会员,你的一部分会费会直接去支持我。

感谢您阅读这篇文章。如果你感兴趣,这里有我其他一些关于熊猫的帖子的链接:

◼️️ 在 pandas 中编写 5 个常用 SQL 查询
◼️️ 在 pandas 中编写高级 SQL 查询
◼️️ 给 pandas 用户的 5 个提示
◼️️ 在 pandas 中进行数据聚合的 5 个提示
◼️️ 如何在 pandas DataFrame 中转换变量
◼️️ 3 种重塑 pandas DataFrame 的简单方法
◼️️ 3 种简单方法

再见🏃 💨

使用快乐和冲突数据的简单而惊人且有意义的地理空间可视化

原文:https://towardsdatascience.com/simple-yet-stunning-and-meaningful-geospatial-visualization-using-happiness-and-confict-data-e9fbf690675c

用 Geopandas,Plotly Express,和 leav

在这一块,我们将涵盖如何创造惊人的(我认为😊)使用 Geopandas 和其他交互式库(如 Folium 和 Plotly Express)在 Python 中进行静态和交互式地理空间数据可视化。

作者图片

数据介绍

本文使用世界幸福和非洲冲突数据进行可视化,这些数据来自两个主要来源。世界幸福数据取自《世界幸福报告(WHR)》,从 1970 年到 2020 年,这是一个开放的数据,可以从世界幸福报告【1】中获取。这是一项具有里程碑意义的全球幸福状况调查,根据 156 个国家(2020 年世界幸福指数中的国家数量)的公民对自己的幸福程度进行排名。WHR 不仅根据福利,还根据公民的主观幸福感对各国进行排名,并更深入地探究社会、政府因素如何影响我们的幸福。

WHR 数据集中的一些属性(2019 年)。来源:世界幸福报告

非洲冲突数据集取自武装冲突地点和事件数据项目,该项目公开发布,可通过数据导出工具 ACLED【2】访问。ACLED 包含关于世界各地政治暴力、冲突和无序模式的高质量和最广泛使用的实时数据源。

非洲冲突数据集中的一些属性(2019 年)。来源:武装冲突地点&事件数据项目

本文的重点是地理空间可视化。在以后的文章中,我将讲述如何处理和准备可视化数据。

得到💬任何数据科学或编程问题的 GPT 式答案。为成千上万的人生成摘要和学习笔记📚只需一次点击即可获得学习资源。👉

https://aigents.co/learn

地理空间数据可视化

在本节中,我将分享一些最流行的可视化地理空间数据的方法,如使用气泡图、多层地图、Choropleth 地图和标志性的 GapMinder 动画。让我们首先导入一些我们需要的库。如果你还没有,请安装它们。

import pandas as pd
import numpy as np
import plotly.express as px
import geopandas as gpd
from geopandas import GeoDataFrame
from shapely.geometry import Point
from matplotlib import pyplot as plt
import folium

1.气泡图

气泡图使用圆形大小作为编码变量。圆圈的大小表示特定地理坐标的特征的数值。让我们学习如何使用 Geopandas 和 Plotly 创建静态和交互式气泡图。

地质公园

我们可以很容易地用 Geopandas 创建一个静态气泡图。首先,我们使用的数据集 WHR 包含多边形几何。为了创建气泡图,我们需要创建点几何体。我们可以使用GeoSeries的属性centroid来生成代表每个多边形几何形状的质心的几何点。

gdf_worldhappiness_point = gdf_worldhappiness.copy()
gdf_worldhappiness_point['geometry']= gdf_worldhappiness_point['geometry'].centroid
gdf_worldhappiness_2019 = gdf_worldhappiness[gdf_worldhappiness['year']==2019]
gdf_worldhappiness_point_2019 = gdf_worldhappiness_point[gdf_worldhappiness_point['year']==2019]
fig, ax = plt.subplots(figsize=(16,16))
marker = gdf_worldhappiness_point_2019['GDP per Capita']/500
gdf_worldhappiness_2019.plot(ax=ax, color="lightgray",edgecolor="grey",
 linewidth=0.4)
gdf_worldhappiness_point_2019.plot(ax=ax,color="#07424A", 
markersize=marker,alpha=0.7, categorical=False, 
legend=True plt.axis('equal')
plt.show()

下面的地图显示了 2019 年 WHR 所有国家的人均 GPD 泡沫图(请注意,有些国家的数据缺失)。圆圈的大小代表每个国家的人均 GDP 该国的地理坐标。可以看出,非洲国家似乎是世界上人均国内生产总值最低的一些国家,据报告,布隆迪的人均国内生产总值是有史以来最低的,紧随其后的是南苏丹和索马里。一个简单但有见地的可视化,不是吗?

人均国内生产总值(WHR 2019)。作者可视化。

Plotly 散点图 _ 框

上面的气泡贴图完成了它的工作,但是它是一个静态贴图。为了创建令人惊叹的交互式地图,允许用户与感兴趣的区域进行交互和放大,我们可以构建一个交互式气泡地图。

有不同的 Python 库用于绘制交互式地图。一个有用的库来构造它是 Plotly Express。几年前,以 R 和 Python 的交互式可视化而闻名的 Plotly 提出了一个名为 Plotly Express 的新库,它具有一个 scatter_mapbox()函数,可以使用地理数据框和用于气泡大小的列/属性来创建交互式气泡图。

fig = px.scatter_mapbox
(gdf_worldhappiness_point_2019.dropna(subset = 'GDP per Capita'), 
lat="latitude",lon="longitude",color = 'continent', 
size= 'GDP per Capita', 
color_continuous_scale=px.colors.cyclical.IceFire,size_max=15,
hover_name = 'name',mapbox_style="carto-darkmatter", zoom=1)

fig.show()

以下交互式图形提供了与静态气泡图相同的洞察力,但用户现在可以将鼠标悬停在几何点位置上并与地图进行交互。所以,我们做得更好,不是吗?

人均 GDP(WHR 2019),Plotly Scatter_Mapbox。作者可视化

2.多层地图

我们有时需要将多个数据集相互叠加在一起。让我们看看如何用 Geopandas 和 Let 来实现这一点。

地质公园

有几种方法可以叠加图。制作多图层地图的一种方法是使用 Geopandas。绘图功能。这可以在同一组轴上绘制多个数据集。通过添加额外的 plot 命令并将轴传递给 ax 关键字,可以将每个数据集添加到前一个数据集的顶部。在下面的代码中,我有两个数据集:“非洲”和“acled”。“Acled”绘制在数据集“非洲”的顶部。

fig, ax = plt.subplots(1,1,  figsize=(12,10))
Africa.plot(ax=ax, linewidth=0.5,edgecolor = 'white',facecolor = 'Green', 
alpha=0.5);
acled.plot(ax= ax,column = 'event_type',alpha=0.7,legend = 'event_type');
plt.title('Conflicts in Africa', fontsize=40, 
fontname="Palatino Linotype", color="grey")
ax.axis("off")
plt.axis('equal')
plt.show() 

剧情显示西非是一个充满战斗、暴乱和暴力等冲突的次区域,这非常有意义,因为那里的大多数国家都经历过内战,如塞拉利昂、利比里亚、几内亚比绍和科特迪瓦。此外,所有类型的冲突似乎也存在于东非的一些地区。令我惊讶的是,马达加斯加似乎正在发生一些战斗和暴力事件。在使用这个数据集之前,我对此一无所知,所以我学到了一些新东西。

非洲的冲突(ACLED 2019)。作者的视觉化

薄层

为了允许用户与冲突地图中感兴趣的不同区域进行交互和放大,我们可以使用 allow,这是一个功能强大的 Python 库,可以创建多种类型的传单地图。首先,我们通过简单地调用 follow 来创建一个底图。地图()。然后,我们可以使用 CircleMarker 在基本地图的顶部添加标记,circle marker 是一个在基本地图上绘制圆形覆盖图的类,用于使用坐标信息精确定位位置。

fm = folium.Map()
geo_Africa_list = [[point.xy[1][0], point.xy[0][0]] for point in acled.geometry ]
def color_producer(event_type):
    if event_type == 'Battles':
        return 'lightblue'
    elif event_type == 'Violence against civilians':
        return 'green'
    elif event_type == 'Riots':
        return 'pink'
    else:
        return 'red'
for i, coordinates in enumerate(geo_Africa_list):
    color = color_producer(acled.event_type.iloc[i])
    folium.CircleMarker(location = coordinates, color = color, radius = 0.5
        ,popup =  f"{acled.event_type.iloc[i]}"
                       ).add_to(fm)
fm

非洲的冲突(ACLED 2019)。交互式树叶地图。作者可视化

3.等值区域图

Choropleth 地图可能是最常用的地理空间可视化。choropleth 地图是一种专题地图,它使用颜色强度来对应空间单元内的地理特征摘要,例如人口密度或人均 GDP。同样,Geopandas.plot 函数或 leav。Choropleth()可用于创建 Choropleth 映射。

gdf_worldhappiness_2019.plot(column = "Freedom to make life choices",
figsize=(15,10),legend=True, edgecolor="white", 
cmap = 'pink_r',
legend_kwds={"label":"Freedom to make life choices",
 "orientation":"horizontal"})

我美丽的祖国越南在选择生活的自由方面获得了很高的分数。多么令人惊喜啊!我从发展研究课上学到了一个很好的概念化发展的方法。它说,“发展应被视为人们有自由过他们有理由珍视的生活”。这是回答这个问题的全国*均分:“你对自己选择生活的自由满意还是不满意?”

自由穹顶做出人生选择(WHR 2019)。作者可视化

4.用 PlotlyExpress 重新制作 Gapminder 动画

当汉斯·罗斯林在他著名的增强现实动画作品和 Ted 演讲中展示这个令人惊叹的动画时,Gapminder 动画已经成为数据可视化的标志性时刻之一。在本节中,我们将学习如何使用 Plotlyexpress 通过以下代码重新创建一个类似的图标 Gapminder 动画:

fig = px.scatter(
    gdf_worldhappiness_point.dropna(subset = 'GDP per Capita'),   
    color = 'Region Name',
    size= 'GDP per Capita', 
    color_continuous_scale=px.colors.cyclical.IceFire, 
    animation_frame= "year",
    animation_group="country",
    hover_name = 'name',
    size_max=35, 
    x="GDP per Capita", y="Healthy life expectancy at birth",
    color_discrete_map={"Europe": 'rgba(260,0,0,0.4)'},
    log_x = True
)
fig.show()

这很容易实现。普洛特利。express 模块(通常作为 px 导入)具有从地理数据框架获取数据的函数px.scatter()。然后你需要定义xy轴以及每年递增的animation_frame, animation_group。随着更多关于外观改进的争论,动画泡泡图来了!你现在可以与这张地图互动,看看各国的收入和预期寿命在过去 15 年里发生了怎样的变化。

作者可视化

嘣,你有它!三种出色的绘制地理空间数据的方式:GeoPandas、Plotly 和 Folium。请在评论中告诉我你的想法:)

感谢您的阅读。如果你觉得我的帖子有用,并且正在考虑成为中级会员,你可以考虑通过这个 推荐会员链接 :)来支持我,我将收取你的一部分会员费,不需要你额外付费。如果你决定这样做,非常感谢!

参考

[1]公开数据:世界幸福报告首页|世界幸福报告。Helliwell,John F .,Richard Layard,Jeffrey Sachs 和 Jan-Emmanuel 德·内维编辑。2020.2020 年世界幸福报告。纽约:可持续发展解决方案网络

[2]开放数据:武装冲突地点和事件数据项目ACLED(acleddata.com)。罗利、克里纳德、安德鲁·克林、哈瓦德·黑格雷和约阿金·卡尔森。(2010)."介绍武装冲突地点和事件数据."和*研究杂志 47(5) 651- 660。

剧情文档

用 Python 创建美国各州 Choropleth 地图的最简单方法

原文:https://towardsdatascience.com/simplest-way-of-creating-a-choropleth-map-by-u-s-states-in-python-f359ada7735e

使用此代码模板在两分钟内创建一个美国 Choropleth 地图,无需 GeoJSON

图片由 Pixabay 提供(作者修改)

Choropleth 地图是最流行的地理空间数据可视化之一。它是一种专题地图,在地图上的预定义区域使用不同的阴影来表示与度量相关的地理特征。它可以被标绘在任何地图上,也可以被标绘在不同的地理区域,如国家、州、县、邮政编码等。

follous 通常是我创建 choropleth 地图的首选包,尤其是那些可能需要对风格和设计进行大量定制的地图。然而,很多时候,我们可能只需要按美国各州绘制一个简单的 choropleth 地图(就像下面的例子),而不想经历使用geopandas或导入geojson文件来完成工作的麻烦。

作者图片

在对比了多个图形库如 Folium、Plotly、Matplotlib 等之后。,我发现使用Plotly_Express是按州创建美丽的美国 choropleth 地图的最简单快捷的方法。Plotly_Express内置了美国各州的几何图形,因此无需使用任何 GeoJson 文件。您所需要的只是在一个简单的代码模板中指定几个参数,一切都准备好了!

先决条件

如果您还没有这样做,您将需要使用以下命令安装Plotly_Express:

pip install plotly-express

此外,在本帖中,我们将使用一个小样本数据集来创建一个 choropleth 地图,该地图可视化了 2022 年 1 月美国各州的单户房产价格。

要下载原始数据集,请转到 Redfin 的数据中心,向下滚动到“它如何工作”部分,并下载“州”级别的区域数据。这是一个免费的、开放的数据集,你可以下载并根据自己的目的使用,并注明出处。然后,我们可以使用下面的代码来选择绘制地图所需的列和数据。

作者图片

创建美国各州的 Choropleth 地图

现在我们的数据已经准备好了,让我们看看使用Plotly_Express创建美国各州的 choropleth 地图有多简单。下面是代码模板,您可以使用Plotly_Express来绘制美国各州的 choropleth 地图。

import plotly.express as pxfig = px.choropleth(df,
                    locations='state_code', 
                    locationmode="USA-states", 
                    scope="usa",
                    color='Median Sales Price ($)',
                    color_continuous_scale="Viridis_r", 

                    )
fig.show()

以下是您需要在代码模板中指定的一些重要参数:

  • locations:这应该是数据帧中代表美国各州的列名。确保您提供的locations是两个字母的州名缩写。
  • locationmode:要使用 Plotly_Express 内置的美国州几何图形,需要设置locationmode='USA-states'
  • scope:这里应该指定为‘美国’。
  • color:这应该是表示您希望按状态可视化的指标的列的名称。
  • color_continuous_scale:您可以使用以下任何一种指定的色标。您可以将“_r”添加到已命名的色标中以反转它。

作者图片

基本就是这样!如果您想向地图添加带有某种格式的标题,也可以简单地添加以下代码:

fig.update_layout(
      title_text = 'Jan 2022 Median Housing Price by State',
      title_font_family="Times New Roman",
      title_font_size = 22,
      title_font_color="black", 
      title_x=0.45, 
         )

作者图片

奖励:创建美国各州的动画 Choropleth 地图

在上面的例子中,我们使用plotly_express创建了一个快照 choropleth 地图,它可视化了 2022 年 1 月各州的房价。使用动画的 choropleth 地图来观察各州的房价如何随着时间的推移而演变将会很有趣。

好消息是用plotly_express做起来超级简单。您所需要做的就是确保您的时间范围列(在我们的例子中是:“period_begin”)以升序排序(第 9 行),然后在您的px.choropleth()代码中添加animation_frame参数(第 17 行)!

作者 GIF

Plotly_Express还具有内置的几何图形,可显示自然地球数据集中定义的国家/地区的世界地图。根据 Plotly 的文档,要使用内置国家几何图形,您需要提供locations作为三个字母的 ISO 国家代码。其他一切都遵循美国各州 choropleth 地图的相同代码模板。

如果您需要按美国各州或世界各国以外的地理区域绘制 choropleth 地图,则需要使用适当的 GeoJson 文件来定义相应的地理区域边界,例如县、邮政编码等。您可以使用Plotly_ExpressPlotly_GoFolium来创建地图。如果你想学习如何使用Folium创建一个高度可定制和复杂的 choropleth 地图,你可以阅读下面的文章。

感谢阅读!我希望你喜欢这个教程,并且学到了一些关于地理空间数据可视化的新知识。

参考和数据源

  1. Plotly 文档:https://plotly.com/python/choropleth-maps/
  2. 数据来源:Redfin 月度住房市场数据——州一级。这是由 Redfin一家全国性房地产经纪公司提供的一个开放数据集,你可以免费下载,并注明出处。

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!

简化了 Kubeflow 与 Kfops 的 MLOps

原文:https://towardsdatascience.com/simplified-mlops-for-kubeflow-with-kfops-785e36a6c5f4

这篇文章介绍了 Kfops ,它是一个基于 Kubeflow 的工具,可以插入到你的 MLOps 生命周期中。

作者图片

什么是 Kfops?

该项目的主要目标是简化和标准化 Kubeflow 管道编译、管道执行和模型部署。
它有两个“组件”——一个专用的 python 包和 chatops 命令。Python 包在开发/实验阶段使用,chatops 命令直接在 Pull 请求注释中使用。

它如何融入 MLOps 生命周期?
我们来举个例子。您的 ML 模型的代码存储在 Github 存储库中。你的团队利用 Github 问题来讨论 ML 模型和(Kubeflow)管道的特性、改进和 bug 修复。
在其中一个问题中,团队计划改进管道的预处理步骤。您的任务是测试这些变化将如何影响模型。
作为第一步,您想在您的开发环境中进行试验。您对代码进行修改,并使用 Kfops CLI(命令kfc build-run)直接在 Kubeflow 中执行管道。Kfops 将链接输出到管道运行。
你做了更多的修改,直到结果准备好与团队分享。
您将变更推送到存储库并创建拉请求。在 Pull 请求中,使用/build_run chatops 命令再次运行管道。Kfops 运行管道并直接在 PR 中打印结果。现在,您可以通过在本期杂志中引用您的公关来与团队分享结果。
团队对结果很满意,并决定将其部署到生产中。你回到公关部门,发布/deploy命令。

你完成了什么?

  • 您创建了可重复的实验,其中只有预处理步骤发生了变化,而其他的都保持不变。
  • 因此,您可以很容易地衡量您的修改所带来的改进。
  • 这个实验毫不费力地“记录”在问题和公关中,并与特定的 Kubeflow 管道运行及其结果相联系。
  • 您的改进现已投入生产!

以下是该过程的可视化表示(参见此处的全屏图像):

作者图片

你可能需要一些时间来消化这个数字。以下是关键要点:

  • kfc和“chatops”命令都旨在隐藏 Kubeflow 的底层复杂性。
  • 作为一名数据科学家/工程师,您希望有一个简单、灵活、可扩展的实验/开发阶段。已经提到的kfc命令控制它。根据您的设置,该命令可以直接在集群内运行(例如,Jupyter notebook 预装了 Kubeflow 或任何其他配置为访问 Kubeflow 管道的 Kubernetes Pod)或从连接到远程 Kubeflow 实例的本地环境(Kubernetes 集群之外)运行。
  • 不允许使用kfc命令进行模型部署,因为它没有记录执行命令的时间和人员。
  • 模型部署被有目的地从 Kubeflow 管道中移除。相反,你应该使用/deploy来发布那些满足你的“生产”需求的模型。
  • 类似/build_run、【或/build/run/deploy(或/staging_deploy)的命令在拉取请求的上下文中执行。
  • 团队使用存储库的问题和拉请求来讨论计划的实验、改进、修复等。在 Pull 请求中执行的命令将这个讨论与 Kubeflow 管道 UI 中的实验结果联系起来。
  • 管道运行(/build_run/run)直接在 PR 注释中打印出 Kubeflow 管道输入参数值。
  • 目前,kfop 只能将 ML 模型部署到安装了 Kubeflow 和 kfop 的同一个 Kubernetes 集群。
  • 并非存储库中的所有变更都与 ML 模型相关。模特培训可能很耗时或者很昂贵。因此,当创建或更新 PR 时,Kubeflow 管道不会自动启动。必须用“运行”命令手动执行。

出于简洁的原因,该图隐藏了容器图像生成器等细节。查看文档中的更多数字和细节。

主配置文件

Kfops 的另一个重要特性是主配置文件。它集中了项目的管道、容器映像生成器和部署设置。
在开发过程中,您可以使用kfc命令覆盖与pipeline相关的设置。可以用kfc--set标志和/或单独的覆盖文件(如kfc build_run --config-override override.yaml)覆盖参数。
请注意,因为配置文件是存储库的一部分,所以您可以很容易地跟踪过去对它所做的所有更改。

Kfops 示例配置文件(图片由作者提供)

更多细节请参考配置文件文档

约定、限制和更多功能

您可以在文档中找到完整的列表;以下是最重要的几个:

  • 惯例(例如,集中配置)在你所有的 ML 项目中强制执行标准化。
  • 每个存储库只允许一个 ML 模型。
  • Kfops 可以插入任何 SCM 系统(虽然目前只支持 Github)
  • /deploy期间,默认执行“金丝雀”阶段,随后是可选的推断端点检查(HTTP 返回代码200)。
    如果 ML 模型部署成功,Kfops 将标记 PR(例如Deployed-to-production),与 HEAD 合并并关闭 PR。
    所有故障都被记录为 PR 注释。
  • 如果您的 ML 模型库向公众开放,您可以限制谁可以执行 chatops 命令(功能正在开发)
  • 各种警告和错误的输出直接“记录”在 PR 上下文中:

管道错误示例(作者图片)

管道警告示例(图片由作者提供)

下一步是什么?

尽管功能齐全,该项目仍处于早期开发阶段。它的长期目标是为处于 MLOps 成熟度不同阶段的公司提供选择。前往文档了解更多详情。

用 Python 模拟一个微小的太阳系

原文:https://towardsdatascience.com/simulate-a-tiny-solar-system-with-python-fbbb68d8207b

使用 Matplotlib 中的真实质量、距离和速度数据,模拟一个包含太阳、地球、火星和一颗未知彗星的微型太阳系

有太阳、地球、火星和彗星的太阳系。Andrew Zhu 的 GIF

我对 PCA(主成分分析)感到惊讶,并想到用动画来展示机器学习过程。当我设法制作一个图表动画时,我很想制作一些很酷的东西。我想到的第一个很酷的东西是太阳系。这篇文章也是我回忆牛顿物理学、向量空间、matplotlib 等知识的一次旅程。

为了模拟行星的轨道并制作动画,我需要解决两个关键问题:

  1. 生成轨道数据。
  2. 用 Matplotlib 制作数据动画。

在这篇文章中,我将涉及上述两个问题。如果你赶时间,不想看我啰嗦的八卦,可以在 Github 上查看代码主机。

作者所有图片和 gif。

牛顿引力定律

根据牛顿万有引力定律,我们可以计算出两个物体之间的力。只考虑地球和太阳。太阳对地球的引力影响可以用下面的等式来表示:

M表示太阳的质量,m表示地球的质量,r表示两个物体中心之间的距离。G是一个常量值。

根据牛顿第二定律:

我们可以得到加速度a:

嗯,加速度a只与太阳质量和日地距离r有关。现在有了已知的a。我们可以计算δ时间后地球的速度- dt

最后,得到位移的变化— dd:

(我知道在一篇文章中使用/阅读如此多的方程令人讨厌,但这可能是解释引力轨道模拟基础的最简单、最简洁的方式)

向量空间中的引力定律

由于我要用 matplotlib 绘制地球轨道,所以需要计算xy的值。为了简单起见,我将使用 2D 坐标轴,而不是三维。

地球引力分析。附录中的图表代码

我在上图中画出了方程的矢量版本。如果你是第一次读这个方程,你可能会有几个问题。

  1. 为什么引力方程中有一个负号(-)?因为力的方向与距离矢量r相反。当你计算地球对太阳的作用力时,你需要去掉负号(-)。在矢量坐标中,我们需要非常小心方向。
  2. 离心力呢,为什么不处理离心力?因为离心力其实是万有引力的结果。如果我们在地球上进行受力分析。只有一个引力作用在它上面。当地球绕着太阳旋转时,地球正在向太阳下落。然而,地球垂直于 G 力的初速度将它拉离太阳,这就是为什么地球可以绕太阳运行。

在 Python 代码中,假设太阳在位置(0,0),地球在位置(6,6):

sx,sy = 0,0
ex,ey = 6,6

距离 r 将是:

rx = ex - sx
ry = ey - sy

定义日地引力常数GMmgravconst_e:

Ms          = 2.0e30                    *# mass of sun in kg unit*
Me          = 5.972e24                  *# mass of earth in kg unit* 
G           = 6.67e-11
gravconst   = G*Ms*Me

模拟器将每隔dt时间计算一个数据点。我们可以把dt设定为某一天:

daysec = 24.0*60*60
dt     = 1*daysec

r (|r|)的立方是:

modr3_e = (rx**2 + ry**2)**1.5

要计算施加在地球方向上的力:

*# get g force in x and y direction*
fx_e = gravconst_e*rx/modr3_e
fy_e = gravconst_e*ry/modr3_e

基于矢量形式的牛顿第二定律:

结合上述等式:

因为我已经计算了力的矢量,所以我可以在dt时间后得到新的速度矢量:

xve += fx_e*dt/Me
yve += fy_e*dt/Me

最后得到地球新的位移(速度乘以时间就是距离:d=v*t):

xe += xve*dt
ye += yve*dt

下面是模拟地球轨道运行一年的完整代码。

G           = 6.67e-11                  *# constant G*
Ms          = 2.0e30                    *# sun*
Me          = 5.972e24                  *# earth* 
AU          = 1.5e11                    *# earth sun distance*
daysec      = 24.0*60*60                *# seconds of a day*
e_ap_v      = 29290                     *# earth velocity at aphelion*gravconst_e = G*Me*Ms
*# setup the starting conditions*
*# earth*
xe,ye,ze    = 1.0167*AU,0,0
xve,yve,zve = 0,e_ap_v,0*# sun*
xs,ys,zs    = 0,0,0
xvs,yvs,zvs = 0,0,0t           = 0.0
dt          = 1*daysec *# every frame move this time*xelist,yelist,zelist = [],[],[]
xslist,yslist,zslist = [],[],[]*# start simulation*
**while** t<1*365*daysec:
    *################ earth #############*
    *# compute G force on earth*
    rx,ry,rz = xe - xs, ye - ys, ze - zs
    modr3_e = (rx**2+ry**2+rz**2)**1.5
    fx_e = -gravconst_e*rx/modr3_e
    fy_e = -gravconst_e*ry/modr3_e
    fz_e = -gravconst_e*rz/modr3_e

    *# update quantities how is this calculated?  F = ma -> a = F/m*
    xve += fx_e*dt/Me
    yve += fy_e*dt/Me
    zve += fz_e*dt/Me

    *# update position*
    xe += xve*dt
    ye += yve*dt 
    ze += zve*dt

    *# save the position in list*
    xelist.append(xe)
    yelist.append(ye)
    zelist.append(ze)

    *################ the sun ###########*
    *# update quantities how is this calculated?  F = ma -> a = F/m*
    xvs += -fx_e*dt/Ms
    yvs += -fy_e*dt/Ms
    zvs += -fz_e*dt/Ms

    *# update position*
    xs += xvs*dt
    ys += yvs*dt 
    zs += zvs*dt
    xslist.append(xs)
    yslist.append(ys)
    zslist.append(zs)

    *# update dt*
    t +=dt

看看轨道是什么样子:

**import** matplotlib.pyplot **as** plt
plt.plot(xelist,yelist,'-g',lw=2)
plt.axis('equal')
plt.show()

用 matplotlib 制作动态观察动画

现在,我要把静止的地球轨迹变成一个移动的物体,并在*面上保留一个轨迹。以下是我在制作动画时解决的一些问题:

  1. 所有的魔法都发生在update 函数内部。基本上每一帧都会被FuncAnimation 调用函数。您可以调用ax.plot(…)在*面中绘制新像素(所有像素将保留在下一帧中)或调用set_dataset_position更新可视对象的数据(matplotlib 将根据当前帧的数据重新绘制所有内容)。
  2. 表达式“line_e, = ax.plot(…)”中的逗号一开始看起来很怪异。但是请注意,是逗号构成了元组。"ax.plot(…)"返回一个元组对象。这就是为什么您需要在变量名后添加一个逗号。
  3. 在地球后面画出轨迹。我初始化空列表(exdata,eydata = [],[])来保存所有先前的轨迹点。每次调用update 函数时,将轨迹点追加到列表中。

我不打算把这部分变成动画教程。如果你以前从未尝试过 matplotlib 动画。杰克·范德普拉斯的 Matplotlib 动画教程非常值得一读。

以下是制作动态观察动画的代码:

**import** matplotlib.pyplot **as** plt
**from** matplotlib **import** animation

fig, ax = plt.subplots(figsize=(10,10))
ax.set_aspect('equal')
ax.grid()

line_e,     = ax.plot([],[],'-g',lw=1,c='blue')
point_e,    = ax.plot([AU], [0], marker="o"
                      , markersize=4
                      , markeredgecolor="blue"
                      , markerfacecolor="blue")
text_e      = ax.text(AU,0,'Earth')

point_s,    = ax.plot([0], [0], marker="o"
                      , markersize=7
                      , markeredgecolor="yellow"
                      , markerfacecolor="yellow")
text_s      = ax.text(0,0,'Sun')

exdata,eydata = [],[]                   *# earth track*
sxdata,sydata = [],[]                   *# sun track*

**print**(len(xelist))

**def** update(i):
    exdata.append(xelist[i])
    eydata.append(yelist[i])

    line_e.set_data(exdata,eydata)
    point_e.set_data(xelist[i],yelist[i])
    text_e.set_position((xelist[i],yelist[i]))

    point_s.set_data(xslist[i],yslist[i])
    text_s.set_position((xslist[i],yslist[i]))
       ax.axis('equal')
    ax.set_xlim(-3*AU,3*AU)
    ax.set_ylim(-3*AU,3*AU)

    **return** line_e,point_s,point_e,text_e,text_s

anim = animation.FuncAnimation(fig
                                ,func=update
                                ,frames=len(xelist)
                                ,interval=1
                                ,blit=True)
plt.show()

结果:

点击查看完整代码

如果…会怎样

改变地球的初始速度,比如将地球速度降低到19290 米/秒(从 29290 米/秒):

改变地球质量,增加到 5.972e29 kg(从 5.972e24 kg) 。太阳在移动!

将火星和一颗未知彗星加入轨道系统

因为我已经成功模拟了地球和太阳的轨道。向系统中添加更多的对象并不困难。比如火星和一颗未知的彗星。

点击查看其代码

在 Matplotlib3D 中制作动画

通过对绘图代码进行一些调整,我甚至可以制作 3D 轨道动画!在上面的模拟代码中,我已经计算了z轴的数据。

点击查看代码

摘要

在这篇文章里,我把牛顿万有引力定律的基础,如何把方程组转换到向量空间,一步一步的计算仿真数据。有了模拟数据在手,我也解释了动画路上的三个关键问题。

感谢您阅读到这里。彻底了解模拟过程,你甚至可以搭建著名的三体问题模拟器。(是的,我也是同名小说的粉丝)

如果你有任何问题,请告诉我,我会尽力回答。并且不要犹豫指出我在文章中犯的任何错误。期待与您的进一步联系。

我就讲到这里,祝你喜欢它,编程愉快。

参考链接

附录—代码生成矢量坐标图

在运行代码之前,您需要在您的机器上安装 LaTex。

**import** numpy **as** np
**import** matplotlib.pyplot **as** plt
plt.rcParams['text.usetex'] = True
font = {'family' : 'normal',
        'weight' : 'bold',
        'size'   : 18}
plt.rc('font',**font)

*# initialize the stage*
fig,ax = plt.subplots(figsize=(8,8))

*# set x, and y axis,and remove top and right*
ax.spines[['top','right']].set_visible(False)
ax.spines[['bottom','left']].set_position(('data',0.0))
ax.axis('equal')
ax.axes.xaxis.set_visible(False)
ax.axes.yaxis.set_visible(False)
ax.set_xlim(-3,10)
ax.set_ylim(-3,10)

*# draw arrows*
ax.plot(1, 0, ">k", transform=ax.get_yaxis_transform(), clip_on=False)
ax.plot(0, 1, "^k", transform=ax.get_xaxis_transform(), clip_on=False)

*# sun*
a = np.linspace(0,2*np.pi,360)
xs = np.sin(a)
ys = np.cos(a)
ax.plot(xs,ys,c='r')
ax.text(-1,1,'sun')

*# earth*
xec,yec = 6,6
xe = xec+ 0.5*xs
ye = yec+ 0.5*ys
ax.plot(xe,ye,c='b')
ax.text(xec+0.5,yec+0.5,'earth')
ax.text(xec-1,yec+1.1,r"$\vec{v}$")

*# r vector*
ax.quiver([0,xec,xec-0.3],[0,yec,yec+0.1]
         ,[xec,-2,-3],[yec,2,-3]
         ,units='xy',scale=1,width=0.07)
ax.text(3,2,r"$\vec{r} = \vec{earth}-\vec{sun}$")
f_eq = (r"$\vec{F}=-G\frac{Mm}{|r|^2}\frac{\vec{r}}{|r|}\\$"
        r"$=-G\frac{Mm}{|r|^3}\vec{r}$")
ax.text(0.5,5.5,f_eq)

*# plot data*
plt.show()

结果:

用神经网络模拟任何功能

原文:https://towardsdatascience.com/simulate-any-functions-with-neural-network-8e52f2083e3d

使用 PyTorch 建立一个神经网络模型来模拟任何函数

我们知道线性回归模型可以帮助生成一条线来模拟“线性数据”。一个形式为 f(x) = ax + b 的函数。

线性回归,作者图片

那么,其他函数呢,比如非线性函数?甚至是我们一无所知的不规则形状。我们还能模拟一个模型来拟合它吗?像头图中的那种甚至是螺旋形的?

最*,我受到了这个视频的启发:为什么神经网络可以学习几乎任何东西。我意识到,有了真实的数据在手,我们可以…

用神经网络模型模拟任何函数!

紧急花园的视频确实是一个被低估的视频,强烈推荐你也去看,视频中的链接质量也很高。

在这篇文章中,我将使用 PyTorch 实现一个任意函数模拟器。出于两个目的:

  1. 见证神经网络模型的威力。看试衣过程真的很好玩。
  2. 经过训练的模型也可以用于检测异常数据。常规数据通常处于稳定的模式,比如说,在工作日相对较高,在周末下降。经过训练的神经网络模型可用于检测异常数据。

生成培训数据

以下代码将初始化 x 数字,并使用以下函数生成用于模拟的 y 数字。

import numpy as np
import matplotlib.pyplot as plt
X = np.array([*range(-20,20)],*dtype*=np.float32)
X = X*0.1
y = [x**3+ x**2 -3*x -1 for x in X]
plt.plot(X,y,'ro')

一个“未知形状的点”,作者图片

现在我们既有输入 x 又有输出 y ,接下来让我们忘记上面的函数,因为我要构建一个神经网络来模拟“未知”函数。

PyTorch 模型的转换数据

在将数据输入 NN 模型之前,我需要将 list 和 NumPy 数组数据转换为 PyTorch 张量。

import torch
import torch.nn as nn
import torch.nn.functional as FX_t = torch.tensor(X,*dtype*=torch.float32)
y_t = torch.tensor(y,*dtype*=torch.float32)
X_t = X_t.view(X_t.shape[0],1)
y_t = y_t.view(y_t.shape[0],1)

现在, X_ty_t 都是 2d 张量数组。

定义具有一个隐藏层的神经网络模型

在这里,我将定义一个简单的神经网络模型,只有一个隐藏层,隐藏层神经数字设置为 16 更容易可视化。在代码中,我将把数字增加到 128

具有一个隐藏层的 NN。Andrew Zhu 使用此工具生成的图像

代码如下:

*class* func_simulator(nn.Module):
    *def* __init__(*self*):
        super(func_simulator,*self*).__init__()
        *self*.l1 = nn.Linear(1,128)
        *self*.l2 = nn.Linear(128,1) *def* forward(*self*,*x*):
        out = **F.relu**(*self*.l1(*x*))
        out = *self*.l2(out)
        return out

ReLU 激活是这里的关键,不要改成乙状结肠。

训练并验证结果

现在,让我们设置学习率,历元数,损失,梯度函数开始训练。

learning_rate,num_epochs    = 0.0001,100000
model                       = func_simulator()
loss                        = nn.MSELoss()
gradient = torch.optim.SGD(model.parameters(),*lr*=learning_rate)# start training
for epoch in range(num_epochs):
    y_pred = model.forward(X_t)
    l = loss(y_pred,y_t)
    l.backward()
    gradient.step()
    gradient.zero_grad()
    if(epoch+1)%1000==0:
        print(*f*'epoch:{epoch+1},loss={l.item()*:.3f*}')

查看结果:

predicted = model(X_t).detach().numpy()
plt.plot(X_t,y_t,'ro')
plt.plot(X_t,predicted,'b')
plt.show()

带有一个隐藏层的模拟线条,图像由 Andrew Zhu 提供

红点是训练点,蓝线是来自已训练模型的点。模拟在一些区域不是很好,特别是开始和结束部分,我添加了另一个隐藏层,看看这两层是否能带来更好的模拟。

用两个隐藏层微调模型

具有两个隐藏层的 NN。Andrew Zhu 使用此工具生成的图像

*class* func_simulator(nn.Module):
    *def* __init__(*self*):
        super(func_simulator,*self*).__init__()
        *self*.l1 = nn.Linear(1,128)
        ***self*.l2 = nn.Linear(128,10)**
        ***self*.l3 = nn.Linear(10,1)** *def* forward(*self*,*x*):
        out = F.relu(*self*.l1(*x*))
        **out = F.relu(*self*.l2(out))**
        out = *self*.l3(out)
        return out

我强调了与单层版本不同的代码,2 层模型可以生成更好的模拟。

红点是实数,蓝线是 NN 模拟的线。模拟线与两个隐藏层,图像由安德鲁朱

包裹

为什么神经网络模型可以模拟任何函数?这种强大的功能是从神经网络模型的本质继承而来的。本质上,神经网络是一个多层参数系统。提供给模型的训练数据越多,需要的参数就越多(层也越多)。一层中每个神经捕捉一个微小特征,几个组合的神经确定一个主要特征,等等。

从上面的示例中,我们可以看到,与只有一个隐藏层的模型相比,两层神经网络生成了更好的拟合线。训练数据越多,训练模型需要的神经元和层数就越多。但最终,如果存在一台无限的计算机,有足够的数据,我们可以模拟几乎任何功能。

当我意识到,只要有足够的训练数据,计算机和神经网络就可以模拟任何函数。我对图像对象检测的理解提高了,并导致了一个更令人震惊的猜想:

如果存在一台超级强大的计算机,它能模拟我们的现实世界,甚至整个宇宙吗?

也许我们都生活在一个模拟的世界里。

参考

  1. 神经网络能解决任何问题吗?
  2. 神经网络可以计算任何函数的直观证明
  3. 为什么神经网络几乎可以学习任何东西
  4. 深度前馈网络

附录—代码

这里是本文中使用的完整代码,您可以在安装了 PyTorch 的机器上复制并运行它,不需要 GPU。

用 SimPy 模拟哲学家进餐

原文:https://towardsdatascience.com/simulating-dining-philosophers-with-simpy-5abf5106e2ca

探索 Python 中的竞争条件、死锁和共享资源

照片由玛丽莎·哈里斯Unsplash 拍摄

哲学家进餐问题是计算机科学中的一个问题,特别是在并发系统中。它最初是由埃德格·迪杰斯特拉发明的一种考试题型,很快就采用了现在的形式,并成为一种经典。这可以被视为一个玩具问题,但它有效地展示了资源争夺的基本难题。今天,我们将把它作为一个完美的借口来学习关于 Simpy 的教程,这是一个 Python 的离散事件模拟包,非常适合对问题进行建模。设置描述如下:

圆桌旁坐着 K 个哲学家。他们每个人面前都有一碗米饭,还有一根筷子——只有一根!—在每对相邻的碗之间。每个哲学家思考的时间是不确定的,然后变得饥饿,不得不吃东西。吃饭时,他/她必须同时拿到左右筷子。请注意,相邻的哲学家,因此,必须交替使用筷子坐在他们之间。吃完后,哲学家放下筷子,开始新的思考。

相当不切实际的是,哲学家们不会自发地交流,也不会担心共用筷子而不用时不洗,但这只是一个模型!我们可以开始使用 Simpy 编写一个 Python 函数来模拟K 用餐的哲学家,同时介绍主要概念。

在 Simpy 中,所有模拟的东西都存在于第 4 行声明的Environment(在我们的例子中是table)的实例中。当存在有人会争夺的资源时,环境变得有趣起来:在我们的例子中是chopsticks,它是Resource类的K实例的列表。一个Resouce必须引用一个环境,在我们的例子中是(不出所料)table,并且有一个capacity,这意味着资源可以同时满足多少个使用请求。一根筷子的容量是 1,因为它一次只能被一个哲学家使用(容量为> 1 的资源的一个例子是,例如,有多个隔间的浴室)。
在第 10–11 行,我们使用tableprocess方法将K进程添加到环境中,每个哲学家一个进程。流程是事件发生器;在我们的例子中,他们会发出一些事件,比如抓住一根筷子(一旦它变得可用)并释放它。当我们看下一段代码时,这一点可能会更清楚。我个人发现,没有过程,环境和资源是没有意义的……反之亦然:你需要考虑两者才能有完整的图景。根据我们所写的,这些进程是对philosopher_process的调用,而这又是我们函数的一个参数。我们必须传递一些可以用正确的签名调用的东西。

既然我们已经解决了环境问题,我们可以研究哲学家了。归根结底,这个问题很有趣,因为如果放任自流,哲学家们可能会做出一些行为,将他们带入一种僵局状态,在这种状态下,不可能有任何进展,他们将会挨饿。例如,假设他们使用以下程序:

while True:
    # 1-think for a random amount of time
    # 2-grab left chopstick
    # 3-grab right chopstick
    # 4-eat
    # 5-release left chopstick
    # 6-release right chopstick

正如我们在上一节中看到的,哲学家由一个进程表示,事实上我们可以从上面的伪代码中看到,它将发出筷子的使用请求。因此,我们将编写一个函数——特别是用 Python 行话编写一个生成器。让我们把它编码进去并仔细检查一下。

这里面肯定有相当多的陈述。Simpy 中的一个进程可以通过yield产生尽可能多的事件,在底层,Simpy 引擎将遍历所有声明的进程生成的事件,并使它们以正确的顺序发生。在哲学家的例子中,我们允许(并希望)它永远继续下去,但这不是必须的:一个过程也可以在产生了有限数量的事件后明确终止,如果这是建模对象的性质的话。
我们在这里发布三种类型的事件:

  • Resource.request()发布资源使用请求。在引擎满足该请求之前,发布流程不会继续执行。如果资源不可用(换句话说,由于先前的请求而满负荷),模拟将继续进行,直到它变得可用(如果曾经可用的话!)之后再将资源分配给进程并继续执行。
  • Resource.release(request)向先前请求的资源返回 1 个单位的能力。它需要一个先前完成的请求作为参数。
  • Environment.timeout(duration)指示 Simpy 引擎继续模拟一段时间duration,然后返回并继续执行。在此期间,其他进程自然会发出自己的事件。在 Simpy 模拟中,超时是时间流逝的唯一方式!如果其间没有超时,所有其他事件都将在时间 0 生成。

注意,抓住或放下筷子不是一个原子操作,但是它有一个持续时间handling_time。代码的其余部分应该非常简单。我们建立了一个小的日志系统,将记录附加到一个列表中,这将允许我们更清楚地看到最终发生了什么。此外,philosopher的签名包含一些额外的配置参数,在将它传递给我们的run_dining_philosophers函数之前,我们必须修复这些参数(例如通过使用部分)。让我们在一个主脚本中将所有这些联系在一起,并尝试一下:

首先,这段代码终止,即使进程内部有一个无限循环。为什么它们以某种方式停止生成事件,Simpy 认识到这一点并停止模拟。让我们看看打印的日志:

<T=210131.76>  Phil_#0 is hungry!
<T=210133.76>  Phil_#0 has taken L chopstick (#0)
<T=210140.80>  Phil_#1 is done, releases chopsticks #1/#2
<T=210144.80>  Phil_#0 has taken R chopstick (#1) and can eat
<T=210145.72>  Phil_#3 is done, releases chopsticks #3/#4
<T=210164.80>  Phil_#0 is done, releases chopsticks #0/#1
<T=210179.26>  Phil_#4 is hungry!
<T=210181.26>  Phil_#4 has taken L chopstick (#4)
<T=210183.26>  Phil_#4 has taken R chopstick (#0) and can eat
<T=210203.26>  Phil_#4 is done, releases chopsticks #4/#0
<T=210214.71>  Phil_#3 is hungry!
<T=210215.58>  Phil_#2 is hungry!
<T=210215.80>  Phil_#4 is hungry!
<T=210216.71>  Phil_#3 has taken L chopstick (#3)
<T=210217.14>  Phil_#0 is hungry!
<T=210217.58>  Phil_#2 has taken L chopstick (#2)
<T=210217.80>  Phil_#4 has taken L chopstick (#4)
<T=210217.82>  Phil_#1 is hungry!
<T=210219.14>  Phil_#0 has taken L chopstick (#0)
<T=210219.82>  Phil_#1 has taken L chopstick (#1)

在某一点上,所有思考的哲学家同时变得饥饿,在需要实际抓住他们各自左边筷子的短暂时间里,他们都发现他们的邻居抓住了他们右边的那根筷子。它们不能按照当前的算法进行。他们陷入了僵局。

让我们来看看这个问题的一个可能的解决方案:奇怪的哲学家从左边的筷子开始,甚至哲学家也从右边的筷子开始。

如果你使用这个版本的哲学家进程,而不是以前的,死锁消失,程序不会停止。我喜欢这种解决方案,因为它有些优雅,不需要额外的同步原语,只需要一点小小的修改就可以完全避免死锁,但它在实践中有一个主要问题:如果K是奇数,最后一个哲学家比其他人有优势,*均起来,他等待筷子的时间会更少。事实上,这两个哲学家 0-1,2-3…会希望他们之间的筷子是他们的第一根筷子,但最后一个哲学家没有这样的竞争对手来争夺他/她的第一根筷子,这是一个优势。考虑到公*性要求、不对称性和其他微妙的交互现象,该问题还有其他解决方案(以及该问题本身的其他变体),如果您对共享资源和同步感兴趣,您应该看看它们。然而,通过这个相对简单的设置,我希望已经给出了它的要点并展示了 Simpy 的基本原理!

作者迷因

[1]如果处理确实是原子性的,这个问题无论如何都会存在,但在实践中基本上不会发生,例如解决方案#2 中所示,这里的为每根筷子包含一个互斥体 Simpy 资源确实是互斥体。事实上,如果将handling_time设置为 0,死锁的概率是天文数字。

模拟 R 中的统计功效

原文:https://towardsdatascience.com/simulating-statistical-power-in-r-9eba698625e7

一个教程(带代码!)解释了支撑疫苗试验设计的重要概念、A/B 检验的有效性以及推动社会科学中的“再现性危机”。

假设你有两个硬币:硬币 A 是公*的,硬币 B 在 90%的情况下会正面朝上。如果让你找出哪个硬币是公*的,哪个是有偏见的,你可能会把两个硬币都抛过来,并记录下每个硬币正面朝上的频率。如果你这样做的次数足够多,硬币 B 就会变得明显有偏差。但是每枚硬币你应该掷几次呢?也就是说,你需要多大的硬币投掷样本才能正确判断出哪枚硬币是有偏差的?

乔丹·罗兰Unsplash 上的照片

即使硬币 B 几乎总是正面朝上,你有时也会翻转反面。因此,在任何给定的样本中,你可能只是运气不好,硬币 B 看起来很公*,尽管它并不公*。如果你把每枚硬币抛了很多次,就不太可能出现这种情况,但这需要付出努力,而且你永远无法保证自己不会倒霉,所以问题是:你对自己犯错有多满意?如果硬币 B 只有 60%的机会正面朝上呢?凭直觉,你似乎需要一个更大的样本,因为硬币现在更相似了,但是要大多少呢?

这些都是关于统计力的问题。通俗地说,异能就是你检测效果的能力。用更通俗的术语来说,权力是识别一个效果的可能性,以一个人在场为条件。权力支撑着疫苗试验和 A/B 试验的设计,也是社会科学中“再现性危机”的驱动因素。确保你有足够的权力意味着你可以更确定你的统计数据告诉你什么。动力不足意味着你发现的任何效应都是偶然的结果,你没有发现的任何效应可能只是因为你没有足够的数据*。

什么是权力?

统计功效是识别一个效应的概率,以一个效应存在为条件。功效通常是你的假设效应大小、你的观察结果的可变性、你的样本大小、你的信心阈值的函数。要获得一个像样的概述,请参见:科恩的权力初级读本(1992)

照片由 Raphael RenterUnsplash 上拍摄

在硬币的例子中,效果大小是硬币 A 正面朝上和硬币 B 正面朝上的频率之差;更大的影响更容易被发现。在硬币的例子中,你被告知硬币 B 有多大的偏差,但是在现实世界中,你必须对影响大小做出合理的猜测。样本大小是你掷硬币的次数;较大的样本不太可能是“不幸的”,因此也增加了功率。你的信心阈值是你对说有影响而实际上可能没有影响的容忍度。

功效分析最常用于确定:a)您的分析需要多大的样本,以及 b)您能够检测到的最小效应大小。这是通过保持其他因素不变,并增加/减少感兴趣的变量(样本大小或效果大小)直到达到功效阈值来实现的。一般来说,研究人员选择 0.80 的任意功率阈值,这意味着您有 80%的功率,并且能够在 80%的时间内检测到该量级的真实影响。

模拟能力

虽然存在用于计算功率的标准公式和软件,但在许多情况下它们可能不适用(例如,当使用随机化推断、混合效应模型等时..参见:阿诺德等人,2011 年)。另一种方法是进行模拟。模拟可以针对所有可能的设计运行,并且更加健壮。它们还增加了生态学的有效性,因为从理论上讲,您可以采用完全相同的分析方法(甚至相同的 R 代码),并将其应用到您的实际数据中。最后,模拟迫使你明确你对数据生成过程的假设。

在硬币的例子中,你的力量模拟的一次运行将包括模拟一组投掷硬币的次数,比较硬币 A 和硬币 B 的正面率,运行统计测试以确定硬币 A 是否!=硬币 B,并记录结果。你将重复这个过程很多次,并计算你正确说出硬币 B 有偏差的次数的百分比:这是你的功率估计。

虽然像 SIMR 这样的 R 包是用于运行功率模拟的,但我发现它们可能限制过多。本文将概述我是如何以最小的依赖性组织我的功率模拟的。虽然硬币的例子是有用的,但这些模拟考虑了随机实验需要多少参与者来提高 4 所学校的出勤率。

五个电源模拟依赖项。其中三个只是为了可视化输出

模拟结构

我组织我的模拟有三个重要的功能:

  1. 数据生成器:根据你的世界模型创建数据样本。
  2. 评估者:获取样本并返回包含效果评估的模型(两个硬币有什么区别?)
  3. 鉴别器:接受模型并确定该效应是否可信(统计显著)。

这些都用在您的组织能力模拟函数中,该函数将重复调用所有三个函数,并返回您的鉴别器正确发现效果的运行比例。

数据生成器

你的数据生成器将你所做的关于你的实验在现实世界中如何工作的所有假设整理成文。通常,这将包括关于观察独立性、恒定治疗效果、共同支持、非发现性等的假设。然而,在模拟您的功效分析时,您可以根据需要调整这些假设。鉴于功效分析的两个有趣问题是关于最小样本量最小可检测效应量,我将它们作为参数包括在内。每次调用数据生成器函数时,它都会创建一个具有完全相同属性的全新数据样本。

使用您的数据生成器函数对数据生成过程进行建模。

在这个例子中,每一行代表 4 所不同学校中的一所学校的学生;我们也有那个学生那学期出勤的天数。w 代表治疗指标,我们假设该指标可提高 5 天的出勤率。我们还假设不同的学校有不同的基线出勤率,并且在个体层面存在一些随机噪声。

来自数据生成器的样本模拟了对四所学校的干预对出勤率的影响。左侧图的样本量为 50;右图的样本大小为 5000。两者都模拟了相同的 5 天效应大小。图片作者。

正如我们可以看到的,当运行我们的数据生成器功能时,具有相同的效果大小(额外参加了 5 天)但不同的样本大小,我们得到了一个非常不同的图片。重要的是,小样本运行(左,n=50)比大样本(右,n=5000)噪声大得多。

评估者

您的估计器接收数据样本,并尝试恢复您感兴趣的属性。重要的是,它返回某种类型的模型,允许您总结数据生成过程。在硬币示例中,感兴趣的属性将是硬币 A 与硬币 b 相比出现正面的频率。在我们的学校示例中,感兴趣的属性是在控制其他因素后,治疗组与对照组相比参加的天数。理想情况下,您的估计函数应该执行与您希望对实际数据执行的分析相同的分析。值得注意的是,使用的估计量也会影响统计功效,因为有些估计量的效率比其他估计量低。

然后传递给估计器函数的数据生成器函数的新样本。在这里,我们使用一个线性模型,其中包括治疗和学校指标,以模拟每个学生参加的天数。

在这个例子中,我们使用线性回归,包括治疗指标 W,以及作为对照的学校指标。由于样本量小(左),该模型给出的估计不精确,标准误差大。较大的样本量能够自信地恢复我们在数据生成函数中编码的参数,包括治疗效果和学校基线出勤率的差异。

数据生成器的两个样本的估计函数的结果。请注意,右侧的参数估计值具有小得多的标准误差,并且非常接*我们的数据生成器中编码的真实值。图片作者。

鉴别器

鉴别器接受你的模型作为输入,并给出你的原始问题的答案,给出数据样本和分析方法。在我们的硬币示例中,这将评估硬币 A 和硬币 b 之间的差异。在我们的学校示例中,这将确定治疗指标的系数在我们设定的置信阈值下是否具有统计显著性。即检查治疗指示器的 p 值是否低于 0.05。

从下面可以看出,当使用大样本时,我们可以说我们的治疗对出勤率有统计学上的显著影响,但我们不能用小样本。但是请注意,如果我们再次运行这个代码块,我们可能会得到不同的结果(因为我们将使用新的数据样本)。

虽然这对于本例来说是微不足道的,但在某些情况下,您可能需要一个更复杂的鉴别器。例如,如果我们学校试验中的治疗分配不是在个人层面,而是在学校层面,那么您可能希望在学校层面对您的标准误进行聚类(Abadie 等人,2017)。或者,如果您的研究涵盖整个人群,并且您正在基于设计的推理框架内工作,您可能希望使用随机化推理来确定统计显著性(Abadie 等人,2020)。通过使用电源模拟,您可以根据需要交换这些不同的方法。

把所有的放在一起

一旦定义了数据发生器、估计器和鉴别器,就可以运行全功率仿真了。这个函数接受你的三个函数作为输入以及一些模拟参数(例如,你想在模拟中运行多少次?).对于模拟中的每次运行,您生成一个新的数据样本,将它传递给您的估计器,并记录您的鉴别器是否能够确定一个效果。最后,力量模拟会返回您正确识别效果的次数比例。这是你统计能力的*似值。

由于您经常想要改变数据生成器中的效果大小/样本大小,我传递了一个参数化的(一个返回带有设定参数的函数的函数)版本的数据生成器。

在我们学校的例子中,我们的治疗增加了 5 天的出勤率。这种影响在不同的学校是不变的,这些学校有不同的基线出勤率。如果我们的研究只包括 50 名学生,我们只能说我们的治疗在大约 40%的时间里有效。如果我们的研究包括 5000 人,我们可能永远不会犯错误,说我们的治疗没有帮助。

因此,如果我们希望以 80%的功率供电,那么样本大小为 50 时,我们的功率就会不足,因为该设计的功率只有 40%。然而,如果 80%是我们的功率阈值,5000 名学生可能太多了,因为我们的功率接* 100%**。显然,能够确定地发现您的干预的效果是令人敬畏的,但是考虑到资源限制,5000 可能是多余的。

功率曲线

在我们学校的研究示例中,50 名学生参与者太少,5000 名则太多。为了找出满足我们的置信阈值所需的样本量,我们可以生成一条功效曲线。这是通过保持所有其他因素不变并增加样本大小,直到我们的功率模拟达到 80%的功率来实现的。

下图显示了样本规模从 20 到 3000 名学生的 4 种不同效应规模(0 天、2 天、5 天和 10 天出勤天数增加)的功效曲线。达到我们预定义的 0.80 (80%)的功效阈值所需的样本量取决于我们认为我们的干预将对出勤率产生多大的影响。如果我们认为它会有很大的效果,我们只需要招募 20 名学生就可以很好地观察效果。如果我们认为它会有一个小的影响,我们需要招募数百人。

幂曲线显示您通过注册学生人数(x 轴)检测治疗对学校出勤率(幂,y 轴)的影响的能力。这是针对四种不同的效果大小进行的。功率阈值为水*虚线,表示“功率充足”。图片作者。

如果干预没有任何效果,也没有提高出勤率,那么我们招收多少学生就无关紧要了。在这种情况下,我们只报告了一个假阳性,并且大约有 5%的时间“发现了一个效应”,如果您还记得的话,这是我们在鉴别器中设置的置信阈值。因此,你可以知道你的假阳性率在这些分析中也没有被夸大。

反过来,如果你的样本大小是固定的——可能是因为预算限制——你可以找到最小的可检测效应大小。随着你的效果尺寸变大,你的力量也会增加,希望直到你达到你的力量阈值。这就给了你一个基准,来衡量你的治疗有多有效,才能被你的分析检测出来。

结论

功效分析对于确定统计分析的可信度非常重要。需要它们来决定你应该收集多大的样本,或者需要多大的影响才能让你观察到它。虽然存在用于估计功率的标准方程,但是模拟是灵活且简单的*似,可用于任何研究设计。它们还迫使你考虑进入你的数据生成过程的心理模型的假设,以及它们与你的分析技术的一致性。

完整代码在这里:https://github . com/Cameron-raymond/power-simulation-tutorial

更多关于数据科学、因果推理、ML 和社会科学的博文,请查看我的网站:【https://cameronraymond.me/

由此得到的教训是而不是*收集数据,直到你发现一个效果。样本大小应该预先确定,以避免 p-hacking。

**实际上,您永远无法使幂=1,但在本次模拟中,我们仅运行了 500 次,从未遇到过无法拒绝零假设的样本。

参考

Abadie,a .,Athey,s .,Imbens,G. W .,& Wooldridge,J. (2017 年)。什么时候应该调整聚类的标准误差?(编号 w24003)。美国国家经济研究局。

a .阿巴迪、a .阿塞、s .伊本斯、G. W .、伍尔德里奇和 J. M. (2020 年)。回归分析中基于抽样与基于设计的不确定性。计量经济学88 (1),265–296。

阿诺德,B. F .,霍根,D. R .,科尔福德,J. M .,,哈伯德,A. E. (2011)。估算设计功率的模拟方法:应用研究综述。BMC 医学研究方法论,11(1),1–10。

科恩,雅各布。"动力引丨爆丨器"心理学通报 112.1 (1992): 155。

X 射线计算机断层成像的模拟

原文:https://towardsdatascience.com/simulation-of-x-ray-computed-tomography-2579f1ad2652

拉冬变换的一个应用

图 1:显示拉冬变换的动画。顶部:输入图像。下图:对应的拉冬变换。由生成此代码。图片由作者提供。

在电子制造业中,用 X 射线计算机断层扫描检查两个分立元件之间的互连是常见的做法。例如,球栅阵列(BGA) 是连接印刷电路板(PCB)和芯片的小焊球的密集网格。我们希望确保每个球都正确焊接到芯片和 PCB 的金属焊盘上。如果我们对组装好的封装进行垂直 X 射线检测,就像你的牙医获取你牙齿的图像一样,最终的图像质量会受到芯片和 PCB 中多层金属结构的影响。每个金属结构都充当一个 X 射线衰减器,形成遮挡,可能会阻止检测 BGA *面中的细微缺陷(通常是孤立的焊料卫星、桥和异常球)。

图 2:球栅阵列(BGA)位于 PCB 和芯片之间,不完全可见。图片由作者提供。

BGA 检查场景

你是计算机视觉工程师,负责检查装配线上的 BGA,就在元件被焊接到熔炉中之后。您考虑从阵列的侧面拍照,但是您很快意识到除了第一行连接之外,您看不到更远的地方。你在寻找可以在芯片下任何地方找到的缺陷,所以你已经排除了这个选项。

经过大量的谷歌搜索,你找到了一个基于 X 射线计算机断层扫描的解决方案。作为一名杰出的制造商,您构建了一个一维 X 射线成像系统,可以从多个角度捕捉 BGA 吸收的切片(参见图 3)。

警告,孩子们:不要在家里这样做,除非在一个拥有高能辐射博士学位的成年人的监督下。

再三考虑后,不要这样做,句号。

图 3:X 射线成像系统。图片由作者提供。

您的成像系统沿着 363 个 X 射线传感器的线性阵列长度产生 BGA 集成密度的切片。旋转机构可以选择投影的角度θ。你可以在这里找到模拟这个系统的代码。

经过几天的对齐调整,你终于成功地获得了一个通过水*堆叠 BGA 的投影构建的图像,以 256 个离散的角度,从- π /2 到 π /2:

图 BGA *面正弦图。图片由作者提供。

我们称这样的图像为正弦图,因为原始密度图像中的一个点在堆叠切片的图像中产生了正弦曲线。

你现在做什么?图 4 的正弦图如何揭示 BGA *面上的密度图?

拉冬变换

通过在给定角度的*行射线上整合 2D 密度图来创建线性轮廓的操作是拉冬变换

图 5:(ρ,θ)中 Radon 变换的值是 f(x,y)沿(ρ,θ)定义的线的积分。图片由作者提供。

f(x,y)的拉冬变换由下式定义:

我们在图 3 中所示的旋转源和传感器的设置执行了对应于 BGA *面的 X 射线局部吸收系数的函数的 Radon 变换。由于我们对 X 射线吸收图感兴趣,我们需要计算逆拉冬变换

逆拉冬变换

我们可以通过利用傅立叶切片定理得到逆 Radon 变换的*似:

对于给定的θ=θ₀,Rf(ρ,θ₀)(在ρ上积分)的 1D 傅立叶变换是 F(u,v)(f(x,y)的 2D 傅立叶变换)穿过原点并沿着θ₀ + π /2 的切片视图。

图 4 中的每一列都生成了一个 1D 频谱,并粘贴到 2D 频谱 F(u,v)中。有了大量的投影,我们可以建立一个“径向密集”的 2D 光谱(参见图 6)。

图 6:傅立叶切片定理。图片由作者提供。

你的意思是我们必须把任意角度的 1D 谱粘贴到定义在矩形网格上的 2D 谱上吗?

是的。你可以猜到,逆拉冬变换是一个插值节。幸运的是, skimage 库已经为我们完成了这项工作,计算 Radon 逆变换非常简单:

from skimage.transform import iradon
reconstruction = iradon(sinogram, theta=thetas, filter_name='ramp')

BGA 密度图

将逆 Radon 变换应用于图 4 的正弦图,我们获得以下密度图:

图 7:图 4 的逆 Radon 变换。图片由作者提供。

因为逆 Radon 变换不精确,所以重建有点嘈杂。质量受到投影角度的数量和用于将 2D 谱的矩形点阵与 1D 切片的极坐标匹配的插值方法的影响。

然而,我们可以看到 BGA 的 16 x 16 单个金属连接。在图像中心的左侧,有一个不应该在那里的小而密集的物体。我们发现了一个微珠,这是影响 BGA 的缺陷之一,只有通过计算机断层扫描才能看到。

结论

我们用计算机断层扫描模拟了 BGA 的检测。这让我们引入了拉冬变换及其逆运算。

我们通过整合多个角度的投影来构建 BGA 的正弦图。正弦图的逆拉冬变换揭示了物体密度图。

有奖游戏

在正弦图中,小物体表现为正弦曲线。我们 BGA 里的微珠打破了对称性。你能感觉到图 4 中看起来不同相的微弱正弦波吗?

[1]我们可以将 X 射线沿路径的总吸收建模为一系列短传播长度δX 上的指数吸收的乘积,其中局部吸收系数为αi:

图 8:X 射线强度随着射线穿过被测物体而降低。图片由作者提供。

检测到的强度 Id 与源强度 I₀的比值可以写为:

等式(4)告诉我们,-ln(Id/I₀与 x 射线沿其穿过材料的路径遇到的局部吸收系数αi 的总和(极限情况下的积分)成比例。这就是为什么我们可以将 X 射线投影视为物*面中密度图的积分。

[2]

傅立叶切片定理的证明

图 9:密度图 f(x,y)及其 2D 傅立叶变换 F(u,v)。图片由作者提供。

我们需要展示:

将(1)插入到 1D 傅立叶变换的定义中:

因为变量从(z,ρ)到(x,y)的变化对应于轴的旋转而没有标度变化,所以整个(z,ρ)*面上的积分与整个(x,y)*面上的积分相同。

SimPy 模拟

原文:https://towardsdatascience.com/simulation-with-simpy-287cbdf5d78e

第 7 部分:非终止模拟

照片由 JC GellidonUnsplash 上拍摄

在本系列的第四篇文章中,我们区分了关于输出数据分析的两种类型的模拟:[1]终止模拟;[2]非终止模拟。

终止模拟是指运行一段时间,直到某个预定义事件标志着运行结束。该事件被称为“终端事件”,它必须在运行前指定,其发生时间可能是一个随机变量。模型的初始条件必须在运行前完全建立,因为我们关心的是被研究系统的短期动态行为

另一方面,非终止模拟是一种我们关心被研究系统的稳态性能的模拟。模型运行了很长一段时间:没有预定义的终端事件来指定每次运行的长度。当然,我们必须在某个时间点停止运行。建立停止点有不同的技术,我们将在本文中讨论其中的一种。

非终止模拟与终止模拟有几个概念上的不同:

1)模型的初始条件不需要完全建立,因为它们的影响随着运行的长度而显著降低。

2)最初,有一个瞬态,在此状态下,性能指标在稳定之前会大幅波动。通常的程序是从瞬态中移除数据,以便它们不会对系统的稳态性能产生任何影响。

3)不可避免地,在非常长的运行中,出现自相关现象,因此在计算模型的稳态参数时出现偏差

已经开发了几种方法和程序来确保非终止模拟的输出数据真正代表所研究系统的真实稳态行为。也许最著名的是分批*均法。

批量方法指

让我们记住( article 4 )在终止模拟中,我们执行 n 个独立复制。每次运行的独立性通过在每次运行中使用不同的随机数流来实现。每次复制都以相同的初始条件开始。最后,我们计算每个性能指标的点估计和置信区间。

另一方面,我们在一个非终止的模拟中进行一个单独的长期运行。

非终止模拟的批量*均方法由以下步骤组成:

1)做一次非常非常长的跑步。通过消除初始瞬态的所有数据来消除瞬态效应。这被称为预热阶段。它的长度通常是通过初步的试运行来确定的。

2)剩余长度(有效行程)被分成等长的 n 个区间,称为批次

  1. 每批被认为是一个独立的复制。因此,我们可以通过将每批视为一个独立的复制来计算样本均值、样本方差和置信区间及其相应的置信水*(*均性能指标)。

这是一个非常简单的程序,尽管有不同的方法涉及****批次数量和每批的长度。一些作者认为至少应该有 20 至 30 个批次,而其他作者认为由于自相关问题,不应超过 20 个批次。一般认为,批次应等于或长于预热时间,以确保在一定程度上接*独立重复的条件。

用 SimPy 模拟急诊室

急诊室是医院或医疗机构中的一个部门,当病人病危或受伤并需要尽快治疗时,他们会去那里。

急诊室的操作方面通常被分析为 排队系统 ( 等候线系统),其中患者随机到达,并排队等候(或不排队),直到他们被初步筛选以确定他们的严重程度。这些患者中有一定比例的人需要住院治疗,另一个比例的人需要门诊治疗,其余的人不需要额外治疗,并在确认他们的病情不需要任何治疗后离开系统。

我们将使用 SimPy 来模拟在一个虚构的急诊室中对人员分配的可能修改,该急诊室包括一个入院区,以及分配给以下三个区域的医生:医院护理;门诊护理;不在乎。

首先,我们导入了 SimPy,Pandas 作为 pd, Numpy 作为 np ,以及 scipy.stats 模块,该模块包含了再现服从均匀分布和截尾正态分布的随机数序列的方法。

@author: darwt
"""## Emergency Room Simulation Model
## Non Terminating Simulation
## Batch Means Technique# Import Modulesimport pandas as pd
import numpy  as np
from numpy.random import RandomStateimport simpyfrom scipy import stats
from scipy.stats import uniform
from scipy.stats import truncnorm

我们包含了一个初始化模块来指示代表患者到达率的泊松分布的参数值。我们还显示了住院服务台的数量和医院护理、门诊护理和非护理级别的医生数量。我们认为住院、住院和流动区域的住院时间遵循正态分布,其*均值和标准偏差值在模块中显示。不关心水*遵循均匀分布,因此我们指出了它的最小值和最大值。我们还初始化了模拟运行的长度,以及用于存储中间结果的列表。第 3 条中所示的类似逻辑用于将患者分为三个护理级别。

# initialization module
# Unit of time = hoursPATIENT_ARRIVAL_RATE  = 1.2NUMBER_ADMISSIONS_DESKS = 2ADMISSION_MEAN = 0.3
ADMISSION_STD  = 0.15HOSPITAL_MEAN = 25
HOSPITAL_STD  = 1.5AMBULATORY_MEAN = 6
AMBULATORY_STD  = 1NO_CARE_INF = 0.5
NO_CARE_SUP = 1.0NUMBER_DOCS_HOSPITAL= 1
NUMBER_DOCS_AMBULAT = 5
NUMBER_DOCS_NO_CARE = 1# discrete probabilities for three care levels
prob1, prob2, prob3 = 0.3, 0.6, 0.1prob1 = round(prob1, 2)
prob2 = round(prob1 + prob2,2)
prob3 = round(prob2 + prob3,2)
list_of_probs = [prob1, prob2, prob3]patient_arrival, arrival = [], []
patient_admission, patient_hospital_care = [], []
patient_ambulatory_care, patient_no_care = [], []
time_in_admission,       delay_in_admission = [], []
time_in_hospital_care,   delay_in_hospital_care   = [], []
time_in_ambulatory_care, delay_in_ambulatory_care = [], []
time_in_no_care,         delay_in_no_care         = [], []SIM_TIME = 3000

利用生成器功能生成患者来模拟患者的到达。我们计算住院和三级护理的住院时间。 yield env.timeout() 是一种离散事件,在经过一定的模拟时间后触发。

def generate_patient(env, patient_arrival_rate,inital_delay = 0,                 
              stoptime = simpy.core.Infinity,prng = RandomState(0)): number_of_patients = 0
    yield env.timeout(inital_delay)     # Initial delay    while (env.now <stoptime):  
       inter_arrival_time=prng.exponential(1.0/patient_arrival_rate) left, right = 0, np.inf 
       loc1, scale1  = ADMISSION_MEAN, ADMISSION_STD
       loc2, scale2  = HOSPITAL_MEAN,  HOSPITAL_STD
       loc3, scale3  = AMBULATORY_MEAN,AMBULATORY_STD a1 = (left -  loc1)/scale1
       b1 = (right - loc1)/scale1
       a2 = (left -  loc2)/scale2
       b2 = (right - loc2)/scale2
       a3 = (left -  loc3)/scale3
       b3 = (right - loc3)/scale3       los_admis = truncnorm.rvs(a1,b1,loc1, scale1 ,size=1)
       los_hosp  = truncnorm.rvs(a2,b2,loc2, scale2 ,size=1)
       los_ambu  = truncnorm.rvs(a3,b3,loc3, scale3 ,size=1)
       los_exit  = uniform.rvs(loc = NO_CARE_INF, 
                               scale = NO_CARE_SUP + 1,size=1) number_of_patients += 1
       pat_str = patient_stream(env, 'Patient number: {}'.format(number_of_patients),
              los_admis, los_hosp,los_ambu, los_exit) env.process(pat_str)
       yield env.timeout(inter_arrival_time)

Simpy 在仿真建模中使用 流程交互方法patient_stream 生成器函数描述了单个患者在整个系统中的流动,代码模拟了这些患者在急诊室模型中的路线中的体验。方法 request() 生成一个事件,该事件在资源再次可用时被触发( yield req )。进程请求那些资源成为用户,并且一旦完成就必须释放

def patient_stream(env, patient_number, los_admis, 
                   los_hosp, los_ambu, los_exit): #  Admission
    print("%3s is arriving for admission at %.2f" %(patient_number, env.now))
    patient_arrival.append(patient_number)
    arrival_time = env.now
    arrival.append(arrival_time) adm_request_desk = admission_desks.request()
    yield adm_request_desk
    print("%3s admitted to admission at %.2f" %(patient_number, env.now))
    time_in_admission.append(env.now) if (env.now > arrival_time):
        delay_in_admission.append(env.now - arrival_time)
        patient_admission.append(patient_number)
        print("%3s has to wait %.2f' for admission" %(patient_number, env.now - arrival_time)) yield env.timeout(los_admis)
    print('%3s stays at admission %.2f' % (patient_number,los_admis)) admission_desks.release(adm_request_desk) # uniform distribution for level assigments
    r_v = uniform.rvs(size=1)
    if r_v < prob1:
       stream = 1
    elif r_v < prob2:
       stream = 2
    else:
       stream = 3 if stream == 1:
       #  Hospital Care
       print('%3s is arriving for hospital care at %.2f' % (patient_number,env.now)) arrival_time = env.now
       time_in_hospital_care.append(arrival_time) hospital_care_request = hospital_care.request()
       yield hospital_care_request
       print('%3s is admitted to hospital care at %.2f' % (patient_number,env.now)) if (env.now > arrival_time):
          delay_in_hospital_care.append(env.now - arrival_time)
          patient_hospital_care.append(patient_number)
          print('%3s has to wait for hospital care for %.2f'
                 %(patient_number, env.now - arrival_time)) 

       yield env.timeout(los_hosp)
       print('%3s stays at hospital care at %.2f' % (patient_number,los_hosp)) hospital_care.release(hospital_care_request) elif stream == 2:
       #  ambulatory care
       print('%3s is arriving for ambultory care at %.2f' % (patient_number,env.now)) arrival_time = env.now
       time_in_ambulatory_care.append(arrival_time) ambulatory_care_request = ambulatory_care.request()
       yield ambulatory_care_request
       print('%3s is admitted to ambulatory care at %.2f' % (patient_number,env.now)) if (env.now > arrival_time):
          delay_in_ambulatory_care.append(env.now - arrival_time)
          patient_ambulatory_care.append(patient_number)
          print('%3s has to wait for ambulatory care for %.2f' %
                 (patient_number, env.now - arrival_time)) yield env.timeout(los_ambu)
       print('%3s stays at ambulatory care at %.2f' % (patient_number,los_ambu)) ambulatory_care.release(ambulatory_care_request)elif stream == 3:
       #  no care
       print('%3s is arriving for no care at %.2f' % (patient_number,env.now)) arrival_time = env.now
       time_in_no_care.append(arrival_time) no_care_request = no_care_level.request()
       yield no_care_request
       print('%3s is admitted to no care at %.2f' % (patient_number,env.now)) if (env.now > arrival_time):
          delay_in_no_care.append(env.now - arrival_time)
          patient_no_care.append(patient_number)
          print('%3s has to wait for no care for %.2f' % 
                (patient_number, env.now - arrival_time)) yield env.timeout(los_exit)
       print('%3s stays at no care at %.2f' % (patient_number,los_exit)) no_care_level.release(no_care_request)

我们编写了函数 calc_batches() ,用于计算门诊护理级别的*均排队延迟的置信区间(置信水*为 0.90)。我们在每批中使用了 12 批相同数量的记录。

def calc_batches():
    ## delay in ambulatory care global inf, sup, delay_in_ambulatory_care
    number_batchs = 13        ## selected by the analyst
    number_recs    = len(delay_in_ambulatory_care)
    recs_per_batch = int(number_recs/number_batchs) # to guarantee equal number of records in each batch
    matrix_dim = number_batchs*recs_per_batch
    rows_to_eliminate = number_recs - matrix_dim   
    delay _in_ambulatory_care = 
                    delay_in_ambulatory_care[rows_to_eliminate:] # eliminating transient effects (warm-up period)
    delay_in_ambulatory_care =  
                    delay_in_ambulatory_care[recs_per_batch:] matrix = []
    while delay_in_ambulatory_care != []:
         matrix.append(delay_in_ambulatory_care[:recs_per_batch])
         delay_in_ambulatory_care = 
                   delay_in_ambulatory_care[recs_per_batch:] 

    number_batchs = number_batchs – 1 ## the warm-up batch

    dof  = number_batchs - 1
    confidence = 0.90                   ## selected by the analyst
    t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))
    batch_means = np.mean(matrix, axis = 1)
    batch_std   = np.std(matrix,  axis = 1)
    average_batch_means  = np.mean(batch_means,axis = 0)
    standard_batch_means = np.std(batch_means, axis = 0) inf = average_batch_means 
          -standard_batch_means*t_crit/np.sqrt(number_batchs) sup = average_batch_means 
          +standard_batch_means*t_crit/np.sqrt(number_batchs) inf = round(float(inf),2)
    sup = round(float(sup),2) print('')
    print('Simulation of an Emergency Room')
    print('')
    print('%3s patients arrived at the emergency room' % (len(patient_arrival)))
    print('%3s patients derived to ambulatory care' % (number_recs))
    print('%3s batches of %3s records were used for calculations' %      (number_batchs,  recs_per_batch))
    print ('')
    print('The average delay in ambulatory care belongs to the interval %3s %3s' % (inf, sup))

模拟算法的核心代码如下。它包含了一个 Environment() 的实例,因为所有 SimPy 进程都存在于一个环境中[2]。我们排队系统的服务器(资源)是接诊台的数量,以及医院护理、门诊护理和非护理级别的医生数量。我们使用 1234 作为泊松、均匀和截尾正态分布的随机数序列的种子值。变量stop _ arrives在验证步骤中使用,必须在生产运行时分配给 SIM_TIME

env = simpy.Environment()
admission_desks = simpy.Resource(env,
                                 capacity = NUMBER_ADMISSIONS_DESKS)
hospital_care   = simpy.Resource(env, 
                                 capacity = NUMBER_DOCS_HOSPITAL)
ambulatory_care = simpy.Resource(env, 
                                 capacity = NUMBER_DOCS_AMBULAT)
no_care_level   = simpy.Resource(env, 
                                 capacity = NUMBER_DOCS_NO_CARE)prng = RandomState(1234)
stop_arrivals = 2000env.process(generate_patient(env,PATIENT_ARRIVAL_RATE,0,
            stop_arrivals, prng ))env.run(until = SIM_TIME)
calc_batches()

分析

我们模拟了一个急诊室,包括一个有两个入院台的入院区、一个医院护理级别的医生、五个门诊护理级别的医生和一个非护理级别的医生。根据泊松分布,病人以每小时 1.2 个病人的速度到达急诊室。根据正态分布,患者随机停留在入院区、医院护理级别和门诊护理级别。历史数据表明,30%的到达患者留在医院护理,60%需要流动护理,只有 10%离开房间时仅进行了根据均匀分布的额外筛查。

医疗机构的管理人员希望分析人员分配的可能修改,因为他们收到了来自众多患者的大量投诉,这些投诉与排队时间过长有关,尤其是在门诊护理级别。

他们决定进行模拟研究,以了解最终变化的影响,而不必在实际系统中进行。

最初,我们进行了一次长度为 8760 (24 小时 365 天/小时)的运行。在剔除第一批(预热期)后,我们用 12 个等长批次的批次均值法计算了我们的性能指标(稳态参数)。*

图 1 总结了输出数据:在 10514 个模拟患者后,我们以大约 90%的置信度声称,门诊护理水*的*均延迟包含在置信区间[4.36,6.06]小时内。

图 1 由作者制作

在接下来的文章中,我们将继续分析当前配置的替代方案,以使急诊室的运作更好地响应患者的需求。

参考文献

****【1】https://simpy . readthedocs . io/en/latest/API _ reference/simpy . resources . html # simpy . resources . resource . resource . release

****【2】https://simpy . readthedocs . io/en/latest/API _ reference/simpy . html # environments

Python 中的单一责任原则

原文:https://towardsdatascience.com/single-responsibility-principle-in-python-ac4178b922de

为初学者提供易于理解的解释

Unsplash 上的 Dmitriy 拍摄的照片

让软件工作和让软件干净是两个非常不同的活动——罗伯特·C·马丁(干净的代码)

编码时,你很容易被手头的任务冲昏头脑,只专注于让你的代码工作。结果,你失去了添加到代码中的代码行的踪迹,并以庞大、无组织、但仍能工作的函数结束。

单一责任原则是一个软件设计指南,它规定您代码中的每个模块函数都应该有唯一的责任和唯一的更改理由

这个原则是关于组织你的代码的复杂性,把因为同样原因而改变的东西收集在一起,这样你就知道在哪里寻找来修改东西,而不用考虑所有涉及的复杂性。

继续阅读,了解更多关于单一责任原则的内容,为什么以及如何在 Python 中实现。

适用单一责任原则的好处

如果你遵循单一责任原则,你最终会得到大量的小函数,或者类而不是大函数。

你可能会认为这可能不是一个好主意。也许你更喜欢一些大的函数和类。

显然,如果你盲目地遵循这个原则,将你的代码分解成原子部分,这也会导致一些不希望的副作用。应该有一个*衡考虑,下面的引用定义了它是什么;

因为同样的原因而改变的东西 。把那些 因不同原因而改变的东西分开——罗伯特·C·马丁

为什么遵循单一责任原则是一个好主意;

  • 它有助于将一大块代码转换成定义明确、标记良好、高度内聚、干净和健壮的组件。
  • 它要求你命名更多的代码块,并强迫你明确你的意图。随着时间的推移,这将使您的代码更具可读性。
  • 当你的函数和类尽可能小的时候,很容易发现重复的代码。
  • 定义良好的小代码块可以更好地混合和重用。
  • 小功能容易测试,也容易被别人理解。
  • 当一个函数不属于一个名称空间时,很容易发现。
  • 花时间确定独特的职责有助于我们在代码中识别和创建更好的抽象。

Unsplash 上由 Austin Distel 拍摄的照片

单一责任原则的 Python 实现

单一责任原则— Python 类:

你可以看到下面的模型类有很多职责。预处理数据、训练和评估模型、做出预测是不同的职责,都在模型类中处理。这违反了单一责任原则,由于上述原因,强烈建议不要这样做。

**# Before the single responsibility principle**class **Model**:

  def **pre_process(self)**:
    pass def **train(self)**:
    pass

  def **evaluate(self):**
    pass def **predict(self):**
    pass

如下所示,我们可以创建单独的类来处理每个职责,以使我们的类与单一职责原则兼容。

**# After the single responsibility principle applied**class **PreProcess**:
  passclass **Train**:
  passclass **Evaluate**:
  passclass **Predict**:
  pass

单一责任原则— Python 函数:

说到职能,遵循单一责任原则就更重要了。我总是发现自己在一个功能体中处理许多任务,这使得功能变得庞大而不杂乱。

**# Before the single responsibility principle applied**def **pre_processing_data()**:
  #importing data
  #converting data types
  #handling missing values
  #handling outliers
  #transforming data

现在,一旦我们在一个单独的函数中处理每个任务,我们就可以拥有比第一个更干净、更容易混合和重用的函数。

**# After the single responsibility principle applied**def **import_data()**: 
  passdef **convert_data_type():** passdef **handle_missing_values(): 
 ** passdef **handle_outliers():** passdef **transform_data():** pass 

关键要点和结论

关键要点是:

  • 单一责任原则是一个软件设计指南,它规定您代码中的每个模块函数都应该有唯一的责任和唯一的更改理由
  • 它有助于将一大块代码转换成定义明确、标记良好、高度内聚、干净和健壮的组件
  • 定义良好的小代码块可以更好地混合和重用。

我希望你发现这篇文章很有用,并且将开始在你自己的代码中应用单一责任原则。

基于 SIR 模型的传染病传播概率估计

原文:https://towardsdatascience.com/sir-model-informed-probabilistic-estimation-of-infectious-disease-spread-ec9d4a35453e

用 Pymc3 推断龙格-库塔积分

布莱恩·阿萨尔拍摄的图片

机器学习方法在流行病学研究中发挥了重要作用。对这个领域感兴趣的数据科学家是幸运的,因为他们不必从头开始寻找数据驱动的解决方案。相反,他们会被一些经过充分研究的数学模型所引导。

这篇文章解释了如何使用 Pymc3 (一个用于贝叶斯统计建模的 Python 包)来构建贝叶斯推断,以预测由最基本的流行病学模型(例如 SIR 模型)提供的疾病传播。该实现也可以通过类似实现的张量流概率来完成。请查看笔记本获取完整的 python 代码。

SIR 模型

SIR 模型可能是最简单的房室模型,并衍生出许多变体。它描述了整个种群中三个基本区间的动态:易感个体、传染性个体和被移除的个体,正如该模型的名称所示。该模型值得一章,这篇文章不会深入到各种参考资料中很好解释的细节。取而代之的是,我们在这一节只做一个简单的介绍。

SIR 模型通过一个常微分方程(ODE)给出了不同人群的动态,假设整个人群为常数 N,忽略了流行病期间的出生率和死亡率。

图片作者:SIR 模特

两个参数β和γ代表感染率和清除率。它们的比率是基本再生数,其值决定了流行病的渐*行为。一旦给出这两个参数,SIR 模型所描述的流行病的动力学将被确定。

资料组

以下模型化中使用的数据集是通过 SIR 模型的简单模拟生成的合成数据集。它包含在流行病的前 30 天期间易感个体、感染个体和被移除个体的人口。总人口为 10000 人,最初有 10 名感染者,9990 名易感者,0 名被清除者。我们想训练一个模型,预测未来 30 天的疫情。

为了显示以下模型化的能力,只考虑了 100 个数据点。

作者图片:SIR 人群数据集

SIR 模型通知模型化

模型化的目标

分室模型应该指导机器学习方法来模拟流行病的发展。此外,对于给定的初始状态,如果我们知道β和γ的值,就可以得到 SIR 群的未来粒子数。因此,模型化的主要目标变成学习 SIR 模型的两个参数。

同时,当我们记录不同群体的人口时,不确定性是不可避免的。因此,我们将建立一个 SIR 动态和贝叶斯推理的网络来学习两个相关参数中的后一个。

龙格-库塔法

由 ODE 给定的 SIR 模型在被投入网络之前应该被离散化。在这里,我们将使用经典的四阶龙格-库塔积分,这是一种涉及逐次逼*求解微分方程的数值方法,其形式为 dy/dx=f(x,y)。

图片作者:四阶龙格-库塔

推理

  1. 将β和γ的先验设置为[0,1]中的均匀分布;
  2. 给定 SIR 的初始状态(用 y0 表示),使用 Runge-Kutta 方法获得未来时刻的状态序列。
  3. 给每个实例添加一个项式泊松噪声。
  4. 做贝叶斯推断得到β和γ的后验概率。

我们不会在这篇文章中提供详细的代码。请读者阅读笔记本,欢迎提问。

结果

在 100 个数据点上训练,参数收敛到以 0.6 和 0.3 为中心的正态分布。

作者图片:参数跟踪图

因此,我们可以预测接下来 30 天的 SIR 数量。我们可以在图中看到,流行病趋于渐**衡。幸运的是,我们最终几乎不再看到新的病例。

作者图片:30 天后疫情动态

结论

在这篇文章中,我们将一个数学模型插入到贝叶斯推理中,并将其应用于传染病传播用例。当模型被一些背景知识告知时,小数据集似乎足以用于参数的收敛。

最后,我们要指出,合成数据集只是一个理想的情况,有些不真实的简化。一个有趣的下一步将是在真实数据集上测试该模型,例如大城市的 covid 案例。

你应该自动化的六个数据函数

原文:https://towardsdatascience.com/six-data-functions-you-ought-to-automate-cad4b11e404b

数据管道中应该被视为可重复模式的方面

莱尼·屈尼在 Unsplash 上的照片

任何好的解决方案或数据架构师在任何给定的时间点都应该有各种各样的问题。这很正常。什么是 ab 正常的是,同样的问题会回到那个桌子上。这是您想要摆脱的一个可重复的模式。

在概念上正确的地方解决方案应该永远是架构师的现状。虽然这是我们一直想要的,但是时间压力会让我们做傻事。然而,当您的解决方案建立在可重复的模式之上时,它多少会迫使您这样做。

如果单点解决方案是由自动化过程生成的,那么修复它们是非常不舒服的。通过创建这些孤立的解决方案,你会树敌很多。

通过使用可重复的模式,你将被迫缩小视野,从整体上看问题。例如,您将被迫查看逻辑数据模型并识别潜在的缺点,而不是简单地调整一些将数据加载到目标对象中的 SQL 代码。它会凸显出你的模式并不完美,从而迫使你让它变得更加健壮。

你总是可以通过编码让自己摆脱困境,但是在你的生态系统中拥有定制的编码模式是不可持续的。

我不太支持无代码*台。我对高代码*台非常满意,只要我不需要自己写代码,或者如果我要写,只写一次。但是如果不允许我查看代码并在需要时调整模式,我会受到严重的限制。

关于自动化的话题,如果我每次谈到它时都不分享这个形象,那就太虚伪了。这仍然是事实,人们应该记住这一点。

图片来源:xkcd.com

仅仅因为一些事情是痛苦的,并不意味着它不是正确的事情。设置这些初始模板和模式是困难的,但在某些时候它将胜过手工。

下面是一些我认为应该考虑自动化的明显的可重复模式。

装货

无论您的加载模式如何,无论是批量加载、CDC 方法还是消息驱动流。您将数据接收到所需*台或存储的方式应该是轻量级和可重复的。它应该是一对夫妇点击设置加载过程。

我是 ELT 的支持者——让您的数据按原样加载并在目标中转换。这使得加载的模块化变得更加容易,然后目标*台可以为 on target 转换做优化查询计划的艰苦工作。

数据转换

ETL/ELT 中测试不一定要完全定制。

模块化不同的功能在许多职业中是一个很好的原则,尤其是软件开发。将经常重复的功能模块化。这方面的一些例子是数据清理模式和数据类型转换等硬业务规则。

从概念上讲(这个词又出现了),在同一个地方做同样的事情。不要一步到位地净化和改造。通过这种方式,你可以将重复性的任务模块化,并将它们从非重复性的任务中剥离出来。

测试也依赖于风格。例如,Data Vault 建模风格是高度面向自动化的,因此不需要花费太多精力在可重复的模式上,但是即使是 3NF 和 Kimball 建模风格本质上也是高度重复的。

选择一种造型风格并坚持下去,这也会迫使你在正确的地方做正确的事情。不选择特定的建模技术,你也在做选择——把决定权留给每个工程师。这将导致不可重复的代码。

数据质量

在数据采集过程中模块化和重复质量检查,但要确保它落到正确的人手里。

失败的质量检查需要反馈给那些不愿意接受坏数据的人。无论是您的产品所有者还是指定的数据管理员,他们都需要积极主动地解决数据质量问题。

一个简单的流程可能看起来像这样,其中隐藏了“坏数据”。如果数据集没有通过质量检查,可以通过标记数据集来隐藏数据。然后可以在加载过程中排除它们,或者通过视图或行/列级安全性隐藏它们。

作者图片

无论您决定标记、隐藏还是排除失败的数据,都可以作为内置于自动化流程中的 DDL 语句发送。

分销策略

MPP *台上的数据分布是数据*台生命周期和可持续性的关键部分。

根据您的建模模式,这可以是高度自动化的,也可以是稍微自动化的,但是不应该没有自动化的机会。

不管您使用什么*台,您应该如何存储数据取决于几个因素,如查询模式、数据量以及对象是否应该针对读取或写入进行优化。查询模式高度依赖于您正在使用的数据建模风格。许多这些因素在设计时就知道了,因此可以在最初创建 DDL 时分配。随着时间的推移,随着数据大小和查询模式的变化,有些需要改进。这仍然可以自动化,因为它会持续对您的数据进行健康检查。

由于 Data Vault 作为一种建模风格的重复性和标准化性质,因此有很好的机会在没有干预的情况下应用适当的分布标准。在设计时,如果需要更改,可以覆盖它。

标准化的 DV 查询模式可轻松实现自动化——按作者分类的图片

通常,不同的*台(如 Synapse、Redshift、Snowflake 和 Delta Lake 等)都有关于分区、分布和集群策略的良好文档。他们通常也有文档告诉你如何检查你的数据是否有偏差。然后,您可以从这个更大的社区中学习,并将其烘焙成一个可重复的模式来定期运行,从而允许您的数据“自我修复”。

数据安全

每当我与客户交谈时,数据安全占据了大量的通话时间。它一直很重要,但现在变得更加微妙了。大多数公司正在转向云计算和“即服务”解决方案。这一点,再加上个人信息的复杂性,让你难以理解。不仅每个国家都有自己的法律,每个公司都有自己对法律的解释。

免责声明——自动化不会修复这些细微差别。

自动化确实允许您更快地迭代和测试您的安全策略。给合规团队一些工作。只有律师能在纸上谈兵,我们其他人需要看到它在行动中。

您不需要手动对用户或角色应用安全策略。预先定义策略,理想情况下,您希望这些策略在设计时是可部署的(而不是可悲的)。

根据您的数据建模风格,您可以轻松地将敏感数据部署到安全模式中的不同规范化表中(如 Data Vault),或者您可以将它们保存在一起,但使用 DDL 生成对表应用列级或行级策略。无论您是屏蔽数据、加密数据还是简单地保护数据不被用户发现,大多数目标*台都满足这些特性中的一部分或全部。设计保持同质,部署基于您要部署到的目标*台而定制。

测试

关于这一点已经写了很多,我建议你阅读一下数据操作方面的测试。

这是一个广泛的话题,我将简单地强调一个我认为被忽视的方面:策划输入,策划输出。创建可测试的精选数据集是痛苦和荣耀之所在。

会议邀请谢绝

好吧,这是一个额外的,但人们都缩小了。良好架构的解决方案与良好执行的计划密切相关。两者都需要时不时不间断的专注努力。

总之,可能有更多的领域可以自动化,但是我发现这六个领域在你的架构的可持续性中扮演着关键的角色。持续改进这些模式将迫使您构建架构良好的解决方案,并停止不得不修复相同问题的循环。

简单地问自己哪里存在可重复的模式,或者哪里可以改进——改进模式,清洗并重复。

[1] - DataKitchen. 2022\. *Blog - Add DataOps Tests to Deploy with Confidence | DataKitchen*. [online] Available at: <https://datakitchen.io/add-dataops-tests-to-deploy-with-confidence/>

理解 Python 装饰者:从初学者到专家的六个层次的装饰者

原文:https://towardsdatascience.com/six-levels-of-python-decorators-1f12c9067b23

装饰者如何工作,何时使用它们,以及 6 个越来越复杂的例子

这段代码已经装饰得很漂亮了(图片由复杂的探险家Unsplash 上提供)

装饰器是一个非常方便的工具,可以用来改变函数的行为,而不用修改函数本身。它们允许您在不更改现有代码的情况下轻松添加功能。有许多用例(其中一些我们将在今天讨论),如日志记录、性能检查、验证权限等。

读完这篇文章后,你会对装饰者如何工作、如何应用以及何时应用有一个清晰的了解。我们将从最简单、最基本的例子开始,然后慢慢地向更复杂的例子发展。最后我们会有一个函数,用不同类型的多个装饰器来装饰。我们来编码吧!

装修工如何工作

首先,我们将指定一个我们想要装饰的函数:

正如你所看到的,这个函数叫做‘say hello ’,我们可以通过运行sayhello()来执行它(注意函数名后面的()。这和print(sayhello)不一样;这将返回函数名和存储函数的内存地址:
<function sayhello at 0x000001F64CA25A60>

下一步是创建一个装饰器:

正如你所看到的,我们创建了一个期望另一个函数的函数。然后,我们定义一个名为 wrapper 的内部函数,在其中执行接收到的函数。然后我们返回内部函数。定义内部函数的原因是为了处理参数,我们将在下一章看到这一点。

我们可以像这样使用这个装饰器our_decorator(sayhello())或者添加一点语法糖,然后像这样传递它:

这两种方法是等价的:我们将sayhello包装在our_decorator中。

复杂 6 步中的装饰者

现在我们已经了解了 decorator 及其语法的工作方式,我们将通过 decorator 的 6 个步骤向您展示它们提供的所有可能性和灵活性。

让我们开始装饰吧(图片由张家瑜Unsplash 拍摄)

1.最简单的装饰

首先,我们创建一个装饰器的最基本的例子。我们创建了一个名为logging_decorator的函数,它将用于修饰其他函数。首先,我们检查代码,然后浏览:

如你所见,我们正在重新使用旧的sayhello功能。我们用一个叫做logging_decorator的函数来修饰它。正如您在第 3–5 行中看到的,我们可以在实际执行sayhello之前和之后做一些事情,所以让我们打印出我们调用这个函数用于调试的事实。

还要注意,我们可以将函数作为对象来访问。这样我们可以在第 3 行和第 5 行调用函数的名字。执行下面的代码会打印出以下内容:

[LOG]   calling sayhello
 === Hello from the function function
[LOG] called sayhello

注意,我们的函数非常简单;它甚至不返回任何东西。在下一部分,我们将增加一点复杂性。

2.传递参数和返回值

在前一部分中,我们的装饰器只是执行包装的函数。我们将升级代码,以便函数接收参数并实际返回一些内容。这就是名为wrapper的内部函数的用武之地。

在上面的代码中,我们修饰了multiply函数。这个函数需要两个参数。我们在第 2 行用*args 和**kwargs 修改了 decorator 函数内部的包装器。然后我们将它们传递给第 4 行的修饰函数。在同一行中,我们还接收修饰函数的结果,然后在第 6 行返回。这些微小的变化使得我们可以从我们的目标函数中得到一个结果:

[LOG]   calling multiply
  === Inside the multiply function
 [LOG] called multiply
[result]   20

3.多个装饰者

也可以用多个装饰器来装饰一个函数。

上面的代码首先将multiply包装在logging_decorator_2中,然后将整个串包装在logging_decorator_1中。它从目标函数开始,通过所有装饰器向上移动。您可以在下面的输出中看到装饰器被调用的顺序:

[LOG1]   calling decorator 1
[LOG2]   calling decorator 2
 multiply function
[LOG2] called decorator 2
[LOG1] called decorator 1
[result]   42

用我们的两个装饰器调用目标函数非常类似于下面的代码行。两者是等价的,但是在下面一行中,类似包装器的属性非常清楚:
logging_decorator_1(logging_decorator_2(multiply(14,3)))

4.将参数传递给装饰者

在这一部分中,我们将稍微修改一下日志装饰器,以便它可以接收参数。我们希望收到一个指示调试模式是打开还是关闭的参数。如果它打开,我们就打印出测井曲线,否则就不打印。

变化在第 1 行;这里我们允许装饰者接受一个变量。然后在包装器中,我们可以访问这个变量,并决定是否打印出这一行。我们可以在第 13 行确定调试模式是开还是关。

5 个有状态的装饰类

在这一部分中,我们将创建一个装饰器来计算目标函数被调用的次数。为了做到这一点,装饰器需要保存一些状态:它必须记住函数被调用的次数:

正如你看到的,我们用目标函数初始化了这个类。然后,当目标函数被调用时,我们增加 call_count。这是如何使用这个装饰器:

执行此操作将产生以下结果:

Called multiply for the 1th time
Called multiply for the 2th time
Called multiply for the 3th time
Called multiply for the 4th time
res: (42, 70, 54, 8)

跟踪一个函数被调用的频率对于限制 API 调用非常方便

6.装饰参数和函数参数之间的交互

在最后一步中,我们将创建一个装饰器,它可以用密码保护一个函数。在装饰参数中,我们将定义密码。我们将使用通过目标函数提供的密码对此进行检查。类似的装饰器通常用于为 API 路由提供安全性,例如

请记住,下面的例子只是一个例子;这不是一个安全代码的好例子。查看 这篇文章 了解更多关于如何在代码中安全存储机密信息的信息。

如您所见,我们在第 14 行的装饰器中提供了我们的有效密码。然后,在装饰器中,我们从第 5 行的 args 或 kwargs 中获取通过目标函数提供的密码。然后,在第 6 行,我们检查提供的密码是否正确。执行上面的代码会产生下面的输出:

You can only see this if you provided the right answer
Incorrect password

装饰者允许你创造真正的杰作(图片由伊戈尔·米斯克Unsplash 上提供)

结论

在这篇文章中,我试图解释为什么以及何时需要使用装饰器。然后我们看了 6 个越来越复杂的例子,我希望用它们来展示装饰者可以带来的灵活性。

如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的关于各种编程相关话题的其他文章,比如:

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

https://mikehuls.medium.com/membership

分析时间序列数据的六张图

原文:https://towardsdatascience.com/six-plots-to-analyze-your-time-series-data-d71b555f99cf

在 Python 中实现箱线图、傅立叶变换、熵、自相关和 PCA 来分析时间序列数据

照片由卢卡斯像素上拍摄

数据可视化是任何数据相关项目中最重要的阶段之一。根据数据可视化的对象,有:

  1. 数据可视化以报告结果。
  2. 数据可视化,用于分析数据,或者换句话说,数据科学家内部使用的可视化,用于提取有关数据的信息,然后实现他们的模型。

本文主要关注后者,因为它解释了一些有助于时间序列数据分析的方法。具体来说,这篇文章涵盖了以下方法:(1)均值和标准差,(2)箱线图,(3)傅立叶,(4)熵,(5)降维,以及(6)自相关。

本文使用的所有数据和代码都包含在下面的 GitHub 库中。

0.图书馆

首先,这些是与笔记本一起使用的库。大部分代码绕过了 NumPy 和 P 和 as 库,因为数据主要用作 P 和 as DataframeNumPy 数组

1.导入文件

一旦下载了位于 GitHub 存储库中的数据,运行下面的代码来导入它。

如所观察到的,数据具有六个传感器的传感器数据、每个数据点的日期时间以及机器状态。这一个是“已中断”、“正常”或“正在恢复”,但是为了简化可视化,它被分组如下:

{'BROKEN', 'NORMAL', 'RECOVERING'}0: {'NORMAL', 'RECOVERING'}
1: {'BROKEN'}

在任何编程语言中处理 datetimes 总是具有挑战性,Python 也不例外。尽管有几种处理日期时间的方法,但是列 datetime(作为字符串读取)被函数 pandas.to_datetime 转换为时间戳。

<class 'str'>
<class 'pandas._libs.tslibs.timestamps.Timestamp'>

2.数据预处理

在进行可视化之前,对重复和缺失的值进行了分析。

以下是在我们的数据框中删除重复项的函数:

这是填充缺失值的函数:

这是预处理阶段的整个管道。此外,数据分为输入和输出数据。

Input shape: (10081, 7)
Output shape: (10081, 2)

3.数据可视化

现在,我们准备开始数据可视化。这是传感器数据和异常的曲线图。

图一。时间序列。参考:图片由作者提供。

3.1.*均值和标准值

更好地总结数据随时间变化的行为的最基本的图之一是均值-标准差图,其中我们显示了按时间范围分组的均值和标准差。这主要有助于分析特定时间段的基线和噪声。

图二。均值和标准差。参考号:图片由作者提供。

3.2.箱线图

另一个有趣的图是通过 boxplots 显示的图。箱线图是一种通过四分位数以图形方式展示数字数据的局部性、分布和偏斜度组的方法。有两个主框表示从第 25 个百分位数到第 75 个百分位数的数据,由分布的中值分隔。除了方框之外,还有从方框延伸出来的胡须,表示上下四分位数之外的可变性。与数据集的其余部分显著不同的异常值也绘制为箱线图上触须之外的单个点。

这个类似于均值和标准差图,因为它表明了数据的*稳性。但是,它也显示异常值,这有助于直观地检测异常和数据之间的任何关系。

图三。箱线图。 Ref :图片作者。

3.3.傅里叶

快速傅立叶变换(FFT)是一种计算序列的离散傅立叶变换的算法。这种类型的绘图非常有趣,因为它是处理时间序列时提取特征的主要方法之一。通常的做法是应用傅立叶变换提取频率,然后训练模型,而不是训练时间序列的模型。

为此,您必须选择一个滑动窗口来计算 FFT。滑动窗口越宽,频率数越高。缺点是您将获得更少的时间戳,从而失去数据的时间分辨率。当减小窗口大小时,我们得到相反的结果:更少的频率但更高的时间分辨率。然后,窗口的大小应该取决于任务。

图 4 。FFT 的滑动窗口。参考:图片由作者提供。

对于下图,我选择了 64 个数据的时间窗口。因此,频率从 1-32 赫兹。

图 5 。FFT。参考:图片由作者提供。

3.4.熵

可视化信息和熵是机器学习中的有用工具,因为它们是许多特征选择、构建决策树和拟合分类模型的基础。

熵的计算如下:

  • 归一化频率分布

  • 计算熵

最低熵是为一个随机变量计算的,这个随机变量有一个概率为 1.0 的确定性的单一事件。如果所有事件的可能性相等,那么随机变量的熵最大。

图 6 。熵。参考号:图片由作者提供。

3.5.降维

当有多个传感器时,实施降维方法以获得包含大部分信息的 1、2 或 3 个主要成分总是令人感兴趣的。

对于这个例子,我实现了主成分分析(PCA)。这是计算主成分并使用它们对数据进行基变换的过程。

解释的差异比率是由每个所选组件引起的差异的百分比。

图七。PCA 解释的方差比。 Ref :图片作者。

对于第一个主成分分析,您可以绘制数据并直观地检查异常和时间序列之间是否存在关系。

图 8 。PCA。参考:图片由作者提供。

3.6.自相关

最后,特别是对于预测任务,绘制数据的自相关图是很有趣的。这一个代表一个给定的时间序列和它本身在连续时间间隔内的滞后版本之间的相似程度。

Autocorrelation is:  0.024363541344977133

图 9 。自相关。参考:图片由作者提供。

与自相关相关的是扩展的 Dickey-Fuller 统计检验,用于检验给定的时间序列是否*稳。

sensor_00:  (-4.288982026391745, 0.00046413778677505943, 13, 10067, {'1%': -3.4309997435394877, '5%': -2.861827148204747, '10%': -2.5669228438492597}, -31804.0714734676)
pca1:  (-3.7298433193954073, 0.003712434586758542, 5, 10075, {'1%': -3.430999227482557, '5%': -2.8618269201630375, '10%': -2.566922722462425}, 44790.129278286884)

如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!

参考

[1] GitHub,异常检测

[2]开放风险,可视化时间序列的 21 种方法

从业务人员转变为数据科学家需要知道的六件事

原文:https://towardsdatascience.com/six-things-you-need-to-know-about-changing-from-business-person-to-data-scientist-975003bbcf78

宏向量创建的图像—www.freepik.com

一位成为数据科学家的董事总经理的见解

首先,从商业管理(或咨询)转向数据科学不是职业“支点”。物理学家或工程师成为数据科学家将是职业生涯的转折点。从商业人士到数据科学家是一个飞跃,进入一个非常不同的职业生涯!

但是如果你热爱编程、科学和绞尽脑汁解决问题,这绝对是一个伟大的举动。

以下是我作为一名数据科学家第一年的一些观察——这是我在销售和商业战略领域工作了 9 年之后做的。

1.你会以完全不同的方式使用你的大脑

你处理问题的方式,实际上是你思考的方式,将会改变。

在数据科学中,系统而有条理地思考至关重要。例如,如果你试图在你的代码中找到一个 bug,你需要组织你的想法,并一个接一个地找出潜在的原因。你需要系统地理解系统,它们如何相互交流,A 如何影响 b。

如果你还没有强大的 STEM 背景,尤其是数学科目,这可能是你大脑如何运作的一个换挡。可能需要一些时间和努力来把你的大脑塑造成这种思维方式。

虽然系统思维在商业中非常有用,但面对客户的商业角色往往更依赖于个性、建立关系、倾听客户并创造性地提供满足客户需求的方法。

准备好以不同的方式思考。

2.你将有更多的时间独自工作(和少得多的会议!)

作为一名数据科学家,你仍将定期与你的团队互动。例如,你可能每天都有站立会议(当你向团队汇报你的进展时,每天都有简短的会议)。你还会定期与同事召开决策和解决问题的会议。然而,你会花更多的时间和你的代码独处。****

你将有更多的机会进入状态,更加专注于你的工作。

3.你可能会离客户和市场需求越来越远

作为一名数据科学家,你可能会工作数年而不与使用你所构建的东西的人交谈。因此,您可能在没有真正理解用户需求的情况下进行构建。

你可能有内部“客户”(如果你正在为你组织中的其他部门创建算法),一旦你变得更高级,你可能会与外部客户互动。但是一开始,你可能只和你自己的团队一起工作。

这与商业角色相反,在商业角色中,你定期与客户交谈,一切都基于他们的需求和偏好。

4.你将成为巨大的全球知识交流的一部分

数据科学社区是真实的-它与你在商业世界中看到的任何东西都不一样。人们纯粹基于他们是数据科学家而联系。你会在 meetup 小组、在线论坛、出版物(如《走向数据科学》)等形式中看到这一点。数据科学家喜欢聚在一起谈论数据科学——这提供了一个无价的知识交流和归属感。

5.学习更被鼓励和接受

数据科学中的技术和方法发展速度非常快,因此,有一种理解是,无论你学了多少,总会有很多你不知道的。这意味着人们通常更愿意相互解释和教授数据科学中的新事物。

例如,许多数据科学团队会有每周一次的“代码审查”,其中一名成员向团队的其他成员展示他们的一些代码,并解释他们是如何做到的。

做好不断学习新技术技能的准备。

6.你没有财务目标(也可能没有机会获得奖金)

如果你从事过任何商业工作,你会对财务目标很熟悉。无论是月销售额、在线收入还是利润目标,钱永远是当今的话题。

作为一名数据科学家,你的表现不能直接用任何财务指标来衡量。金钱这个话题很少被提及,这让商务人士耳目一新。然而,这也意味着获得奖金的机会减少了。

最后的想法

这篇文章的作者直到 30 岁才开始接触编程和严肃的数学——这只是一个角度。能听到别人的经验和见解就太好了:)

希望那有用!如果您有任何问题,请随时在 LinkedIn 上给我发消息。也可以邮箱 订阅我的帖子 这里

最后,如果你想进一步支持 Medium 上的作者,你可以在这里注册成为会员。

关于 Python 字典你应该知道的六个窍门

原文:https://towardsdatascience.com/six-tricks-you-should-know-about-python-dictionary-41c86570d282

图片来自 PixabayFelix Merler

通过了解常用字典操作的快捷方式来节省您的时间

Python 的一个优点是字典可以与 JSON 对象无缝集成。这使得字典数据结构的使用比其他编程语言要频繁得多。

然而,我确实意识到一些学习者仍然在写一些代码,这是在重新发明轮子。换句话说,有许多“捷径”,或者我们应该说是“正确的方法”,来对 Python 字典进行大量频繁的操作。

在这篇文章中,我将介绍关于 Python 字典的 6 个技巧,我相信如果你以前不知道这些技巧,你会从中受益。

1.按值对字典数组进行排序

图片由 cocoparisienne 来自 Pixabay

我们经常从一些 Web API 收到一组 JSON 对象。假设我们已经在 Python 列表中读取了它们。现在我们想按照里面的某个值对这些字典进行排序。

看到有人用选择性或冒泡排序编写 for 循环并不少见。那太累了。我们可以使用 Python 内置函数sorted()

通常,当我们想要对列表进行排序时,sorted()函数会非常容易使用。但是,这一次我们希望根据它包含的字典对象的值对列表进行排序。事实上,sorted()函数有一个名为key的参数。

在 Python 文档中,指定如下:

key 指定一个参数的函数,用于从 iterable 中的每个元素提取一个比较键。默认值为 *None* (直接比较元素)。

听起来我们可以创建一个函数来告诉sorted()函数我们想要用来排序的标准是什么。

让我们首先创建一个字典的样本列表。假设我们想按人们的年龄对字典进行分类。

students = [
    {'name': 'Alice', 'age': 40},
    {'name': 'Bob', 'age': 37},
    {'name': 'Chris', 'age': 33}
]

然后,按照文档中所说的,我们可以创建这样一个函数:

def get_age(dict_item):
    return dict_item['age']

现在,我们可以在sorted()函数中使用get_age函数。

sorted(students, key=get_age)

那完美地完成了工作。然而,我们意识到get_age()函数非常简单,所以我们可以将其简写为 lambda 函数。

sorted(students, key=lambda item: item['age'], reverse=True)

在上面的例子中,我们不需要额外的函数。相反,我们使用 lambda 编写了一个内嵌函数。此外,我还添加了reverse标志,这样列表就不会按降序排序。

展开按关键字排序的用法

虽然这不是为了字典,我只是想用key展示一个sorted()函数的扩展用法。

假设我们有一个如下的字符串列表

strings = [
    'aaa3',
    'bbb2',
    'ccc1',
    'ddd5'
]

如果我们用sorted()函数来排序,那一定是基于每个字符串的首字母。所以,顺序不应该改变。

sorted(strings)

但是,如果我们希望根据最后一位数字对列表进行排序,该怎么办呢?我们可以编写一个 lambda 函数来获取字符串的最后一个字符。所以,sorted()函数将使用它作为标准。

sorted(strings, key=lambda s: s[-1])

2.按关键字或值对字典排序

图片来自 PixabayReimund Bertrams

我们已经提到了按值对字典列表进行排序。如果我们想按键或值对字典进行如下排序呢?

my_dict = {'c': 1, 'b': 2, 'a': 3}

在 Python 3.6 之前,不要为此烦恼,因为字典是无序实现的。也就是说,我们只能对字典进行排序,按顺序打印。这在 for 循环中很容易做到。如果条目的顺序很重要,我们需要使用有序字典。

from collections import OrderedDictmy_ordered_dict = OrderedDict(sorted(my_dict.items()))

按字典关键字排序

然而,从 Python 3.6 开始,字典将有顺序,这意味着对字典进行排序开始有意义。如果我们想按关键字对字典进行排序,我们可以简单地使用sorted()函数对条目进行排序,然后将它们转换回字典。

my_dict = dict(sorted(my_dict.items()))

按字典值排序

如果我们想按值对字典进行排序,我们将需要再次使用我们从前面的问题中得到的技巧。即使用sorted()功能的key参数。

dict(sorted(my_dict.items(), key=lambda item: item[1]))

由于字典的items()将返回元组,我们可以使用订阅[1]来访问它的第二个元素,这将是值。

3.使用理解创建词典

图片来自 Pixabay贝卡·克拉克

我们可能经常使用列表理解,但不要忘记理解语法也适用于词典。

当我们想用 for-loop 创建一个逻辑简单的字典时,使用字典理解可以非常简洁高效。

例如,我们想使用sqaure_x作为键,其中x是一个数字。那么这个值应该是相应值的*方。我们可以使用一个带有range()函数的 for 循环来获得一个数字序列,然后字典创建如下。

my_dict = {
    'square' + '_' + str(i): i**2 
    for i in range(5)
}

4.将两个列表编入词典

图片由 Foundry CoPixabay 获得

有时,我们可能会分别得到键和值。有些 API 被设计成分别返回这两个东西的数组,这种情况并不少见。

为了以后的使用,我们必须把它们缝合在一起。我们可以用索引开始一个 for 循环,这样我们就可以分别从列表中获得键和值。但是,有更好的办法,而且很 Pythonic。

keys = ['name', 'age', 'skill']
values = ['Chris', 33, 'Python']my_dict = dict(zip(keys, values))

所以,我们不必重新发明轮子。如果您不太熟悉 Python 中的zip()函数,请查看下面的文章。我相信你会理解的。

使用zip()功能的好处更多。例如,如果我们有两个不同长度的列表,多余的条目将被忽略。

keys = ['name', 'age', 'skill']
values = ['Chris', 33, 'Python', 'English']my_dict = dict(zip(keys, values))

在上面的例子中,第二个列表中的字符串“English”被忽略,因为没有与之对应的键。

如果我们知道列表长度应该严格相同,我们也可以强制执行它。因此,如果两个列表的长度不同,将会抛出一个错误。请注意,这个strict标志是在 Python 3.10 中添加的。

my_dict = dict(zip(keys, values, strict=True)

5.合并两本词典

图片来自 PixabayDaniel Kirsch

合并两个或更多的字典可能是非常频繁的操作。然而,这是最棘手的问题之一。在不同的 Python 版本中,我们有不同的“最佳方式”来做到这一点。

在 Python 3.4 或更低版本中

我们可以使用字典的update()功能。它会将另一个字典追加到当前字典中。

my_dict1 = {'age': 33, 'name': 'Chris'}
my_dict2 = {'skill': 'Python'}my_dict1.update(my_dict2)

然而,缺点是第一本字典my_dict1被修改了。如果我们想保留两个原始字典,我们必须创建第三个。

my_dict = {}
my_dict.update(my_dict1)
my_dict.update(my_dict2)

来自 Python 3.5 — Python 3.8

从 Python 3.5 开始,它将用于解包列表的星号*list语法扩展到使用双星号**dictioanry解包字典。

因此,我们可以创建一个新的字典,并将其中的两个原始字典解包。

my_dict = {**my_dict1, **my_dict2}

这就变得容易多了。

从 Python 3.9 开始

在 Python 3.9 中,涉及到了一种新的语法,即使用管道|来合并两个字典。

my_dict = my_dict1 | my_dict2

现在这个更加简洁,可读性更强,也更直观。此外,已经证明它在上述所有方法中具有最高的性能。

6.更改密钥的名称

图片来自s . Hermann&f . Richter来自 Pixabay

如果我们想改变一个键的名字呢?假设我们从一个 API 收到了 JSON 对象,但是键名与我们在数据库中的键名不一致。因此,在将对象保存到数据库之前,我们需要更改键的名称。

请不要写 for-loop 来查找带键的项,除非基于某个规则有很多键要改。相反,一个简单的方法是使用简单的传递值到一个新的项目,然后删除旧的。

my_dict['programming_language'] = my_dict['skill']
del my_dict['skill']

在执行了上面的示例之后,关键字“skill”已经被更改为“programming_language”。这是最直观的实现方式。然而,我们甚至可以通过使用字典的pop()功能来简化它。

想法是在字典中创建一个新的键,并从弹出的旧键-值对中获取值。之后,用旧值创建新的键,旧的键-值对已经不存在了。

my_dict['skill'] = my_dict.pop('programming_language')

如图所示,使用最简单的方法将密钥改回来。

摘要

图片由拉蒙·佩鲁丘皮克斯拜拍摄

字典是 Python 中最重要的数据结构之一。所以,知道一些做一些经常性操作的捷径是很重要的。这些技巧不仅可以节省我们重新发明轮子的时间,还可以提供更高的性能。

https://medium.com/@qiuyujx/membership

如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和其他成千上万的作者!(点击上面的链接)

Word2Vec 神经网络从无到有

原文:https://towardsdatascience.com/skip-gram-neural-network-from-scratch-485f2e688238

纳迪·博罗迪纳Unsplash 上拍摄

使用 Skip-Gram 和 CBOW 深入研究非上下文 NLP

自然语言处理领域具有挑战性有几个原因,但最主要也是最琐碎的原因是处理数据,因为不是数字,而是文本。

因此,需要一种从文本到数字值的转换,这种转换可以尽可能地保留单词的含义,而不会丢失单词在人的头脑中唤起的任何东西。

这种转换被称为单词嵌入。我们可以进行更低层次的分析,创建一个字符嵌入,或者更高层次的分析,创建一个句子嵌入。这取决于我们选择了哪些记号(我们的原子元素)。

迄今为止,在众多尝试中,最好的尝试是将单词映射成数字向量。目标是让相似的单词在向量空间中紧密地放置在一起。我们可以用余弦相似度来度量向量的相似度。

作者图片

让我们看看上面图片的代码示例。

你可以注意到啤酒葡萄酒之间的相似度很高,这有道理吧?

在本文中,我们将处理非上下文嵌入方法,其中单词的表示,因此不依赖于上下文

例如,即使以下句子中的单词 second 具有不同的含义,使用这些嵌入方法,其矢量表示将总是相同的。

This is the second time that I try it

He just arrived one second later

如果你对非上下文自然语言处理的有影响力的论文感兴趣,这里有一个简短的列表:

Word2Vec

2013 年,随着 Word2Vec ,谷歌的 Mikolov 等人彻底改变了嵌入范式:从那时起,嵌入将是一个神经网络的权重,根据任务进行调整,以最小化一些损失。嵌入已经成为一种神经网络算法。

我们接下来要做的是训练一个简单的神经网络,在某个任务上只有一个隐藏层。但是请注意,我们不会使用网络来解决手头的任务,我们感兴趣的只是代表单词 embedding 的网络的权重。

我们将训练神经网络做以下事情。给定一个句子中间的特定单词(输入单词),查看附*的单词(窗口大小)并随机选择一个。一旦经过训练,神经网络将告诉我们,我们词汇表中的每个单词成为我们选择的邻*单词的概率。

我们将训练一个具有单个隐藏层的简单神经网络来执行某项任务,但之后我们实际上不会将该神经网络用于我们训练它的任务!相反,目标只是学习隐藏层的权重(不知何故,这类似于自动编码器的目标)。最后,我们会看到这些权重就是我们试图学习的单词向量

例如,如果我们给训练好的网络输入单词 New ,那么像 YorkCity 这样的单词的输出概率会比 booklight sabre更高。

可以使用两种不同的方法来构造 Word2Vec:

  • CBOW :神经网络查看周围的单词,并预测中间出现的单词。
  • SkipGram :神经网络接受一个单词,然后尝试预测周围的单词。

作者图片

从头开始编写程序

首先,为了简单起见,让我们从三个单词句子的语料库中创建一个词汇表。

从自定义语料库创建词汇库

例如,如果我们输入单词,上下文单词将是 Idog。

打印输入和目标单词

现在我们准备准备我们的数据。我们将使用一个 window_size = 1,因此给定一个字 j 目标将仅仅是 j-1j+1

为 skip program 准备数据

在构建我们的模型之前,让我们分批构建我们的数据,并将单词转换成 单键向量

生成批次

建立模型

这是我们模型的样本。隐层神经元上没有激活函数,但输出神经元使用 softmax。

和 PyTorch 一样,让我们定义一个类来构建我们的神经网络。为了简单起见,我们的嵌入大小是 2,这样我们可以在二维向量空间上绘制嵌入。我们的神经网络的输入的维数等于 vocab_size (因为我们使用的是独热向量)。此外,输出具有 dimension = vocab_size,每个输出神经元的权重告诉我们代表该特定神经元的单词在输入中给定的单词附*的概率。

作者图片

是时候训练我们的模特了!

一旦训练结束,单词嵌入被存储在模型参数中。

输出应该是这样的。

作者图片

绘制嵌入图

作者图片

好吧,这个情节和嵌入没有意义。这只是一个虚构的例子。我们希望有相*的单词。你能做的就是使用预训练的嵌入,比如 GloVe 并在你的项目中使用它们!

结束了

马赛洛·波利蒂

LinkedinTwitterCV

在运行时将图像分割成重叠的小块

原文:https://towardsdatascience.com/slicing-images-into-overlapping-patches-at-runtime-911fa38618d7

一种用于计算机视觉任务的高分辨率图像切片的有效方法

图片“000000380244.jpg”取自 MS COCO 数据集

处理大的高分辨率图像通常会给计算机视觉任务带来问题,因为在训练或推理期间创建批处理时需要大量的 GPU 内存。

也许克服这种限制的最常见的方法是将图像调整到较小的尺寸。虽然这在某些情况下可能有效,但对于保持高水*的图像细节对良好性能至关重要的情况(如小对象检测),调整大小时发生的下采样可能会导致重要信息的丢失。一种替代的解决方案是将每个图像分成重叠的切片,并在聚集预测之前在训练和推断期间使用这些切片;这样,我们可以减少所需的内存量,但保持原始图像的细节。

尽管这种方法很有用,但在为最*的客户项目做准备时,我发现大多数可用的实现都采用了这样的方法:在开始正常训练之前,提前将每个图像分成切片,并将这些图像存储为单独的数据集。尽管这意味着在训练过程中只需要最少的预处理,但需要额外的磁盘空间来存储这些图像,这可能会导致大型数据集的巨大成本!

在这篇文章中,我将演示一个预先计算图像切片的实现,并在训练和评估期间在运行时应用裁剪之前将它们存储在一个单独的文件中。我们这样做的原因如下:

  • 我们避免以任何方式修改原始数据,所有与切片图像相关的信息都将存储在一个额外的文件中,以便在必要时使用。
  • 附加文件将包含使用了哪些切片的记录,这可能有助于分析和调试。
  • 通过在运行时裁剪图像,我们避免了将图像的裁剪作为单独的文件存储;存储图像切片文件所需的存储量可能比将图像切片存储为图像少得多。
  • 通过创建新文件并在运行时选择它,我们可以很容易地试验不同的裁剪设置。

Tl;博士: 如果你只是想看到一些可以直接使用的工作代码,复制这篇文章所需的所有代码都可以在这里GitHub gist获得。

选择数据集

这里,我们将使用的图像和注释来自流行的 MS COCO(微软上下文中的公共对象)数据集。然而,由于该数据集非常大,包含大约 33 万张图像,我们将使用 Fastai 准备的该数据集的样本,其中包含属于以下类别子集的所有图像:

  • 椅子
  • 表达
  • 遥远的
  • 花瓶

下载数据

我们可以使用以下命令下载数据:

将注释转换为数据帧

像最初的 COCO 数据集一样,这个示例使用一个 Json 文件来存储注释。虽然像 PyCocoTools 这样的库可以帮助我们与这种格式的文件进行交互,但我个人认为这种格式比以表格格式存储的数据更难浏览和查询。

为了简单起见,我们可以定义一个函数来加载作为 Pandas 数据帧的注释;这里,我也将边界框转换为 xyxy 格式,因为我个人认为这是最直观的工作方式。根据我的经验,我发现在项目开始时花一点时间将数据转换成一种易于交互的格式,可以节省以后大量的时间,而这些时间将用于处理一种困难格式的数据!

我们现在可以用它来加载数据集注释。

限制数据样本

通常,当我们试图检测大图像中的小对象时,我们会将图像分割成更小的块。为了表示这个场景,让我们把注意力限制在“远程”类上,它通常是小对象。

我们可以通过使用 Pandas 的查询方法轻松做到这一点,如下所示。

在这里,我们可以看到,我们只剩下与图像中遥控器位置相对应的注释。

创建数据集适配器

为了帮助我们探索我们的数据,让我们创建一个 adaptor 类来包装我们的数据帧,并可用于加载图像。当使用 PyTorch 时,我经常发现将图像和注释的加载抽象到这样一个类是有益的,然后可以将它传递给特定于任务的 dataset 类;这使得更改底层数据集变得容易,同时只需进行最少的代码更改。

我们可以定义适配器类,如下所示:

在这里,我们可以看到,虽然这个类看起来像一个 PyTorch 地图样式的数据集,但是我们没有显式地子类化 PyTorch 的Dataset类;这将使我们能够在不安装 PyTorch 的情况下使用它来探索环境中的数据。由于 Python 使用了 duck typing ,由于这个类提供了正确的接口,我们也可以毫无问题地将它用作 PyTorch 数据集!

现在,我们可以使用它轻松地检查我们的一些数据!

图片“000000371250.jpg”取自 MS COCO 数据集

图片“000000199449.jpg”取自 MS COCO 数据集

图片“000000380244.jpg”取自 MS COCO 数据集

绘制我们的一些图像和标签,我们可以看到它似乎工作正常!

切片图像

现在我们已经加载了数据,让我们探索如何将图像分割成更小的块。在推理过程中,我们可能希望确保所有的图像都被表示出来,所以我们可以采用滑动窗口的方法来分割图像。

如前所述,我们希望提前预计算这些切片,以便在运行时应用它们;而不存储额外的图像文件。实现这一点的一种方法是将每个切片存储为一个边界框,然后可以用它来裁剪图像。

计算切片

我们可以定义一个函数,根据给定图像的尺寸来计算我们需要多少个切片,并将每个切片的坐标作为 xyxy 格式的边界框返回,如下所示:

在这个函数中,我们还包括了指定每个切片应该有多少重叠的选项;这有利于避免小对象被分割成多个切片的情况。此外,根据为每个切片选择的高度和宽度以及图像的大小,我们可能会发现一些重叠是必要的,以确保所有切片的大小相同。

现在,让我们为切片设置一些参数。这里,我们任意选择 250 的切片大小,除非必要,否则没有重叠。

在这里,我们可以看到,使用这些设置,我们有六个不同的图像切片。

分割图像

现在我们已经有了表示切片的边界框,我们可以如下所示来可视化这些边界框:

此外,很容易使用这些边界框来裁剪我们的图像,如下所示:

缩放注释

当我们将图像分割成更小的块时,我们还必须以两种方式修改我们的边界框注释:

  • 对于每个切片,我们需要确定哪些边界框包含在这个切片中
  • 对于现有的边界框,我们需要缩放这些框,以便它们相对于包含的切片。

幸运的是,优秀的相册库包含了这个功能作为其Crop转换的一部分,所以让我们在这里利用它。

在这里,我们可以看到,即使图像被裁剪,这些框也显示正确。

创建切片数据帧

既然我们已经探索了如何分割我们的图像和修改我们的标签,下一步是将它存储在数据帧中。

查找图像尺寸

在我们使用的数据集中,图像具有不同的大小。这可能会给我们带来一个问题,因为为了计算我们的切片,我们需要提前知道每个图像的大小。为了有效地做到这一点,我们可以使用多重处理来打开每个图像并存储高度和宽度。

如果数据集中的所有图像大小相同,我们可以跳过这一步,将高度和宽度值指定为参数。

我们可以定义一个函数来完成这项工作,如下所示:

我们可以用它来获得 DataFrame 中每个图像的高度和宽度,如下所示。

计算和存储切片

现在我们知道了每个图像的大小,我们可以使用前面定义的函数来计算我们的图像切片,并将其作为附加列添加到我们的数据帧中。

正如我们所看到的,我们现在有了每张图像的切片边界框列表。但是,由于每个切片代表一幅图像,所以让我们修改数据帧,使数据帧的每一行都有一个切片。此外,将值存储为列表会在保存和加载数据帧时导致问题,所以让我们也将每个切片坐标分离到它自己的列中。

我们可以这样做,如下所示:

现在,让我们将这两个数据帧合并在一起。

为每个切片创建一个标签

现在我们已经计算了我们的切片,我们拥有了我们需要的所有信息!然而,能够查询我们的图像切片中有多少包含我们试图检测的对象通常是有帮助的。

实现这一点的一种方法是计算每个框与图像切片的并集(IoU) 的交集。或者,我们可以使用白蛋白转换来计算转换后的盒子,然后检查给定图像切片中是否存在任何盒子。

为了与我们将在训练中使用的方法保持一致,让我们使用白蛋白。这种方法还提供了一个额外的优势,那就是我们将能够利用albuminations 的 [min_bbox_area](https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/#min_area-and-min_visibility) [min_bbox_visibility](https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/#min_area-and-min_visibility) 逻辑。这些参数使我们能够控制边界框的哪一点被丢弃,当框的一部分由于变换而不再可见时。

然而,为了节省时间,我们可以避免加载每个图像,并使用具有正确形状的虚拟 numpy 数组来替换它。让我们定义一个函数来做这件事:

我们现在可以将该函数应用于我们的数据帧,如下所示:

把所有的放在一起

既然我们已经了解了所涉及的步骤,我们可以将这些步骤合并到一个函数中,我们可以使用该函数在一个步骤中计算我们的图像切片数据帧:

这里,我还添加了一个额外的步骤,即添加一个 id 列来帮助我们唯一地标识每个切片,这在以后可能会很有用。

现在,我们可以用它来找出有多少图片至少包含一个遥控器。

在这种情况下,我们可以看到这只是超过 40%的图像!

为切片创建数据集

现在我们已经计算了切片,让我们定义一个数据集,它可以使用这个来加载我们的切片图像和标签:

我们可以通过禁用as_slice标志来验证这是否正确

使用随机裁剪的方法

虽然推理需要确定性切片,但这可能不是训练的最佳方法。这样做的原因是,如果数据集中的图像相当均匀,则模型将只看到同一组图像裁剪;这可能影响其普遍性。另一种方法是在训练期间的运行时随机裁剪图像,在模型看到的图像中添加一些变化——希望使模型更加健壮!

让我们探索一下如何修改我们的ImageSliceDataset来包含这一点。

这里,我们添加了一个调整大小的随机裁剪变换,如果deterministic_crop标志未启用,将应用该变换。这样,我们可以很容易地在两种行为之间切换。

多次加载同一个图像,可以看到拍摄的是不同的作物。

加入附加变换

对于更多的变化,我们可以使用额外的白蛋白转换,如下所示:

逆向转换我们的标签

在使用图像的切片进行推断后,我们可能希望转换我们预测的边界框,使它们与整个图像相关,让我们来探索如何做到这一点。

首先,让我们检索图像切片和相对边界框:

为了转换我们的盒子,我们还需要图像切片的坐标。这些没有从我们的数据集中返回,但是我们可以使用返回的图像切片的切片 id 从我们的切片数据帧中轻松地检索到这些。

因为我们的切片和边界框都是以 xyxy 格式表示的,所以我们可以简单地通过将起始 x 和 y 坐标值添加到边界框来转换我们的框,使它们相对于整个图像,如下所示:

为了验证这已经正确工作,让我们加载整个图像和我们的地面真相框。

绘制我们重新缩放的方框,我们可以验证输出是相同的。

这里唯一需要注意的是边界框被分割成多个切片的情况;由于超出切片尺寸的盒子的任何部分都将在初始缩放期间被丢弃,因此不可能完全重建它。在推理过程中,这些情况将导致仅在所使用的切片中存在的对象部分周围绘制一个框,当框在整个图像上可视化时,这是我们所期望的。

一般来说,我们可以避免这种情况发生的唯一方法是尝试并确保我们感兴趣检测的整个对象包含在图像切片中;我们可以通过增加切片之间的重叠量来做到这一点。

结论

虽然可能需要根据您的特定用例来调整具体的实现,但希望这提供了一个清晰的起点,并展示了一种对我来说成功的方法背后的思想!

复制这篇文章所需的所有代码都可以在 GitHub gist 这里 找到。

克里斯·休斯上了 领英

参考

数据集许可证

这篇博文中的所有图片都来自于 MS COCO 数据集

MS COCO 图像数据集是在知识共享署名 4.0 许可证下许可的。因此,本许可证允许您分发、重新混合、调整和构建您的作品,即使是商业性的,只要您认可原始创作者。

使用 Google BigQuery 缓慢改变维度类型 2

原文:https://towardsdatascience.com/slowly-changing-dimension-type-2-with-google-bigquery-749e0f9107fb

使用 SQL MERGE 逐步实现 SCD 类型 2

图片来源于unsplash.com

摘要

重要的是以一种允许管理变更的方式对数据建模,以便对诸如以下问题有一个快速的答案:

  • 变化是什么时候发生的?
  • 现在还有效吗?
  • 以前怎么样?
  • 最终状态是什么?

例如,如果您想跟踪过去几年的支出,然后您开始注意到,如果您按月做报告,则每年都会更改分类,这不会与按年的实际支出相匹配,但如果您有适当的分类维度,则可以拆分更改。

在这篇文章中,我们将学习如何在 MERGE BigQuery 实现中破解缺失的输出,以创建渐变维度 Type 2 ,我将更详细地解释以下内容:

  1. 该设置
  2. SCD 型简介
  3. 逐步执行
  4. 了解解决方案

最后,我将添加结论、感谢、故障排除、有用的资源。

如果您正在使用 Google Query,我建议您查看我以前的帖子,关于使用 Python API 客户端运行 big Query SQL将 DBeaver 连接到 Google BigQuery ,因为它们一步一步地详细介绍了如何创建和配置服务帐户以及如何避免 BigQuery Web 界面。

设置

我们将使用两个表, HR_INPUT,作为随时间变化的输入,而 EmployeeDim 用于维度类型 2:

CREATE OR REPLACE TABLE SCD.HR_INPUT (
   ID         STRING NOT NULL
  ,EMPLOYEE   STRING NOT NULL
  ,JOB_TITLE  STRING NOT NULL
  ,COMPANY    STRING NOT NULL
  ,START_YEAR INTEGER NOT NULL
);CREATE OR REPLACE TABLE SCD.EmployeeDim (
   SKEY       STRING NOT NULL
  ,ID         STRING NOT NULL
  ,EMPLOYEE   STRING NOT NULL
  ,JOB_TITLE  STRING NOT NULL
  ,COMPANY    STRING NOT NULL
  ,START_YEAR INTEGER NOT NULL
  ,END_YEAR   INTEGER NOT NULL
  ,ACTIVE     STRING NOT NULL
  ,CREATED    TIMESTAMP NOT NULL
  ,UPDATED    TIMESTAMP NOT NULL
);

在 HR 输入表中,您将获得当前雇主,我们将模拟我们在不同的年份运行三次:

作者的例子

员工 Dim 的最终表格如下所示:

最终 SCD。作者使用 Google BigQuery Web UI 查询 EmployeeDim 结果

此外,我们将使用存储过程 SP_EmployeeDim_SCD2 来使用 MERGE 处理数据:

CREATE OR REPLACE PROCEDURE SCD.SP_EmployeeDim_SCD2()
BEGIN
  MERGE SCD.EmployeeDim AS output
  USING (
    SELECT src.ID as PSEUDO_ID, src.*
      FROM SCD.HR_INPUT AS src
    UNION ALL
    SELECT NULL as PSEUDO_ID, dup.*
      FROM SCD.HR_INPUT AS dup
     INNER JOIN SCD.EmployeeDim AS trget ON dup.ID = trget.ID
     WHERE trget.END_YEAR = 9999 
       AND trget.START_YEAR <> dup.START_YEAR
  ) AS input
  ON input.PSEUDO_ID = output.ID WHEN NOT MATCHED THEN INSERT (SKEY,ID,EMPLOYEE,JOB_TITLE,COMPANY,START_YEAR
          ,END_YEAR,ACTIVE,CREATED,UPDATED)
  VALUES ( GENERATE_UUID()
          ,input.ID
          ,input.EMPLOYEE
          ,input.JOB_TITLE
          ,input.COMPANY
          ,input.START_YEAR
          ,9999
          ,'Y'
          ,CURRENT_TIMESTAMP()
          ,CURRENT_TIMESTAMP()
  ) WHEN MATCHED 
   AND output.END_YEAR = 9999
   AND output.START_YEAR <> input.START_YEAR THEN UPDATE  SET ACTIVE = 'N'
         ,END_YEAR = input.START_YEAR
         ,UPDATED = CURRENT_TIMESTAMP()
  ;END;

SCD 型简介

在数据建模中,渐变维度是实现维度表中历史变化跟踪的重要部分。

SCD Type 2 的美妙之处在于,它允许我们看到数据发生时的状态,并将其视为当前活动状态。

例如,假设您有财务分类和与每笔交易相关的支出。在这种情况下,如果我们没有正确地跟踪变化,就很容易陷入部门之间的战争。

使用 SCD Type 2,我们可以看到上一版本的分类所花费的全年时间,而且我们可以逐月检查它是如何定义的,这有助于我们解释为什么一个区域花费了更多的钱。

逐步执行

首先是 ,让我们删除两个表中的所有数据,重新开始:

TRUNCATE TABLE SCD.EmployeeDim;
TRUNCATE TABLE SCD.HR_INPUT;

第二个 ,我们将假设我们在 2008 年,数据源使用下面的 INSERT 语句反映了我作为数据仓库架构师的当前工作:

INSERT INTO SCD.HR_INPUT VALUES ('123456','Cristian Saavedra Desmoineaux','Data Warehouse Architect','MILLICOM/TIGO',2008);SELECT * FROM SCD.HR_INPUT;

SCD。HR_INPUT 作者使用 Google BigQuery Web UI 查询的结果

然后,我们将调用存储过程 SP_EmployeeDim_SCD2 在 EmployeeDim 表中插入第一行,从 2008 年开始,到 9999 年结束:

CALL SCD.SP_EmployeeDim_SCD2();SELECT * FROM SCD.EmployeeDim;

SCD。作者使用 Google BigQuery Web UI 进行的 EmployeeDim 结果查询

第三个 ,我们将假设我们是在 2009 年,而输入的数据没有改变它:

CALL SCD.SP_EmployeeDim_SCD2();

如您所见, EmployeeDim 表也没有改变:

SCD。作者使用 Google BigQuery Web UI 第二次运行该过程后的 EmployeeDim 结果查询

第四个 ,我们假设我们在 2021 年,数据源显示我在 REEF Technologies 工作时的当前工作,我们运行存储过程 SP_EmployeeDim_SCD2 :

TRUNCATE TABLE SCD.HR_INPUT;INSERT INTO SCD.HR_INPUT VALUES ('123456','Cristian Saavedra Desmoineaux','Senior Business Intelligence Engineer - Manager II','REEF Technology',2021);SELECT * FROM SCD.HR_INPUT;CALL SCD.SP_EmployeeDim_SCD2();SELECT * FROM SCD.EmployeeDim;

SCD。HR_INPUT 作者使用 Google BigQuery Web UI 第二次修改后的结果查询

现在我们有两张唱片了。之前的记录从 2008 年到 2021 年有效,并且无效:

SCD。EmployeeDim 作者使用 Google BigQuery Web UI 第三次运行该过程后的结果查询

第五个 ,现在我们是 2022 年,HR 输入表反映了我现在的角色:

TRUNCATE TABLE SCD.HR_INPUT;INSERT INTO SCD.HR_INPUT VALUES ('123456','Cristian Saavedra Desmoineaux','Manager, Data & Analytics','Cardinal Health',2022);SELECT * FROM SCD.HR_INPUT;CALL SCD.SP_EmployeeDim_SCD2();SELECT * FROM SCD.EmployeeDim;

我们在 HR 输入表中只看到一条记录:

SCD。HR_INPUT 作者使用 Google BigQuery Web UI 第三次修改后的结果查询

和 Employee 维度中的三个记录,显示我的当前记录为活动记录,其余记录为非活动记录:

SCD。EmployeeDim 由作者使用 Google BigQuery Web UI 运行福特时间的过程后的结果查询

了解解决方案

首先,让我们看一下 SP_EmployeeDim_SCD2 存储过程:

作者使用 Google BigQuery Web UI 创建的存储过程

如您所见,有一个 MERGE 语句说:

尝试将维度与数据源合并,如果伪 ID 不匹配,则插入一个新行,否则更新为无效,并添加年末

MERGE SCD.EmployeeDim AS output 
USING (<... subquery ...>) AS input 
ON input.PSEUDO_ID = output.IDWHEN NOT MATCHED THEN
INSERT (<... columns ...>)
VALUES (GENERATE_UUID(),<... all data ...>,9999,'Y'
,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP())WHEN MATCHED AND output.END_YEAR = 9999
     AND output.START_YEAR <> input.START_YEAR THENUPDATE SET ACTIVE = 'N', END_YEAR = input.START_YEAR, UPDATED = CURRENT_TIMESTAMP()

当不匹配时,我们从 HR 输入表中取出的所有数据,使用 GENERATE_UUID()函数添加一个 代理键 ,添加一个无穷大的结束年份,将其标记为活动,并设置时间戳

WHEN NOT MATCHED THEN
INSERT(SKEY,ID,EMPLOYEE,JOB_TITLE,COMPANY,START_YEAR,END_YEAR,ACTIVE
,CREATED,UPDATED) VALUES (GENERATE_UUID(),input.ID,input.EMPLOYEE
,input.JOB_TITLE,input.COMPANY,input.START_YEAR,9999,'Y'
,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP())

当我们找到它时,我们更新了那些仍然对未来开放并具有不同起始年份的,我们将它们设置为不活动,添加一个结束年份并更新时间戳。

WHEN MATCHED AND output.END_YEAR = 9999 
             AND output.START_YEAR <> input.START_YEAR THEN
UPDATE SET ACTIVE = 'N', END_YEAR = input.START_YEAR
, UPDATED = CURRENT_TIMESTAMP()

最初,我试图实现 Kimball 设计技巧#107 时,我发现 Google BigQuery 没有实现我 14 年前看到的输出。然后我发现有可能改变来源:

USING (
SELECT src.ID as PSEUDO_ID, src.*
FROM SCD.HR_INPUT AS src
UNION ALL
SELECT NULL as PSEUDO_ID, dup.*
FROM SCD.HR_INPUT AS dup
INNER JOIN SCD.EmployeeDim AS trget ON dup.ID = trget.ID
WHERE trget.END_YEAR = 9999 AND trget.START_YEAR <> dup.START_YEAR
) AS input

我带来了所有的数据,复制了 ID 列,并将其命名为 Pseudo ID,新的记录和按年份拆分现有 ID 的值将在这里实现。

SELECT src.ID as PSEUDO_ID, src.*
FROM SCD.HR_INPUT AS src

我添加输入表中的所有记录,这些记录将把现有的 ID 添加为活动的,在分类为要插入的合并语句的伪 ID 中添加一个空值。

SELECT **NULL** as PSEUDO_ID, dup.*
FROM SCD.HR_INPUT AS dup
INNER JOIN SCD.EmployeeDim AS trget ON dup.ID = trget.ID
WHERE trget.END_YEAR = 9999 AND trget.START_YEAR <> dup.START_YEAR

结论

缓变维度类型 2 允许我们看到数据发生时的样子,并将其视为当前活动的,MERGE 语句可以简化 SCD 类型 2 的创建但在 Google BigQuery 中实现;我们需要对源查询进行更改,以替换当前不可用的输出。

如果您正在使用 Google Query,我建议您查看我以前的帖子,关于使用 Python API 客户端运行 big Query SQL将 DBeaver 连接到 Google BigQuery ,因为它们一步一步地详细介绍了如何创建和配置服务帐户以及如何避免 BigQuery Web 界面。

谢谢

我把这篇文章献给沃伦·桑斯韦特(金宝集团),一个了不起的人,一个知识渊博的专业人士,直到他生命的最后几天,他都面带微笑。

当我们一起工作时,他对数据建模的热情向我展示了我职业生涯的成长之路。

对我的朋友来说,我仍然是你所说的数据魔术师。

解决纷争

验证过程主体时出错(添加选项(strict_mode=false)以隐藏):查询错误:没有为此项目启用开单。在 https://console.cloud.google.com/billing.启用计费在自由层不允许 DML 查询。请设置一个帐单帐户来取消此限制。在[3:5]

使用 Google BigQuery Web UI 合并错误消息 Google BigQuery 自由层

如果您正在使用免费层,您需要转到收费项目,方法是转到 https://console.cloud.google.com/billing,创建一个帐户,设置一种支付方式,然后转到我的项目,选择您的项目,并更改计费。

请记住在测试后返回,并再次锁定或禁用计费。

控制台谷歌云计费创建一个帐户。作者使用 Google BigQuery Web UI 截图

有用的资源

https://en.wikipedia.org/wiki/Slowly_changing_dimension https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement https://www.kimballgroup.com/2008/11/design-tip-107-using-the-sql-merge-statement-for-slowly-changing-dimension-processing/ https://www.kimballgroup.com/2008/08/slowly-changing-dimensions/ https://www.kimballgroup.com/2008/09/slowly-changing-dimensions-part-2/ https://www.kimballgroup.com/2013/02/design-tip-152-slowly-changing-dimension-types-0-4-5-6-7/ https://www.kimballgroup.com/1998/05/surrogate-keys/

结果有意义的小样本

原文:https://towardsdatascience.com/small-samples-with-meaningful-results-1a1b15052ac8

使用非参数符号测试扩展您的数据科学工具包

克里斯多夫·伯恩斯在 Unsplash 上拍摄的照片

数据以各种形状和大小出现。大多数统计方法依赖于对丰富和良好数据的假设。非参数方法,如符号检验,有很少的假设,因此是分析具有不寻常形状的小数据集的一个很好的选择。从有限的数据中提取有意义的信息,是生物学(基因分析)、管理学(德尔菲访谈)、地质学(水流)、商业(生产和消费者情绪)中的常见问题。

本文回顾了符号测试,并将其应用于两个实际的业务场景。理想情况下,这些例子将促使您重新考虑处理小数据集的方法,也许是一般的统计方法。通常的假设和规则并不适用。具有高 P 值的可操作结果?绝对的。依赖数据?完全可控。不是来自样本期望值的检验统计量?绝对的。都有可能。

关于标志测试

符号检验是一种基于二项式分布的方法,用于调查小样本。经典的二项式例子是在实验中通过投掷一枚硬币来计算正面和反面的数量。不太可能出现大量正面或反面,如果出现了,那将是意想不到和不寻常的。同样的原理适用于各种各样的现象,这些现象可以通过使用被分类为成功或失败的实验来分成这样或那样的东西。符号测试将识别匹配对或单个样本与参考点之间的一致差异。

符号检验是非参数的,这意味着数据可以是无分布的。也有可能我们可能知道分布,但参数(如均值或标准差)未指定。因此,非参数检验可以应用于数据,而无需可视化或确定其分布特征。这可以节省时间和精力,并为严格的统计分析打开异常分布的数据。

符号测试只有三个假设。第一个直观上是显而易见的:每个试验测量必须来自同一个连续的总体。这种假设通常在研究的设计阶段提出。例如,关于可乐偏好的实验将只有关于可乐的观察,而没有关于番茄酱的观察。第二个假设是,试验测量始终是有序的,以区分成功和失败。[1]成功标记为“+”;失败了,“-”。这就是符号测试得名的原因。测量可以采取二分是/否答案或 A/B 偏好、李克特式量表或正面/负面文本情感结果的形式。第三个也是最后一个假设是,试验是独立的,因此一个观察不会影响任何其他观察。这种约束在一定程度上让一枚公*的硬币变得“公*”,因为之前的投掷不会影响未来的结果。稍后我们将看到如何修改相关数据以生成有用的结果。

二项分布和基数 R

进行符号检验的统计方法是二项式检验。三个变量定义了二项分布:试验次数( n )、试验成功次数( x )以及单次试验成功的概率( p )。二项式概率质量函数的公式为:

美国商务部,公共领域图像 [2]

等式的右边由两部分组成:试验次数乘以成功和失败概率的组合,每一项都是指数。概率就像组合中的一个系数。要查看 p 的效果,可以使用 0.35、0.5、0.6 和 0.9 的值绘制 100 次试验的密度函数。

作者图片

这些密度函数向我们展示了 p 对检测差异的影响有多大,因为试验结果围绕选定的概率聚集。对于大量试验,二项式开始接*正态分布,更有效的 z 检验是更好的选择。

要在 R 中运行精确的二项式测试,插入三个变量的值,然后选择一个单边或双边测试,最后设置置信水*。例如,R 中公*二项式检验的一般语法是[3]:

binom.test(*x*, *n*, *p*,
alternative = c("two.sided", "less", "greater"),
conf.level = 0.95)

假设的选择取决于研究的目标。“双侧”选择检测成功或失败次数的一致差异。“少”用于检测故障;“更大”,成功。P 值的报告方式可能因统计软件包的不同而有所不同,因此请务必验证您的结果。

二项式检验有三个主要的局限性。首先,置信区间的计算对少量试验没有太大帮助。即使有非常显著的 P 值,少于 50 次试验的间隔也会相当宽。第二,如果你知道分布,其他测试会更有效。一般规则是,超过 25 次观察表明其他替代方法可能比符号测试更好。最后,作为一个非参数检验,符号检验是低功率的,这意味着它更有可能拒绝无效假设,如果它是假的。[4]这是一个第二类错误,对数据科学家如何应用非参数检验的结果有影响。

在两个例子中,我们将利用这些统计概念来解决商业中的实际数据科学问题。当你通读这些例子时,考虑一下商业环境的限制如何影响研究的设计。更重要的是,想想这些例子与经典教科书上的问题有什么不同,它们会如何挑战你对应用统计学的信念。

场景一,对速度的需求:快速和*似的结果

场景:一家全球电话销售公司实施了一个新的销售报告系统,但推广情况不佳。在输入销售时出现的任何错误都会导致报告被自动系统拒绝,以便经理可以对其进行审查。因此,报告要么被接受,要么被拒绝,在有 5,000 名代表参与的大型电话销售业务中,不清楚哪些员工会从减少错误率的额外培训中受益最多。高级管理层将这种情况称为危机,因为有太多的报告被拒绝,而经理们手动审阅报告已经不堪重负。销售和营销团队要求您确定 1,000 名员工接受额外培训。他们给你一个数据文件,每个工人占一行,每个报告占 1 到 25 列,或者被接受,或者被拒绝。一些工人提交了少量报告,而其他人则提交了 25 份。

解决方案:有限的信息和时间结合在一起,产生很少的选项来发现模式并得出统计相关的结论。数据来源于对所有提交报告的普查, N 。然后,我们可以通过将成功提交的报告数量除以 N 来计算 p 。期望值和*均值为 N * p 。我们可以简单地计算每个员工成功提交的*均值,并将其与 p 进行比较,但这不会区分只有几份报告的员工和有很多份报告的员工。然而,根据每个员工的结果,有足够的信息来运行 5000 个“更少”的单尾符号测试,以确定哪 1000 名员工可能表现不佳,需要更多培训。

该研究的结果将是 5000 个 P 值,每个 P 值与一个工人相关联。请记住,P 值告诉我们,相对于测试假设的预测,我们的数据有多大差异。举例来说,“P = 0.01 表示数据与统计模型(包括测试假设)预测的数据不太接*,而 P = 0.40 表示数据与模型预测的数据非常接*,考虑到了机会变化。”[5]在典型的统计研究中,拒绝零假设所需的显著性是在设计阶段根据结果将如何被使用而建立的。低 P 值对于医学研究非常重要,以确保患者获得适当的护理。例如,在疾病关联研究中,临界值非常严格,为 0.01,而基因表达研究设定为 0.05。[6]发表的学术文章报道了低 P 值的研究,因为有趣的结果被定义为与预期不同的东西。

在我们的场景中,销售和营销团队已经隐式地为您设置了 P 值,因为他们想要 1,000 名绩效最低的员工的列表,也就是说,相对于我们的模型的成功概率预测,他们具有最大的差异。您将选择第 1000 名表现最差的员工的 P 值作为临界值。完全有可能重要性很低,但这没什么,因为犯错的风险也很低。一些人可能只是错过了培训,而另一些人可能接受了不必要的培训。

为了进一步了解具有大量测试的研究中的 P 值,可以在直方图或密度函数上显示这些值,以确定它们的*似形状。这是一个具有 50 个 P 值的非常具有统计学意义的研究的密度函数:

作者图片

30 个 P 值聚集在 0.01 和 0.05 附*,这意味着许多假设已经被拒绝。剩余的 20 个 P 值是均匀分布的,代表没有被拒绝的零假设。像这样表现良好的数据很受研究人员的赞赏,但很少。将此图与我们场景中前 50 个 P 值的假设密度函数进行比较:

作者图片

这个情节更难理解。最大的密度在右边,为 1.0,代表完美填写所有报告的员工个人。最左边的工人都拒绝接受报告。但是中间还有另外三个凸起。对于其他驼峰可能有许多可能的解释,但是我们需要更多的数据来确定它们为什么在那里。话虽如此,现有的 P 值仍可用于决策。对于这个假设的 50 个 P 值的样本,底层 20%的工人(巧合的是)的 P 值< = 0.2,并被送去接受额外的培训。从图形上看,这包括 P 值从红色箭头到 0.0 的所有员工。

完成这项任务后,你可以提供进一步的见解。其他数据,如工人的位置或工作年限,可能解释 P 值的峰值。培训完成后,可以对参与者进行配对符号测试,以寻找提交成功率的提高。可以考虑使用更强大的测试来识别最成功的报告提交者,以获得额外补偿。(请记住,非参数符号检验容易出现第二类错误,也就是说,更有可能的情况是,一名表现出色的员工在获得应得的奖金时遭到拒绝。薪酬的风险高于培训任务。)每一个新的见解都会带来清晰,并有助于理性决策。

场景 2,复杂数据:简化的统计数据

场景:一家在线电子产品零售商在其*台上销售 2000 多种产品。企业买家将参加一个贸易展,需要一份分析报告,根据网站上的客户评级和评论来支持购买哪些产品。为了准备你的分析,你已经为所有的产品搜集了定量的李克特式 5 分评级和定性的书面评论。您将如何确定哪些产品得到了最高的评价和最好的评论?

解决方案:数据集很复杂,可以从不同的角度进行分析。您可以使用混合方法,尽管有些产品可能缺少书面评论。此外,数据可能不是独立的,因为早期的评论可能会影响后来的评论。虽然数据集本身很大,但您需要深入到单个产品,对于某些电子产品,可能没有太多观察结果。产品也不一样——电视的*均评级可能与助听器电池的评级大相径庭。在任何情况下,零售商将不得不订购所有类别的高评级产品,以提供完整的电子产品选择。这意味着由于产品的异质性,没有一个有用的方法来衡量所有售出的商品。

为了制定可行的建议,可以对数据进行简化和转换,以运行标志测试。因为没有有用的总体*均值,李克特式量表可以作为一个先验的代理。这是合理的,因为消费者基于他们的感知而不是统计*均值做出购买决定。如果“3 星”是“*均”,那么测试“星”> 3 的每个评级将识别高于*均评级的产品。一些信息丢失了,在这种情况下,捕捉 4 星和 5 星评级之间的差异,但权衡是一个新的二分法变量。定性评论可以通过情感分析来分类为“正面”或“负面”结果是每个产品的逻辑数据向量。符号测试的前两个假设现在已经满足;每个产品都来自同一个连续的总体,并且数据是有意义地排序的。

转换后的数据几乎可以进行分析了,但是第三个假设有问题,因为数据可能不是独立的。这是很难克服的,但如果我们能确定依赖性的性质,这并非不可能。最直接的方法是检查数据,看看早期和后续的评论和评级之间是否存在任何关系。然而,这也是一个接受外部研究并将其纳入我们自己的模型的机会。大量的学术文献致力于消费者心理和购买选择,尤其是在线零售。最*的研究显示了第一次评论对产品后续星级的影响。通过对电子产品的案例研究,研究人员发现,不管产品质量或价格有何差异,负面的第一次评论*均会少 0.6 颗星。[7]我们可以使用这种信息来调整数据相关性。该 R 代码生成一个具有 0.1 显著性水*的高评级产品列表:[8]

# Count the number of trial success ratings with a positive first review
pos_review <- filter(v, Review == 1) %>%
  apply(1, function (x) sum(x>3))
# Count the number of trial success ratings with a negative first review
neg_review <- filter(v, Review == 0) %>%
  apply(1, function (x) sum(x>=3)) # Note the change in inequality
# Create a vector of all successes
success <- c(pos_review, neg_review)
# Extract just the ratings to find the number of trials
ratings <- select(v,c(3:ncol(v)))
trials <- apply(ratings, 1, function(x) sum(x>0))
# Find the P-values for each row
p_val <- mapply(function(success, trials) binom.test(success, trials, p=0.5, "greater")$p.value, success, trials)
# Create a dataframe of each product's P-value and identify products <=0.1 significance level
v_new <- data.frame(select(v,Product), p_val) %>%
  filter(p_val <= 0.1)
print(v_new)

这个场景的建议模型被大大简化,以说明数据科学家如何选择成功试验的构成要素。有许多途径可以改进分析;对于情感分析,您可以使用强度级别来确定措辞更强烈的评论是否与星级的较大变化相关。你也可以调整假设来扩大或缩小输出的范围,以适应公司购买者。只要在符号测试中对数据进行了有意义的测量,研究人员就可以应用先验信息来代替观察*均数据,从而得出有价值和有意义的结果。

走向

小数据集可能是决策制定中被忽视的来源。任何时候数据是有限的或不寻常的,考虑运行一个符号测试。它没有假设,为调查有序数据提供了极大的灵活性。不要被测试统计、显著性或依赖性的传统规则所约束。精心设计你的实验以获得洞察力,结果将是有意义的和可行的。

参考文献和注释

[1]美国商务部,NIST/SEMATECH 统计方法电子手册(2012 年),https://www . ITL . NIST . gov/div 898/software/dataplot/ref man 1/auxillar/sign test . htm

[2]美国商务部,NIST/SEMATECH 统计方法电子手册(2012 年),https://www . ITL . NIST . gov/div 898/Handbook/EDA/section 3/EDA 366 I . htm

[3]数据营,RDocumentation (2022),https://www . RDocumentation . org/packages/stats/versions/3 . 6 . 2/topics/binom . test

[4] W.W. LaMorte,MPH 在线学习模块(2017),https://SPH web . bumc . bu . edu/otlt/MPH-Modules/bs/bs 704 _ nonparametric/bs 704 _ nonparametric 8 . html #:~:text = A % 20 type % 20I % 20 error % 20 occurs,due % 20 to % 20 small % 20 sample % 20 size

[5] S. Greenland 等人,“统计检验、P 值、置信区间和功效:误解指南”,《欧洲流行病学杂志》(2016 年),【https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4877414/

[6] J. Mohieddin 和 N. Ansari-Pour,“为什么、何时以及如何调整你的 P 值?,《细胞杂志》(2019),【https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6099145/

[7] S. Park,W. Shin,J. Xie,“决定性的第一个顾客评论”,《营销科学》(2021 年),https://pubson line . informs . org/doi/ABS/10.1287/mksc . 2020.1264

[8]该代码用于样本数据集 v,其中列 1 是产品名称,列 2 是评论,列 3 到 8 是“星级”评级。评级 N/A 被替换为零。数据被分类,正面评论排在最上面,负面评论排在后面。例如,数据集可能如下所示:

作者创建的假设数据。

带 SMD 的 Amazon SageMaker 上的智能分布式培训:第 1 部分

原文:https://towardsdatascience.com/smart-distributed-training-on-amazon-sagemaker-with-smd-part-1-cd296f87a0ee

如何选择与您的训练实例的功能相一致的分发算法来增加吞吐量和降低成本

雅尼克·菲舍尔在 Unsplash 上拍摄的照片

优化训练作业运行时性能的一个关键步骤是调整算法,以便最大限度地利用训练环境中的资源。这需要彻底了解你的资源,(计算设备的数量和类型,可用内存,通信带宽等。)以及分析其利用情况的适当工具。调整您的训练算法以充分利用您的资源可以提高您的训练速度并降低您的训练成本。这尤其适用于您的分布式训练算法,它严重依赖于运行在多个处理器(例如,GPU)上的多个进程之间的高速通信。未能考虑培训环境的具体情况会导致沟通瓶颈、高延迟、培训速度降低和成本增加。

这是关于优化分布式培训主题的三篇文章的第一部分。在第一部分中,我们将简要回顾执行分布式培训的不同方法,同时强调它们对底层培训环境的依赖性。在第二和第三部分中,我们将展示两个例子,说明分布式训练方法和算法的选择如何影响训练速度。运行分布式培训有许多不同的流行框架。在本帖中,我们将使用 Amazon SageMaker 的分布式培训库 (SMD)进行演示。虽然我们的重点是 Amazon SageMaker 环境,但是这篇文章的内容同样适用于任何其他培训环境和分发算法。这篇文章中的框架选择不应该被看作是一种认可。对你来说,正确的选择将高度依赖于你的项目的细节,并应考虑到最新的技术和实用工具。

分布式培训概述

有关分布式培训的更详细概述,请参见此处此处

在分布式培训作业中,对多名员工进行培训。在这篇文章中,我们将假设工人是 GPU。分配培训工作有两个主要原因:

  1. 训练加速:通过结合多个 GPU 的力量,我们可以加快训练过程。
  2. 大模型尺寸:当一个模型太大而不适合单个 GPU 的内存时,我们需要多个 GPU 来训练。

分布式训练有两种,数据并行训练(又名数据分发)和模型并行训练(又名模型分发)。

数据并行分布式训练中,每个 GPU 维护其自己的完整模型副本,并对训练数据的不同子集(本地批次)执行每个训练步骤。在每个训练步骤之后,它发布其结果梯度,并考虑所有 GPU 学习的组合知识来更新其自己的模型。用 k 表示 GPU 的数量,用 b 表示局部批量,对kGPU 进行分布式训练的结果是,在每一个训练步骤中,模型都在 kb* 样本的全局批量上进行训练。理想情况下,在 k 个 GPU 上执行数据分布式训练会将训练速度提高数倍 k 。然而,由于梯度共享的额外开销,这种线性比例加速不应被视为理所当然。实际的训练加速将取决于许多因素,包括 GPU 间的通信带宽、用于共享梯度的算法以及模型架构。查看这篇博文了解更多关于数据分布式培训的信息。

模型并行分布式训练中,模型分布在几个 GPU 上。有几种方法可以分布模型,包括:垂直(管道并行)、水*(张量并行)和通过模型分片。

流水线并行解决方案中,模型的单个副本将被分成 N 个部分,每个部分将包含一个或多个层。每个部分都将存储在其中一个 GPU 上。训练数据将被输入到托管输入层的 GPU 中,并将向前流经模型的 N 部分,然后向后用于梯度计算。在向前和向后传递期间,模型部分之间的数据流在宿主 GPU 之间传递。流水线并行化的主要挑战之一是试图减少 GPU 空闲时间。在一个简单的实现中,您会发现在训练步骤中的任何给定点只有一个 GPU 是活动的。在实践中,现代流水线算法将输入样本分成微批,并使用复杂的调度算法来减少空闲时间。

张量并行解决方案中,有时被称为张量切片,模型张量的子集将跨 GPU 划分,并且将添加适当的通信操作以跨 GPU 传输输入和输出数据。请注意,虽然被归类为模型并行技术,但张量并行与数据并行有一些共同的属性,即每个 GPU 都有自己独特的小批量数据,并且没有并行化的张量是重复的。参见此处了解张量并行性如何工作的更多细节。

分片数据并行中,有时也称为零功耗数据并行,模型参数在所有 GPU 之间进行分片。每个参数将驻留在单个 GPU 上,由单个 GPU 拥有和更新。与标准数据并行性一样,完整的训练步骤将在每个独立的小批量 GPU 上执行。当 GPU 达到存储在 gpu_j 上的参数 _i 时,它将需要从 gpu_j 中提取其权重来执行所需的计算,但它随后会立即删除权重,以便它不会占用任何本地内存。这在向前和向后传球中都发生。一旦计算出梯度更新,GPU 需要将它们传达给它们各自参数的所有者。注意,这种方法有时(例如,这里的)被归类为数据并行方法,而不是模型并行方法。

3D 并行是在这篇论文中引入的一个术语,指的是上面讨论的数据并行和模型并行策略的任意组合。3D 并行技术的目标是结合各种技术的优势。

所有方法的共同点是数据在 GPU 之间传递。然而,GPU 之间的通信路径的数量和细节、每个训练步骤的数据跳数以及传递的数据量可能会有很大差异。

现在你可能会对自己说,“哇,这么多进行分布式培训的方法,多么好的概述啊,但是我怎么知道该用哪一种呢?”。我想让你知道我能感受到你,我的朋友。事实是,弄清楚这一点并不简单。(我想说这是 NP 难的,但我手头没有资料支持这一点。)最佳选择将取决于您的模型的细节以及您的基础培训环境的细节。如果你的模型足够小,可以放入单个 GPU 的内存中,那么你可能会选择数据并行分布式算法。如果您的模型太大,不适合单个 GPU,但 GPU 之间的通信带宽特别小,那么您可能会发现管道并行化是最佳选择。

有许多资源(例如这里的和这里的试图根据模型和环境的细节来提供决策指南。一旦选择了高级策略,包括 SMD 在内的一些库将自动执行一些特定的配置。在撰写本文时,还没有 API(据我们所知)能够自动完成寻找和构建最佳分布式训练拓扑的整个过程。我们只能希望在某个地方,有人正在研究这样的解决方案。

在下一节中,我们将深入研究在选择分布式训练方法和算法时应该考虑的基础训练环境的一个特定方面。

一些 GPU 到 GPU 的链接与其他链接不同

如上所述,所有分布式训练算法都依赖于 GPU 之间的数据通信。该数据可以是参数权重、梯度和/或激活。该通信可以是直接的或者经由某种形式的中介(例如,参数服务器)。它可以通过几种不同的介质,如以太网、pci 或 NVLink 。在大多数现代训练系统中,NVLink 支持同一实例中 GPU 之间的最高数据传输速率。因此,许多分布式训练算法将选择直接的 GPU 到 GPU 数据传输。在许多情况下——当然,如果所有的 GPU 都在一个实例上——这确实可能是最佳选择。然而,当在多个实例上训练时,每个实例都有多个 GPU,重要的是要意识到这样一个事实,即两个不同实例上的两个 GPU 之间的数据传输速度和延迟可能会与同一实例上的两个 GPU 有很大不同。这有几个原因,包括:

  1. 计算实例之间的网络带宽往往比单个实例上直接 GPU 到 GPU 链接的带宽低得多。
  2. 网络带宽由与分布式训练算法无关的其他类型的数据通信共享,例如训练样本加载、检查点保存等。
  3. 实例之间的距离会导致一定量的延迟,这会对分布式训练算法的速度产生负面影响。(注意,在撰写本文时,SageMaker APIs 不允许您为所有实例强制使用一个单一的集群放置组。)

在某些情况下,您可能有一个专用的网络接口来促进节点间的通信,比如 Amazon EFA 。然而,它仍然不是单实例直接 GPU 到 GPU 链接的对手。

理想情况下,我们希望我们的分布式训练算法能够考虑到不同类型的 GPU 到 GPU 连接之间的差异。我们希望这种方法优于一种简单的方法,在这种方法中,所有 GPU 到 GPU 的连接都以相同的方式处理。在接下来的部分中,我们将用两种方式对此进行测试,首先使用 SageMaker 分布式数据并行 (SDP),然后使用 SageMaker 分布式模型并行 (SMP)。

后续步骤

请务必查看帖子的第二部分,我们将在其中演示亚马逊 SageMaker 的分布式数据并行库如何以区分节点内和节点间 GPU 对的方式支持数据分发。

带 SMD 的 Amazon SageMaker 上的智能分布式培训:第 2 部分

原文:https://towardsdatascience.com/smart-distributed-training-on-amazon-sagemaker-with-smd-part-2-c833e7139b5f

如何用 SageMaker 分布式数据并行优化数据分布

照片由斯蒂芬Unsplash 上拍摄

这是关于优化分布式培训的三篇文章的第二部分。在第一部分中,我们提供了分布式训练算法的简要概述。我们注意到所有算法的共同点是它们依赖于多个 GPU 之间的高速通信。我们推测,考虑到底层实例拓扑的分布式算法,特别是 GPU 对之间通信链路的差异,会比不考虑的算法性能更好。

在第二部分中,我们将演示如何使用 Amazon SageMaker 的分布式数据并行 (SDP)库来执行数据分发,区分节点内和节点间的 GPU 到 GPU 通信。

使用 SageMaker 分布式数据并行进行数据分发

在数据分布式训练中,每个 GPU 维护其自己的模型副本,副本之间的对齐通过梯度共享来保持。好的数据分布算法将以限制对训练吞吐量的影响的方式实现梯度共享机制。一些梯度共享算法依赖于一个或多个中央参数服务器,其从所有工人收集梯度更新,然后将结果广播回工人。其他则依赖于 GPU 之间的直接点对点通信。梯度共享的一个流行算法是Ring-all reduction,在这个算法中,多个消息在一个单向环中的工人之间传递。见此处查看环环相扣如何工作的精彩视觉回顾。 Ring-AllReduceHorovod 使用,这是一个流行的数据分布式训练框架。

亚马逊 SageMaker 分布式数据并行 (SDP)库旨在简化和加速数据分布式训练。你可以在特色公告亚马逊 SageMaker 开发者指南以及相关的白皮书中看到关于 SDP 的更多细节。(任何想知道我选择的封面图片如何与本文内容相关的人,只需看看白皮书的标题就知道了: Herring :重新思考云的大规模参数服务器。)SDP 的梯度共享算法依赖于许多巧妙的技术。与本文讨论最相关的属性是节点内 GPU 到 GPU 通信和节点间 GPU 到 GPU 通信之间的区别。下图总结了这一区别,显示了一个双层流程,其中节点内 GPU 通过 NVLink 共享梯度,而不同节点的 GPU 之间的通信则通过所有使用中节点的 CPU 上的服务器进行。

SDP 梯度共享流程(图片来自 Herring 白皮书

白皮书展示了与标准 Ring-AllReduce 算法相比,这种方法,即一种针对底层训练环境的拓扑结构而定制的分布式算法,如何能够加速大规模分布式训练作业。

我们应该注意,除了 SDP 使用的分层梯度分布算法之外,还有几个额外的算法和库提供了解决底层实例拓扑的解决方案。例如,除了推广使用 Ring-AllReduce 之外,Horovod 还支持分层梯度共享算法。它还公开了基于项目和环境细节的调节梯度流的控件。此外,Horovod 使用的底层 NCCL 操作也包括高级分层技术

SDP 相对于其他库的一个优势在于实例拓扑发现。网络带宽和延迟等训练环境的细节是选择最佳梯度共享算法的重要输入。由于 SDP 内置于 SageMaker 框架中,因此它对实例拓扑结构有着即时和详细的了解。其他库不了解相同级别的细节,可能被迫猜测最佳的梯度共享策略,或者试图发现丢失的信息并相应地进行调整。

例子

这里我们展示了一个将 SDP 集成到一个简单的 TensorFlow (2.9)脚本中的例子。SageMaker 文档包括许多演示 SMP APIs 调用的 TensorFlow 示例。在我们的示例中,我们将包括 SageMaker 示例中目前没有的两种有用的技术:

  1. 如何编写您的脚本,以便您可以轻松地在 SDP 和流行的 Horovod 库之间切换。
  2. 如何将 SDP 与 TensorFlow 的高层 API 结合起来进行模型训练— tf.keras.Model.fit ()。虽然高级 API 隐藏了对 TensorFlow GradientTape 的访问,但 SDP 要求它用 tensorflow 包装。分布式 GradientTape API。我们通过定制 model.fit()调用的训练步骤来克服这个冲突。

该示例由两个脚本组成。分布式培训作业启动脚本和培训脚本。

第一个脚本是 SageMaker 培训课程启动脚本。在本例中,我们选择将输入模式设置为快速文件模式,这是亚马逊 SageMaker 的一项功能,支持将输入数据直接从亚马逊 S3 传输到训练实例。数据存储在 TFRecord 文件中。虽然典型的分布式训练将在特别大的数据集上运行,但是对于这个例子,我从 CIFAR-10 数据集创建了文件(使用 this 脚本)。会话由四个 p4d.24xlarge 训练实例实例化,并且分布设置被配置为使用 SageMaker 数据分布库。

from sagemaker.tensorflow import TensorFlow
from sagemaker.session import TrainingInput
s3_input = TrainingInput(
          's3://'+S3_BUCKET_DATASET+'/cifar10-tfrecord/',
          input_mode='FastFile')
*# Training using SMDataParallel Distributed Training Framework* distribution = {'smdistributed':
                    {'dataparallel':{'enabled': True}}
               }
tensorflow = TensorFlow(entry_point='train_tf.py',
                        role=<role>,
                        instance_type='ml.p4d.24xlarge',
                        instance_count=4,
                        framework_version='2.9.1',
                        py_version='py39',
                        distribution=distribution)
tensorflow.fit(s3_input, job_name='data-parallel-example')

请注意,只需修改分发设置,就可以修改相同的脚本来启动 Horovod 会话:

distribution = {
  'mpi': {
    'enabled': True,
    'processes_per_host': 8
  }
}

第二个脚本包括训练循环。我们设计这个脚本是为了展示在使用 Horovod 数据分发框架和 Amazon SageMaker 数据并行库之间进行转换是多么容易。该脚本以一个 run_hvd 开关开始,该开关可用于在两个选项之间切换。随后的 if-else 块包含唯一的库特定代码。如上所述,我们已经实现了一个定制的训练步骤,该步骤使用了分布式梯度带 API。

import tensorflow as tf
*# toggle flag to run Horovod* **run_hvd** = False
if **run_hvd**:
  import horovod.tensorflow as dist
  from horovod.tensorflow.keras.callbacks import \
                            BroadcastGlobalVariablesCallback
else:
  import smdistributed.dataparallel.tensorflow as dist
  from tensorflow.keras.callbacks import Callback
  class BroadcastGlobalVariablesCallback(Callback):
    def __init__(self, root_rank, *args):
      super(BroadcastGlobalVariablesCallback, self).
                                            __init__(*args)
      self.root_rank = root_rank
      self.broadcast_done = False
 def on_batch_end(self, batch, logs=None):
      if self.broadcast_done:
        return
      dist.broadcast_variables(self.model.variables, 
                               root_rank=self.root_rank)
      dist.broadcast_variables(self.model.optimizer.variables(),          
                               root_rank=self.root_rank)
      self.broadcast_done = True
*# Create Custom Model that performs the train step using 
# DistributedGradientTape*
from keras.engine import data_adapter
class CustomModel(tf.keras.Model):
  def train_step(self, data):
    x, y, w = data_adapter.unpack_x_y_sample_weight(data)
    with tf.GradientTape() as tape:
      y_pred = self(x, training=True)
      loss = self.compute_loss(x, y, y_pred, w)
    tape = dist.DistributedGradientTape(tape)
    self._validate_target_and_loss(y, loss)
    self.optimizer.minimize(loss, 
                            self.trainable_variables,
                            tape=tape)
    return self.compute_metrics(x, y, y_pred, w)
def get_dataset(batch_size, rank):
  def parse_sample(example_proto):
    image_feature_description = {
        'image': tf.io.FixedLenFeature([], tf.string),
        'label': tf.io.FixedLenFeature([], tf.int64)
    }
    features = tf.io.parse_single_example(example_proto, 
                                          image_feature_description)
    image = tf.io.decode_raw(features['image'], tf.uint8)
    image.set_shape([3 * 32 * 32])
    image = tf.reshape(image, [32, 32, 3])
    image = tf.cast(image, tf.float32)/255.
    label = tf.cast(features['label'], tf.int32)
    return image, label
 aut = tf.data.experimental.AUTOTUNE
  records = tf.data.Dataset.list_files(
                     os.environ.get("SM_CHANNEL_TRAINING")+'/*',
                     shuffle=True)
  ds = tf.data.TFRecordDataset(records, num_parallel_reads=aut)
  ds = ds.repeat()
  ds = ds.map(parse_sample, num_parallel_calls=aut)
  ds = ds.batch(batch_size)
  ds = ds.prefetch(aut)
  return ds
if __name__ == "__main__":
  import argparse, os
  parser = argparse.ArgumentParser(description="Train resnet")
  parser.add_argument("--model_dir", type=str, 
                      default="./model_keras_resnet")
  args = parser.parse_args()

  # init distribution lib
  dist.init()
  gpus = tf.config.experimental.list_physical_devices('GPU')
  for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)
  if gpus:
    tf.config.experimental.set_visible_devices(
                     gpus[dist.local_rank()], 'GPU')
  input_shape = (32, 32, 3)
  classes = 10
  inputs = tf.keras.Input(shape=input_shape)
 outputs = tf.keras.applications.ResNet50(weights=None, 
                                          input_shape=input_shape, 
                                          classes=classes)(inputs)
  model = CustomModel(inputs, outputs)
  model.compile(loss=tf.losses.SparseCategoricalCrossentropy(),
                optimizer= tf.optimizers.Adam())
  dataset = get_dataset(batch_size = 1024, rank=dist.local_rank())
  cbs = [BroadcastGlobalVariablesCallback(0)]
  model.fit(dataset, steps_per_epoch=100, 
            epochs=10, callbacks=cbs, verbose=2)

结果

在本节中,我们比较了使用 Horovod 数据分布框架和 Amazon SageMaker 数据并行库执行分布式训练的运行时结果。运行时性能通过每个训练步骤的*均秒数来衡量。

*均步骤时间(越短越好)——(作者)

我们的实验表明,SDP 库的环境感知分布式训练算法比 Horovod 库使用的算法高出大约 20%。请注意,基于项目的细节和您选择的实例配置,比较性能会有很大的不同。即使对于相同的项目和相同的实例配置,根据实例在培训工作中的精确放置,结果也可能不同。如上所述,实例可以放置在不同的集群放置组中,这可能会增加延迟并减慢训练。

请注意,在撰写本文时,并非所有实例类型都支持 SDP。详见文档

后续步骤

在本帖的第三部分也是最后一部分中,我们将展示 Amazon SageMaker 的分布式数据并行库如何以区分节点内和节点间 GPU 对的方式支持数据分布。

带 SMD 的 Amazon SageMaker 上的智能分布式培训:第 3 部分

原文:https://towardsdatascience.com/smart-distributed-training-on-amazon-sagemaker-with-smd-part-3-db707db8a202

如何用 SageMaker 分布式模型并行优化模型分布

马丁·杰恩伯格在 Unsplash 上的照片

这是关于优化分布式培训的三篇文章的最后一篇。在第一部分中,我们提供了分布式训练算法的简要概述。我们注意到所有算法的共同点是它们依赖于多个 GPU 之间的高速通信。我们推测,考虑到底层实例拓扑的分布式算法,特别是 GPU 对之间通信链路的差异,会比不考虑的算法性能更好。在第二部分中,我们关注数据分发,并展示了 Amazon SageMaker 的分布式数据并行 (SDP)库的优势。

在第三部分中,我们将把注意力转向模型分布。我们将展示一种方式,通过这种方式, Amazon SageMaker 的分布式模型并行库允许您以区分节点内和节点间 GPU 到 GPU 通信的方式来配置您的模型分布算法。

模型分布与 SageMaker 分布式模型并行

与数据分布的 SDP 类似, Amazon SageMaker 分布式模型并行 (SMP)库旨在简化和加速模型分布式训练。该库包含了在第一部分中讨论的每一种模型分发技术的 API(尽管您应该查看文档以获得支持矩阵的详细信息)。它允许组合一些方法,并包括一些自动化配置的控件。

在本节中,我们将重点介绍对分片数据并行性的支持。回想一下,由 ZeRO 算法规定的参数分片算法(在第一部分中描述)并不区分节点内 GPU 链接和节点间 GPU 链接。考虑到这种算法相对较高的通信量(详见此处)以及这种算法通常应用于相对较大的模型的事实,节点间带宽可能成为潜在的瓶颈。SMP 库通过支持一种称为 MiCS 的技术(由于它使mic 通信 s cale】或零 2D 的事实)使您能够解决这个问题。MiCS 引入了分区组的概念。整个 GPU 集合被划分为分区组(大小相等),每个分区组包含模型的一个副本。每个模型副本的参数在模型的分区组中的 GPU 之间进行分片(使用零算法)。在一个分区组中,参数根据零算法进行通信。模型副本之间的对齐通过不同分区组中相应 GPU 之间的梯度共享来维护。这导致了如下图所示的分层通信策略,图中显示了两个节点,每个节点有两个 GPU:

零 2D 中分层通信模式的一个例子。在每个节点内应用基于零的模型分片算法,并且在单独节点的相应 GPU 之间共享梯度。(作者)

例子

这里我们展示了一个将分片数据并行集成到 PyTorch (1.12)培训工作中的例子。我们选择的模型是一个视觉转换器 (ViT)模型,大约有 6.3 亿个参数。该模型是使用 transformers python 包构建的。

下面的代码块演示了如何使用分片数据并行 API 在八个 g4dn.12xlarge 实例(每个实例有 4 个 GPU)上实例化一个模型分布式训练作业。请注意,这种情况下需要模型并行性,因为我们选择的 ViT 模型太大,不适合放在单个 GPU 中。

from sagemaker.pytorch import PyTorchdistribution = {
        "mpi": {
            "enabled": True,
            "processes_per_host": 4
        },
        "smdistributed": {
            "modelparallel": {
                "enabled": True,
                "parameters": {
                    "ddp": True,
                    **"sharded_data_parallel_degree": 32,**
           "delayed_parameter_initialization": True
           },
            }
        },
    }pytorch = PyTorch(entry_point='train.py',
                  role=<role>,
                  instance_type='ml.g4dn.12xlarge',
                  instance_count=8,
                  framework_version='1.12',
                  py_version='py38',
                  distribution=distribution)pytorch.fit()

上面的脚本配置为分片 _ 数据 _ 并行 _ 度设置为 32。这将运行经典的基于零的参数分片算法。要使用 MiCS(零 2D),请将该参数重新配置为所需的分区组大小。

下面的代码块包含相关培训脚本的代码。

import torch
from torch.utils.data import Dataset
import time
import argparse
from transformers import ViTForImageClassification, ViTConfig class FakeDataset(Dataset):
  def __len__(self):
    return 1000000
  def __getitem__(self, index):
    rand_image = torch.randn([3, 224, 224], dtype=torch.float32)
    label = torch.tensor(data=[index % 1000], dtype=torch.int64)
    return rand_image, labeldef build_model():
  model_args = {
    "image_size": 256,
    "patch_size": 16,
    "hidden_size": 1024,
    "num_hidden_layers": 50,
    "num_attention_heads": 16,
    "intermediate_size": 4*1024,
    "num_labels": 1000
  }
  model = ViTForImageClassification(ViTConfig(**model_args))
  return model if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--model_dir', default='/tmp', type=str)
  args, _ = parser.parse_known_args() *# init* import smdistributed.modelparallel.torch as smp
  smp.init()
  from deepspeed.runtime.zero import stage3
  stage3.assert_ints_same_as_other_ranks = lambda x: None
  torch.cuda.set_device(smp.local_rank()) dataset = FakeDataset()
  data_loader = torch.utils.data.DataLoader(dataset,
                                batch_size=4, num_workers=12) model = build_model()
  model = smp.DistributedModel(model) *# add checkpoint activation* for m in model.get_module().vit.encoder.layer.children():
    smp.set_activation_checkpointing(m) optimizer = torch.optim.Adam(model.parameters())
  optimizer = smp.DistributedOptimizer(optimizer)
  loss_function = torch.nn.CrossEntropyLoss() model.train()
  t0 = time.perf_counter()
  summ = 0
  count = 0 @smp.step
  def train_step(model, inputs, targets):
    outputs = model(inputs)
    loss = loss_function(outputs['logits'], targets)
    model.backward(loss)
    return loss for idx, (inputs, targets) in enumerate(data_loader, start=1):
    inputs = inputs.to(torch.cuda.current_device())
    targets = targets.to(torch.cuda.current_device())
    optimizer.zero_grad()
    loss_mb = train_step(model, inputs, targets)
    loss = loss_mb.reduce_mean()
    optimizer.step()
    if torch.distributed.get_rank() == 0:
      batch_time = time.perf_counter() - t0
      print(f'step: {idx}: step time is {batch_time}')
      if idx > 1:  *# skip first step* summ += batch_time
        count += 1
  if torch.distributed.get_rank() == 0:
    print(f'average step time: {summ/count}')

有关 SMP API 的更多详细信息,请参见 SageMaker 文档和示例

结果

在下表中,我们将标准零基参数分片算法(sharded _ data _ parallel _ degree = 32)的步进时间结果与分区组设置为 4(sharded _ data _ parallel _ degree = 4)的零 2D (MiCS)的结果进行了比较。

*均步骤时间(越短越好)——(作者)

我们可以再次看到,考虑到环境细节的算法比基本算法大约高出 14%。我们再次警告不要对您自己的模型下任何结论,因为相对性能可能会因模型细节和环境设置而异。

摘要

多实例训练可能非常昂贵,任何提高训练速度的机会都会对你的总成本产生有意义的影响。在这篇文章中,我们展示了根据底层训练环境的拓扑结构定制的分布式训练算法如何提高性能。然而,这种算法对你的帮助程度,以及采用它们是否是你的正确选择,将在很大程度上取决于你的项目和培训环境的细节。在我们分享的例子中,我们试图演示如何编程您的代码,以便您可以轻松地在不同的选择之间切换。有选择是好事。

自然语言处理中使用约束波束搜索的智能解释

原文:https://towardsdatascience.com/smart-paraphrasing-using-constrained-beam-search-in-nlp-9af6fd046e5c

套用保留搜索引擎优化和文案的特定关键字

来自 Pixabay 的无版权图片

Huggingface 最*在变形金刚库中引入了带有约束波束搜索的引导文本生成

您可以使用受约束的波束搜索来指导哪些单词需要包含在解码的输出文本中。

这在文案和 SEO 中有一些有趣的用例。

用例 1: SEO(搜索引擎优化)

例如,如果你正在写一篇关于“模因营销”的文章,你想要确保“模因营销”这个词在整个博客或文章中被多次使用,以便它在谷歌搜索(SEO)中排名更高,并在该主题上建立权威。

这个概念叫做“关键词填充”,当然需要适度使用,不要显得垃圾。所以这促使我们使用“模因营销”这个短语,即使我们是在转述,也不要改变它。因此,在这种情况下,有必要重写(意译)任何旧的内容,保持短语“模因营销”完好无损。

这是一个完美的受约束的 beam 用例,在你想要解释的地方搜索,同时在解释的版本中保持一个短语或关键字不变。

通常,SEO 专家会识别出长尾关键词,即搜索量较好但相关结果较少的小众短语(例如:*足的最佳跑鞋),并在其上撰写文章。这有助于他们在谷歌搜索中排名更快,并在第一页显示他们的网站。有时,他们想改写自己写的一句话,但又想原封不动地保留“*足的最佳跑鞋”等短语。

用例 2:文案

文案是为任何营销材料撰写内容,无论是博客、网站、传单等。

有效的文案写作是理解人类的心理,并写一个文案,推动客户走向最终目标,如推动注册,销售产品等。

让我们来看看 Marketingexamples.com 的一个文案技巧,建议使用简单的语言而不是多余的语言。

例如,如果我们将登录页面复制为“通过构建真实项目增强您的前端技能”,则建议可以将像 Supercharge、unleash、exceed、empower 这样的登录页面单词替换为更简单的单词,听起来更自然、更真实。

因此,在我们的例子中,我们理想地将这句话解释为“通过构建真正的项目增强你的前端技能”,但是使用词语“改进”听起来更自然。

这也是约束波束搜索的一个完美用例,我们可以将“改进”作为强制字。

作者图片

输入和输出

我们的解释模型的输入将是-

**Supercharge your data science skills by building real world projects.**

带波束搜索的解释器输出(无约束):

**1\. By implementing real world projects, you can improve your data science skills. 
2\. By implementing real world projects, you can boost your data science skills. 
3\. By implementing real world projects, you can enhance your data science skills.**

如果我们用强制字【提高】,则转述输出带约束的光束,搜索结果是:

**1\. By implementing real world projects, you can improve your data science skills. 
2\. By executing real world projects, you can improve your data science skills. 
3\. Build real world projects to improve your data science skills.**

你可以看到“improve”在每个句子中生成,因为我们限制了对它的波束搜索。

项目

现在让我们进入项目的编码部分!这里,我们将使用我们上面讨论的文案示例,并使用约束波束搜索来解释我们的原始句子。

这里可以找到 colab 笔记本

  1. 在 Google Colab 中安装必要的库
**!pip install -q sentencepiece
!pip install -q transformers==4.18.0**

2.下载我们为 SaaS 应用 Questgen 训练的和开源的转述器模型。这个转述器只被训练来转述短句,所以适用于转述标题等。

将模型加载到 GPU 内存中。

**import torch****from transformers import AutoTokenizer, AutoModelForSeq2SeqLM****model = AutoModelForSeq2SeqLM.from_pretrained("ramsrigouthamg/t5-large-paraphraser-diverse-high-quality")
tokenizer = AutoTokenizer.from_pretrained("ramsrigouthamg/t5-large-paraphraser-diverse-high-quality")****device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print ("device ",device)
model = model.to(device)**

现在让我们运行波束搜索和约束波束搜索,输入句子“通过构建真实世界的项目来增强您的数据科学技能。”和 force_word “提高”。

**# Copy writing example - Use a given word in paraphrasing****context = "Supercharge your data science skills by building real world projects."
force_words = ["improve"]****text = "paraphrase: "+context + " </s>"****input_ids = tokenizer(text,max_length =128, padding=True, return_tensors="pt").input_ids
input_ids = input_ids.to(device)****# Beam search****outputs = model.generate(
    input_ids,
    num_beams=10,
    num_return_sequences=3,
    max_length=128,
    early_stopping=True,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
)****print ("\nNormal beam search\n")
print ("Original: ",context)
for beam_output in outputs:
    sent = tokenizer.decode(beam_output, skip_special_tokens=True,clean_up_tokenization_spaces=True)
    print (sent)****# Constrained Beam search****force_words_ids = tokenizer(force_words, add_special_tokens=False).input_ids****outputs = model.generate(
    input_ids,
    force_words_ids=force_words_ids,
    max_length=128,
    early_stopping=True,
    num_beams=10,
    num_return_sequences=3,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
)****print ("\nConstrained beam search\n")
print ("Original: ",context)
for beam_output in outputs:
    sent = tokenizer.decode(beam_output, skip_special_tokens=True,clean_up_tokenization_spaces=True)
    print (sent)**

这是输出结果:

**Normal beam search****Original:  Supercharge your data science skills by building real world projects.
paraphrasedoutput: By implementing real world projects, you can improve your data science skills.
paraphrasedoutput: By implementing real world projects, you can boost your data science skills.
paraphrasedoutput: By implementing real world projects, you can enhance your data science skills.****Constrained beam search****Original:  Supercharge your data science skills by building real world projects.
paraphrasedoutput: By implementing real world projects, you can improve your data science skills.
paraphrasedoutput: By executing real world projects, you can improve your data science skills.
paraphrasedoutput: Build real world projects to improve your data science skills.**

您可以看到,常规波束搜索的输出仅在生成的一个的中包含“改进”,而它出现在约束波束搜索生成的所有三个句子中。

结论:

条件文本生成一直是语言模型文本生成中的一个活跃的研究领域,我们希望将输出导向生成某些关键字或主题。随着在 HuggingFace 中引入约束波束搜索,我们已经向那个目标迈进了一步。

在许多文本生成应用中,如翻译、图像字幕、释义等,我们经常会遇到在生成的输出文本中使用/保留某个关键字/关键短语的需要。

无论是 SEO 还是文案,我们都需要在转述时保持某个关键字/关键短语不变,我们需要强制某个关键字出现以适应文案意图的语气,我们在本教程中已经看到了几个现实世界中的约束波束搜索用例。

祝 NLP 探索愉快,如果你喜欢它的内容,请随时在 Twitter 上找到我。

如果你想学习使用变形金刚的现代自然语言处理,看看我的课程使用自然语言处理的问题生成

智能道路:人工智能在交通中如何保护司机安全

原文:https://towardsdatascience.com/smart-roads-how-ai-in-transportation-keeps-drivers-safe-98e4dfd4a7e8

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

道路技术已经成熟了吗?我们已经看到了非常光滑和耐用的沥青,它适用于任何运输类型。这条路还能怎么改善?

例如,温哥华提议在沥青中加入可回收的塑料颗粒,这样可以增加沥青的耐久性,并允许在重铺路面时部分重复使用。但它只是改善了现有的路面。下一个里程碑是什么?

人工智能在交通领域的运用让他们变得“聪明”。对道路使用者意味着什么?

重新定义未来的道路

许多国家已经在实施与智能城市相关的项目,以改变、简化人们的生活并改善基础设施,包括交通。智能基础设施是全球智能城市市场的最大部分。Statista 估计其收入将在 2025 年超过 1030 亿美元。

人口以及汽车、自行车、摩托车和公共交通数量的持续增长引发了交通问题,增加了事故和交通拥堵的风险。

根据世卫组织的统计,每年大约有 130 万人死于交通事故。死亡不取决于年龄、性别、经济或社会地位。超速、分心驾驶、酒精、手机——任何,甚至是轻微的分心因素——都可能导致不可挽回的灾难。

智能道路提高了能见度,与自动驾驶和互联车辆进行交互,并控制路线,使驾驶体验更加安全。

他们使用许多智能设备:

  • 速度和声音传感器
  • IP 闭路电视摄像机
  • 智能交通灯和路灯
  • 条件和天气监控系统
  • 数字标牌

人工智能智能道路使用来自数千个摄像头,检测器,交通灯和停车计时器的交通数据,主要是为了减少车祸,提高道路效率,缓解拥堵,并从长远来看,帮助城市规划者消除瓶颈。

将如何改善交通体验?

  • 道路压力传感器有助于实时测量车辆的重量和速度,以执行交通法规。
  • 智能交通标志指导司机如何更安全、更高效地使用道路。该技术可以发送语音信息,告诉智能汽车司机应该遵循什么,以避免与多个交通标志混淆。
  • 采用速度摄像头的交通管理网络将通过及时提醒司机危险情况并发送自动交通转换信号进行交通管制,来提高道路安全并减少拥堵。例如,车辆可以改变路线,以防止像雪堆这样的情况。
  • 使用监控摄像头或智能车辆数据的智能交通灯将收集道路和十字路口的当前交通数据。根据这些信息,他们将根据路况变化管理信号。例如,优先安排急救车辆,如救护车和消防车,这样就不会有人在此刻过马路或占用车道。

人工智能与交通管理

智慧城市概念对交通管理提出了新的要求。它涉及交通繁忙的十字路口,那里经常发生拥堵和事故。简单的交通灯不足以进行交通流量分配,相反,我们需要适应道路上特定情况的系统。这是人工智能在大都市和小城镇广泛使用的地方,成功地在具有挑战性的驾驶情况下保持交通流量。

联合国通过了一项关于安全道路运输的决议,该决议强调了创新汽车和数字技术的作用,并符合联合国秘书长的数字合作路线图。该倡议还应有助于到 2030 年将全球道路交通伤亡人数减半,并确保获得安全、负担得起和可持续的运输系统。

Itransition 的创新分析师安德烈·科普特洛夫(Andrey Koptelov)表示:“事实证明,交通领域的人工智能能够让我们比以往任何时候都更快、更安全、更清洁”。

“GPS、传感器、计算机视觉驱动的摄像机和其他互联的物联网设备被部署来收集关于天气、交通、堵塞或事故的数据。然后,这些工具与基于人工智能的分析系统相结合,处理这些信息,通过机器学习算法识别重复出现的交通模式,并将数据转化为有价值的路线建议或潜在道路拥堵预测,”他补充道。

智能交通管理系统

智能交通管理系统(ITMS)的使用不仅限于监测和报告交通拥堵。该系统确保了交通的效率和安全。

ITMS 提供必要的实时信息,以减少乘客的旅行时间,提高驾驶员的安全性和舒适性。例如,它可以与现有的视频监控系统配合工作,对违反交通法规的车辆进行检测和分类。因此,交通违规者可以依法得到交通罚单。

此外,ITMS 还通过以下方式帮助控制交通流量、减少拥堵和预防事故:

  • 监控十字路口移动车辆和行人之间的距离;
  • 阅读车牌;
  • 检测闯红灯、超速、驾驶不当和丢失安全带。

交通灯控制系统

很久以前,交通灯是手动控制的,使用定时器来*稳运行。今天,它们在机器上运行。然而,我们可以让交通灯更有效地工作,更智能地管理时间。

比如人工智能分析视觉信息,定义特定时间道路某一部分的交通拥堵情况。在十字路口交通管制的情况下,计算机统计车辆,确定它们的类型和速度,然后选择交通灯的最佳操作。

自动距离识别

自动距离识别(ADR)使用传感器来确定车辆与前方物体之间的距离。主要目标是防止拥挤的交通。众多汽车制造商提供 ADR 系统,包括特斯拉、沃尔沃和梅赛德斯-奔驰。

安全和紧急情况

当紧急情况发生时,紧急服务需要迅速有效地采取行动,以确保所有公民的安全。ITMS 自动调节信号灯,提醒司机注意分心的事物。新系统有助于快速过渡到救护车和消防车等紧急车辆。它还可以在即将发生拥堵时警告驾车者改道。

人工智能摄像机如何防止道路交通事故

道路监控和交通管理应用程序使用来自数千个交通摄像头和视频监控系统的数据。与传统的计算机视觉方法相比,人工智能提供了更好的传感性能,通过自动适应不断变化的照明和天气条件的算法,提供了对所有道路使用者更可靠和准确的跟踪。

交通摄像头中的人工智能将更准确地模拟所有用户的运动和行为,从行人和骑自行车的人到公共汽车和卡车,以及新的交通工具,如电动滑板车,这些都可能导致额外的道路事故。

装有智能夜视摄像头的车辆给司机多了一双眼睛来监控路况。这些摄像头很容易检测到交通威胁,包括动物、行人和其他车辆。

人工智能数字录像机(DVR)改善了视觉感知。他们可以识别物体与驾驶员的相关性,并采取必要的预防措施,建议减速、改变方向或继续行驶。即使没有特殊标记,智能夜视仪表盘也能为驾驶员提供安全路线。

澳大利亚 Acusensus 将人工智能应用于道路安全问题。他们的摄像头使用高分辨率图像来检测危险的驾驶模式,跟踪和记录车内的行为。

AI Aware 是沃尔沃、HERE、Carmenta Automotive、爱立信、Zenseact、Trafikverket 和瑞典运输协会之间的合作项目。AI Aware 使用人工智能算法来预测一辆车与另一辆车、行人或物体相撞的风险,然后发出警告以防止事故发生。

与此同时,西班牙一直在城市公交车上测试计算机视觉技术,以绘制道路上车辆和行人可能发生问题的位置,从而确定最高的事故风险。在这里,计算机视觉使用人工智能来理解来自公交车摄像头的原始视频数据,这些数据记录了道路位置、行人和车辆位置以及行驶速度。

底线

交通领域的人工智能有助于提高乘客安全,减少交通事故和拥堵。今天的技术让我们习惯于更谨慎地驾驶,从而提供了舒适的驾驶和更高的道路安全性。人工智能智能道路提醒司机遵守特定的交通规则,保持车辆之间的安全距离,防止与行人相撞,保持在正确的车道上,并采取一切必要的预防措施。创新越多,人工智能就越重视安全,帮助司机管理拥堵的道路并意识到危险。

来源列表:

  1. https://www . statista . com/statistics/1111642/world wide-smart-city-market-revenue-segment/
  2. https://royal society publishing . org/doi/10.1098/rspa . 2019.0439
  3. https://singularityhub . com/2020/01/29/how-smart-roads-will-make-driving-easy-safe-and-green/
  4. https://www . Intel . co . uk/content/www/uk/en/transportation/smart-road-infra structure . html
  5. https://www . who . int/news-room/fact-sheets/detail/road-traffic-injuries
  6. https://press.un.org/en/2022/ga12432.doc.htm
  7. https://vrio Europe . com/en/smart-road-technology-digital-highways-of-the-future/
  8. https://www . McKinsey . com/~/media/McKinsey/industries/automotive % 20 and % 20 assembly/our % 20 insights/the % 20 road % 20 to % 20 artificial % 20 intelligence % 20 in % 20 mobility % 20 smart % 20 moves % 20 required/smart-moves-required-the-road-on-artificial-intelligence-in-mobility . pdf
  9. https://www . science direct . com/science/article/pii/s 2215098621000872
  10. https://www . Intel . com/content/www/us/en/transportation/urban-mobility . html

吸烟贝叶斯符合逻辑回归

原文:https://towardsdatascience.com/smokin-bayes-cd6eb089fcd3

贝叶斯推理结合广义线性模型比较吸烟者和非吸烟者死亡率的例子

我没有发现 Bayes 是否吸烟的信息。然而,我可以通过结合先验知识(当时男性吸烟的比例)和数据(画像上的他不吸烟)来估计他是吸烟者的概率。作者图片

前段时间在看抽烟:数据集(这个数据集原来是在的 mosiac R 包里找到的。)在 calmcode.io 上的系列视频。他们向你展示如何分析和比较吸烟者和非吸烟者的 10 年死亡率。(注意,不是吸烟的影响——而是作为一名吸烟者——因为吸烟者的生活方式与那些除了吸烟以外在其他方面没有区别的人不同。)

在这篇文章中,我将使用贝叶斯推理和一个广义线性模型,来计算与 calmcode 的视觉方法相比,作为一个吸烟者的影响。首先,让我们看看他们发现了什么。

视觉方法

吸烟数据集中的每一行代表一个人,她是否吸烟,十年后是否还活着。

[library](https://rdrr.io/r/base/library.html)([tidyverse](https://tidyverse.tidyverse.org))
[library](https://rdrr.io/r/base/library.html)(rethinking)
[library](https://rdrr.io/r/base/library.html)([tidybayes.rethinking](http://mjskay.github.io/tidybayes.rethinking))
[library](https://rdrr.io/r/base/library.html)([tidybayes](https://mjskay.github.io/tidybayes/))

df_raw <- readr::[read_csv](https://readr.tidyverse.org/reference/read_delim.html)("smoking.csv", col_types = "cfi")

df <- df_raw [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [transmute](https://dplyr.tidyverse.org/reference/mutate.html)(
    age,
    is_smoker = smoker,
    is_dead = [if_else](https://dplyr.tidyverse.org/reference/if_else.html)(outcome == "Dead", 1L, 0L)
  )[head](https://rdrr.io/r/utils/head.html)(df_raw)# A tibble: 6 × 3
  outcome smoker   age
  <chr>   <fct>  <int>
1 Alive   Yes       23
2 Alive   Yes       18
3 Dead    Yes       71
4 Alive   No        67
5 Alive   No        64
6 Alive   Yes       38

我们还稍微调整了数据集,并将其保存到df。这将在以后派上用场。从现在开始,我们将关注死亡率而不是存活率。

最基本(也是最天真)的方法是计算每种吸烟状态的死亡率。

df [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [group_by](https://dplyr.tidyverse.org/reference/group_by.html)(is_smoker) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [summarise](https://dplyr.tidyverse.org/reference/summarise.html)(death_rate = [mean](https://rdrr.io/r/base/mean.html)(is_dead))# A tibble: 2 × 2
  is_smoker death_rate
  <fct>          <dbl>
1 Yes            0.239
2 No             0.314

哇,似乎吸烟者的死亡率更低!但是…我们不会因为年龄而“控制”。让我们比较一下每个年龄组的死亡率:

df_agg <- df [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [mutate](https://dplyr.tidyverse.org/reference/mutate.html)(round_age = plyr::[round_any](https://rdrr.io/pkg/plyr/man/round_any.html)(age, 10)) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [group_by](https://dplyr.tidyverse.org/reference/group_by.html)(round_age, is_smoker) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [summarise](https://dplyr.tidyverse.org/reference/summarise.html)(death_rate = [mean](https://rdrr.io/r/base/mean.html)(is_dead), .groups = "drop")[ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)(df_agg, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(round_age, death_rate, color = is_smoker)) + 
  [geom_line](https://ggplot2.tidyverse.org/reference/geom_path.html)() + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(
    title = "Mortality rates of smokers are higher for all age groups",
    y = "10-year mortality rate",
    x = "Age group"
  )

作者图片

这就展现了一个完全不同的画面!在所有年龄组中,吸烟者的死亡率都较高。之前,由于数据集中吸烟者和不吸烟者的年龄比例不同,我们得到了不同的汇总结果。这就是著名的辛普森悖论

Calmcode 视频到此为止。但是我想知道,如果没有宁滨时代,如何更精确地估计差异?

贝叶斯方法

我们将使用带有二项式可能性的广义线性模型。在我们的案例中,当数据被组织到单个试验案例中时(无论该人是否在未来 10 年内死亡),该模型的通用名称是逻辑回归。我们将使用 logit link 对年龄和死亡率之间的关系进行建模。为了确保所有的p值从[∞,∞]映射到[0,1],这种转换是必要的,否则我们在定义先验时必须小心。

这里是logit(p) ~ age在图上的样子:

[ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)(df_agg, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(round_age, logit(death_rate), color = is_smoker)) + 
  [geom_line](https://ggplot2.tidyverse.org/reference/geom_path.html)() + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(
    title = "We will approximate logit(p) ~ age with linear function.", 
    y = "logit(10-year mortality rate)", 
    x = "Age group"
  )

作者图片

通过观察该图,我们似乎可以将logit(death_rate)建模为某一年龄死亡率的线性组合(截距)和代表死亡率随年龄变化的斜率。两组的斜率似乎也是一样的。

由此,我们可以为我们的模型构建一个公式列表并运行它:

formulas <- [alist](https://rdrr.io/r/base/list.html)(
    is_dead ~ [dbinom](https://rdrr.io/r/stats/Binomial.html)(1, p),
    logit(p) <- a[is_smoker] + b * (age - 60),
    a[is_smoker] ~ [dnorm](https://rdrr.io/r/stats/Normal.html)(-0.3, 0.25),
    b ~ [dnorm](https://rdrr.io/r/stats/Normal.html)(0.1, 0.05)
)

model <- [ulam](https://rdrr.io/pkg/rethinking/man/ulam.html)(formulas, data = df, warmup = 200, iter = 500, chains = 4, cores = 4)

我来解释一下formulas:

  • is_dead遵循 1 次试验的二项分布(也称为伯努利分布),而p代表成功的概率(在我们的例子中,成功等于未来 10 年的死亡)。
  • 关联函数logit(p)是十年死亡率的线性组合,取决于一个人是否吸烟,由截距a表示,斜率b在吸烟者和非吸烟者之间共享。基于上面的情节,我决定使用这样的模型。logit(death_rate)似乎与年龄呈线性相关。两组的斜率似乎是相同的。唯一不同的是截距。我决定在 60 岁而不是 0 岁时计算,因为这更直观,而且该模型对低年龄没有意义。
  • 代表两组 60 岁死亡率的截距a的先验值相同。这是基于目测,但它足够广泛,在有意义的范围内。我也可以从其他在线数据集中重用它。
  • 斜率的先验b也是基于目测,但也可以从在线数据集提前确定,在理想情况下更严格。

在查看模型之前,让我们仔细检查一下先验知识:

prior <- [extract.prior](https://rdrr.io/pkg/rethinking/man/extract.samples.html)(model, n = 200)[tibble](https://tibble.tidyverse.org/reference/tibble.html)(
  sample = 1:200,
  a = prior$a[,1], # you get two priors for intercepts (one for each group) but they are the same
  b = prior$b
) [%>%](https://magrittr.tidyverse.org/reference/pipe.html)
  tidyr::[crossing](https://tidyr.tidyverse.org/reference/expand.html)(age = [min](https://rdrr.io/r/base/Extremes.html)(df$age):[max](https://rdrr.io/r/base/Extremes.html)(df$age)) [%>%](https://magrittr.tidyverse.org/reference/pipe.html)
  [mutate](https://dplyr.tidyverse.org/reference/mutate.html)(p = [pmap_dbl](https://purrr.tidyverse.org/reference/map2.html)([list](https://rdrr.io/r/base/list.html)(a, b, age), \(a, b, age) [inv_logit](https://rdrr.io/pkg/rethinking/man/rethinking-internal.html)(a + b * (age - 60)))) [%>%](https://magrittr.tidyverse.org/reference/pipe.html)
  [ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)() +
  [geom_line](https://ggplot2.tidyverse.org/reference/geom_path.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(age, p, group = sample), alpha = 0.2) +
  [geom_point](https://ggplot2.tidyverse.org/reference/geom_point.html)(data = df_agg, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(round_age, death_rate, color = is_smoker)) + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(title = "Priors seem fine", y = "10-year mortality rate")

作者图片

前科看起来不错——信息足够丰富,但也很灵活。链的跟踪和 trank 图看起来也不错——你必须相信我。

以下是该模型的摘要:

[summary](https://rdrr.io/r/base/summary.html)(model)Inference for Stan model: 49f5d62bf6172f02e3638f3ae2372250.
4 chains, each with iter=500; warmup=200; thin=1; 
post-warmup draws per chain=300, total post-warmup draws=1200.mean se_mean   sd    2.5%     25%     50%     75%   97.5%
a[1]   -0.04    0.00 0.12   -0.26   -0.13   -0.05    0.04    0.20
a[2]   -0.20    0.00 0.10   -0.39   -0.26   -0.20   -0.14    0.01
b       0.12    0.00 0.01    0.11    0.12    0.12    0.13    0.14
lp__ -474.88    0.06 1.23 -478.10 -475.46 -474.57 -473.98 -473.49
     n_eff Rhat
a[1]   627    1
a[2]   678    1
b     1177    1
lp__   494    1

Samples were drawn using NUTS(diag_e) at Mon Jan 10 19:35:02 2022.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

我们可以看到,与不吸烟者的截距a[2]相比,吸烟者的截距a[1]具有更高的死亡率。我们可以使用tidybayes软件包中的便捷函数绘制这两个参数的整体分布图(已经转换为 60 岁时的死亡率)及其差异:

samples_a <- model [%>%](https://magrittr.tidyverse.org/reference/pipe.html)
  [recover_types](http://mjskay.github.io/tidybayes/reference/recover_types.html)(df) [%>%](https://magrittr.tidyverse.org/reference/pipe.html)
  [spread_draws](http://mjskay.github.io/tidybayes/reference/spread_draws.html)(a[is_smoker]) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [mutate](https://dplyr.tidyverse.org/reference/mutate.html)(p = [inv_logit](https://rdrr.io/pkg/rethinking/man/rethinking-internal.html)(a)) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [bind_rows](https://dplyr.tidyverse.org/reference/bind.html)(.,  [compare_levels](http://mjskay.github.io/tidybayes/reference/compare_levels.html)(., p, by = is_smoker)) [ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)(samples_a, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(p, is_smoker)) + 
  [stat_halfeye](http://mjskay.github.io/ggdist/reference/stat_sample_slabinterval.html)() + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(title = "Posterior distributions for intercepts and their difference")

作者图片

这些分布基于后验样本。有了它们,我们还可以计算出不吸烟者死亡率较低的概率:

samples_a [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [filter](https://dplyr.tidyverse.org/reference/filter.html)(is_smoker == "No - Yes") [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [summarise](https://dplyr.tidyverse.org/reference/summarise.html)(prob = [mean](https://rdrr.io/r/base/mean.html)(p < 0))# A tibble: 1 × 2
  is_smoker  prob
  <chr>     <dbl>
1 No - Yes  0.845

吸烟者有 85%的几率会有更高的死亡率。我们还可以看到死亡率的差异是如何随年龄变化的(注意,差异的形状是由我们的模型定义的,而不是大小):

newdata <- tidyr::[crossing](https://tidyr.tidyverse.org/reference/expand.html)(
  age = 20:90, 
  is_smoker = [unique](https://rdrr.io/r/base/unique.html)(df$is_smoker)
)linpreds <- [add_linpred_draws](http://mjskay.github.io/tidybayes/reference/add_predicted_draws.html)(model, newdata = newdata, value = "p") linpreds [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [group_by](https://dplyr.tidyverse.org/reference/group_by.html)(age, .draw) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [summarise](https://dplyr.tidyverse.org/reference/summarise.html)(
    p_diff = p[is_smoker == "Yes"] - p[is_smoker == "No"]
  ) [%>%](https://magrittr.tidyverse.org/reference/pipe.html) 
  [ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(x = age)) +
  [stat_lineribbon](http://mjskay.github.io/ggdist/reference/stat_lineribbon.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(y = p_diff), .width = 0.9, fill = "gray") + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(
    title = "Mean difference in mortality rate with 90% credible interval",
    subtitle = "Shape of the difference was imposed by our model.",
    y = "Difference in 10-year mortality rate with 90% CI"
  )

作者图片

60 岁左右人群的 10 年死亡率似乎*均增加了约 4%(90%可信区间在 10%和-2.5%之间),年龄越小死亡率越低。这可能是有道理的:

  • 对于年轻人来说,吸烟的影响还没有那么大(而且他们的死亡率也更低);
  • 即使不吸烟,老年人也更容易死亡。

我们可以通过除以而不是减去死亡率来计算死亡率的相对上升。在这种情况下,年轻人的相对上升幅度约为 20%,然后开始缓慢下降,在 60 岁时约为 10%,然后几乎可以忽略不计。

我们还可以看到所有年龄组中两组死亡率的 90%可信区间:

[ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)(linpreds, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(x = age, color = is_smoker, fill = is_smoker)) +
  [stat_lineribbon](http://mjskay.github.io/ggdist/reference/stat_lineribbon.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(y = p), .width = 0.9, alpha = 0.5) +
  [geom_point](https://ggplot2.tidyverse.org/reference/geom_point.html)(data = df_agg, [aes](https://ggplot2.tidyverse.org/reference/aes.html)(x = round_age, y = death_rate)) + 
  [labs](https://ggplot2.tidyverse.org/reference/labs.html)(
    title = "Mortality rate ~ age with 90% credible intervals",
    y = "10-year mortality rate"
  )

作者图片

结论

使用logit链接函数使我们能够将伯努利变量p (10 年死亡率)建模为年龄的线性函数。另一方面,贝叶斯方法使我们能够获得参数值的分布——对于任何年龄和吸烟状态。我们可以按照我们喜欢的方式进行汇总:这是否是吸烟者死亡率更高的可能性,死亡率(差异)的可信区间(最终有意义的置信区间)……

贝叶斯推理和广义线性模型代表了一种灵活而强大的组合。但是,正确地指定模型背后的假设(公式集)是我们的责任。从这些发现中学习——所以,不要吸烟。并使用吸烟贝叶斯。

原载于https://mihagazvoda.com/

通气管——构建训练数据的人在回路*台

原文:https://towardsdatascience.com/snorkel-programmatically-build-training-data-in-python-712fc39649fe

停止手工标记您的数据。而是以编程方式对其进行标记

动机

想象一下,你试图判断一份招聘启事是不是假的。你想出了一些关于虚假招聘的假设,比如:

  • 几乎没有描述要求的招聘信息很可能是假的。
  • 没有公司简介或标识的招聘信息很可能是假的。
  • 一份需要一些教育或经验的工作很可能是真实的。

如何测试这些特征在预测欺诈方面的准确性?如何将不同要素的冲突预测合并到一个标注中?

作者图片

这时候浮潜就派上用场了。

什么是浮潜?

snuck是一个开源的 Python 库,用于以编程方式构建训练数据集,无需手动标记。有了通气管,用户可以在几小时或几天内创建一个训练数据集,而不是在几周或几个月内手动标记它们。

通气管支持不同的操作,但本文将只回顾标记操作。

要安装通气管,请键入:

pip install snorkel

加载数据

我们将在 Kaggle 的数据集真实/虚假职位发布预测上使用浮潜来预测职位发布是虚假还是真实。I 对数据进行预处理,并将其分成训练集和测试集。

让我们看看训练数据集是什么样的:

import pandas as pd

train_df = pd.read_pickle("https://github.com/khuyentran1401/Data-science/blob/master/feature_engineering/snorkel_example/train_fake_jobs.pkl?raw=true")
test_df = pd.read_pickle("https://github.com/khuyentran1401/Data-science/blob/master/feature_engineering/snorkel_example/test_fake_jobs.pkl?raw=true")

train_df

fraudulent栏表示招聘信息是否是假的。

现在我们有了数据,我们如何给每个招聘信息贴标签呢?

创建标签功能

让我们根据对假公司的假设创建标签函数:

  • 假公司没有公司简介或标识
  • 真正的公司需要背景调查
  • 真正的公司需要一定的经验或教育水*

让我们使用 scupco 的labeling_function装饰器来测试这些假设。labeling_function装饰器允许我们使用函数快速标记数据集中的实例。

from snorkel.labeling import labeling_function

# Give a meaningful name to each value
FAKE = 1
REAL = 0
ABSTAIN = -1

# Create labeling functions

@labeling_function()
def no_company_profile(x: pd.Series):
    return FAKE if x.company_profile == "" else ABSTAIN

@labeling_function()
def no_company_logo(x: pd.Series):
    return FAKE if x.has_company_logo == 0 else ABSTAIN

@labeling_function()
def has_background_check(x: pd.Series):
    return REAL if "background check" in x.requirements else ABSTAIN

@labeling_function()
def required_experience(x: pd.Series):
    return REAL if x.required_experience else ABSTAIN

@labeling_function()
def required_education(x: pd.Series):
    return REAL if x.required_education else ABSTAIN

关于上述标签功能的详细信息:

  • 每个函数都试图根据一些条件来确定样本是否是假的。
  • ABSTAIN-1表示预测是不确定的。

对数据应用标注函数

让我们使用这些标注函数来标注我们的训练数据集:

lfs = [
    no_company_profile,
    no_company_logo,
    has_background_check,
    required_experience,
    required_education,
]

applier = PandasLFApplier(lfs=lfs)
L_train = applier.apply(df=train_df)

查看训练数据集:

# Get the shape
>>> L_train.shape 
(13410, 5)

# View the first two samples
>>> L_train[0:2]
array([[-1, -1, -1,  0,  0],
       [-1, -1, -1, -1, -1]]) 

L_train的第一维是样本数。L_train的第二维度是来自五个标记函数的预测。

使用真实标签评估标签功能

既然我们已经使用每个标注函数创建了标签,那么我们如何确定这些标签的准确性呢?有了LFAnalysis,这可以很快完成。

让我们将预测与实际标签进行比较,以评估每个标签功能:

from snorkel.labeling import LFAnalysis

LFAnalysis(L=L_train, lfs=lfs).lf_summary(Y=train_df.fraudulent.values)

上表中的详细统计数据:

  • 极性:该 LF 输出的唯一标签集合(不包括弃权)
  • 覆盖率:数据集被标记的部分
  • 重叠:这个 LF 和至少一个其他 LF 一致的数据集部分
  • 冲突:该 LF 与至少一个其他 LF 不一致的数据集部分
  • 修正:该 LF 标注的数据点数正确
  • 不正确:该 LF 标注的数据点数不正确
  • 经验精度:该 LF 的经验精度,通过以下公式计算:

作者图片

从表中可以看出has_background_checkrequired_experiencerequired_education标注函数的精度都在 0.95 以上。

如果不进行测试,我们就无法猜测这些特征是虚假招聘的有力证据。

评估没有真实标签的标签功能

在评估没有真正标签的标签功能时,通气管也是理想的选择。

LFAnalysis(L=L_train, lfs=lfs).lf_summary()

评估冲突

我们可以看到标签功能之间存在一些冲突。这可能意味着一个标注函数预测一个职位发布是假的,而另一个标注函数预测一个职位发布是真的。

可能会有一些招聘广告要求特定的经验,但没有公司的标志。让我们用get_label_buckets找到这些招聘信息。

from snorkel.analysis import get_label_buckets

buckets = get_label_buckets(L_train[:, 1], L_train[:, 3])

train_df.iloc[buckets[(FAKE, REAL)]].sample(10, random_state=1)[
    ["has_company_logo", "required_experience", "fraudulent"]
]

上表可以看出,很多公司要求具体经验,但是没有公司 logo。这导致了两个标记函数之间的预测差异。

组合预测

在本节中,您将学习两种在不同标注函数的预测不一致时组合它们的方法。

作者图片

多数标签投票人

一个简单的方法是使用多数票。如果有两个1s和一个0,那么预测将是1

作者图片

如果没有连接,忽略-1(无标签)。

作者图片

如果有*局,则返回-1

作者图片

让我们尝试一下这个标签模型,看看它在测试集上的表现如何:

from snorkel.labeling.model import MajorityLabelVoter

# Create the model
majority_model = MajorityLabelVoter()
preds_train = majority_model.predict(L=L_train)

# Get test predictions
L_test = applier.apply(df=test_df)

# Get train and test labels
Y_train = train_df["fraudulent"]
Y_test = test_df["fraudulent"]

# Evaluate
majority_acc = majority_model.score(L=L_test, Y=Y_test)[
    "accuracy"
]
print(f"{'Majority Vote Accuracy:':<25} {majority_acc * 100:.1f}%")
Majority Vote Accuracy:   80.7%

成绩还算不错!

标签模型

注意,MajorityLabelVoter没有考虑不同标记函数的变化统计以及这些函数之间的相关性。如果两个特征高度相关,特定信号将在基于多数投票的模型中被过度表示。

LabelModel通过产生噪声感知训练标签来更恰当地处理这些问题。你可以在本文中了解更多关于LabelModel的信息。

from snorkel.labeling.model import LabelModel

label_model = LabelModel(cardinality=2, verbose=True)
label_model.fit(L_train=L_train, n_epochs=500, log_freq=100, seed=1)

# Evaluate the model
label_model_acc = label_model.score(L=L_test, Y=Y_test)["accuracy"]
print(f"{'Label Model Accuracy:':<25} {label_model_acc * 100:.1f}%")
Label Model Accuracy:     72%

LabelModel的精确度不如MajorityLabelVoter的精确度。然而,在不同的数据集或不同的标记函数集中,LabelModel可能比MajorityLabelVoter执行得更好。

结论

恭喜你!您刚刚学会了使用不同的标注函数进行快速测试,并使用通气管为您的训练数据集创建标注。我希望这篇文章能激励你自动化你的标注过程,而不是花费数周或数月来手工标注你的数据。

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

https://github.com/khuyentran1401/Data-science/tree/master/feature_engineering/snorkel_example

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。

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

[## 使用 dirty_cat 对脏类别进行相似性编码

towardsdatascience.com](/similarity-encoding-for-dirty-categories-using-dirty-cat-d9f0b581a552)

参考

Shivam Bansal 。2020–02–29.真/假职位发布预测。
CC0:公共领域。从https://www . ka ggle . com/shivamb/true-or-fake-fake-job posting-prediction检索 2022–01–25。

针对用户、角色和权限的雪花最佳实践

原文:https://towardsdatascience.com/snowflake-best-practices-for-users-roles-and-permissions-3cd9d286b82a

正确设置数据仓库的指南

Unsplash 上由Towfiqu barb huya拍摄的照片

数据仓库中的安全性是雪花架构中最重要的部分,也是经常被搁置的部分。很多时候,我们认为它没有数据本身重要。然而,设置正确的角色和权限是保持数据完整性的关键。从一开始就对此进行配置可以确保防止出现问题。

让我描述一下可能会出错的地方。您可以在 Snowflake 中创建一个角色,供业务中的所有用户使用。分析师、营销人员和工程师对每个数据库都有相同类型的访问权限。您有一个用于一次性分析的报告和数据分析(RDA)数据库,以及一个存储所有原始数据的数据库。一名营销人员正在寻找一份在 RDA 数据库中运行的报告,但意外地在原始数据库上运行了一个命令。他们覆盖数据,现在原始数据永远消失了。

你明白了。不是组织中的每个人都知道如何使用雪花或者正确地查询数据。您希望降低风险,以便他们可以使用这些数据,而您可以放心地生活,知道它是安全的。

当配置您的雪花环境时,我发现从创建您的用户开始更容易,然后使用用户来确定您的环境所需的雪花角色。在这里,您可以创建相应的仓库,并重置每个用户的默认值。让我们从创建我们的用户开始。

用户管理

每个访问你的数据仓库的人和工具都应该有自己的用户。这意味着分析师 Josh 需要自己的雪花用户,工程师 Sarah 需要自己的用户,连接到您的数据仓库的每个工具都需要自己的用户。这将严格控制谁有权访问某些资源。它还允许您跟踪信用使用情况和所做的更改。

让每个人和工具都有自己的用户名和密码是最佳的安全做法。这样,密码就不会到处乱放,而且你知道每个人都可以访问你公司的数据。此外,请记住,您可能不希望公司的每个人都可以访问雪花。

创建用户

要在雪花中创建用户,请运行以下命令:

CREATE USER <user’s name> PASSWORD=<dummy password> DEFAULT_ROLE=<role name> DEFAULT_WAREHOUSE=<warehouse name> MUST_CHANGE_PASSWORD=true

创建用户时,您必须为他们分配一个临时密码。出于安全目的,请确保设置 MUST_CHANGE_PASSWORD=true。这将强制用户在首次登录时更改密码。您也可以在用户界面上这样做,只需选中底部的复选框:

作者图片

但是,如果您正在为工具创建用户,您不希望选择此项。例如,如果您使用 Airbyte 作为您选择的数据集成工具,您将创建一个用户 AIRBYTE_USER。您不希望选中需要更改密码的复选框。该工具无法在登录时更改其密码,因此请确保您从一开始就为其分配了安全密码,并将其存储在 1Password 等密码管理器应用程序中。

请注意,在使用命令创建用户时,我还设置了某些默认值,如角色和仓库。这很重要,尤其是当用户第一次学习如何使用雪花的时候。他们可能不明白如何改变自己的角色或使用正确的角色。为他们分配一个默认角色,以帮助管理他们拥有的权限。例如,分析师将最经常地分析数据,因此他们的默认角色应该是与报告相关的角色,即使他们有时可能会转换数据。

设置默认仓库

您还需要设置一个默认仓库来帮助管理数据仓库的成本和使用。这不一定是一项安全功能,但它将帮助您了解您的雪花积分被用于何处。为用户分配与其角色相关的仓库。接下来我们将更多地讨论具体的角色。

您创建的仓库应该与您创建的角色相匹配。这意味着,对于每一个角色,你都应该有一个对应的仓库。我有一个“加载 _WH”,“转换 _WH”,“分析 _WH”,“报告 _WH”。每个仓库对应于我将在下一部分中提到的角色。这将有助于组织资源,并允许您跟踪数据管道的哪些部分使用了最多的雪花配额。

角色管理

我们刚刚讨论了与您应该设置的默认仓库相关的角色,但是您首先应该在您的仓库中创建什么角色呢?在这里,我们将讨论不同类型的角色以及他们应该拥有的特定权限。虽然这因业务而异,并且取决于访问您的仓库的用户类型,但原则是相同的。

在深入研究角色类型和应该授予它们的权限之前,让我们简单讨论一下我是如何组织我的雪花数据仓库的。我有一个原始数据库,它吸收了我所有的原始数据。我的基本数据库从这个原始数据库中读取数据,以创建供我的数据模型使用的视图。然后我有了 DATA_MART_DEV 和 DATA_MART_PROD 数据库,用于在开发和生产中运行我的核心数据模型。最后,我有一个用于一次性报告和查询的 RDA(报告和数据分析)数据库。你可以在这里阅读更多关于我为什么这样组织我的雪花数据仓库的信息。

现在,我们来谈谈角色吧!

角色类型

最佳实践是为组织内的摄取工具、分析师、工程师和业务用户创建不同的角色。您还可以为 BI 工具或者反向 ETL 工具(如果您使用的话)创建特定的角色。为每个用户和工具创建不同的角色将允许您控制每个用户在每个数据库及其模式中可以做什么和不可以做什么。

摄取工具权限:

  • 获取原始数据
  • 可以查看、选择和创建 RAW

是的,你没看错——你的摄取工具应该有自己的角色!这是应该允许写入原始数据库的唯一角色。您的摄取工具是唯一应该在原始数据位置创建模式和表的工具。绝不允许公司内的任何人写入此内容。

摄取工具是将原始数据写入数据库的唯一方法。‍

这是数据库中最重要的角色,因为它拥有访问原始数据的能力。您的原始数据需要尽可能地安全,因为所有数据模型的完整性都依赖于它。如果公司内部的人在戳戳这些原始数据,你永远无法确定它是 100%准确的。这一角色的存在增加了人们对该数据可靠性的信心。

我将这个角色称为“加载器”,因为它是将原始数据加载到您的数据仓库中的工具。这是在将数据源中的数据接收到雪花中时,您将为 Airbyte 提供的角色。你可以阅读更多关于为了正确使用 Airbyte 你需要给角色的特定权限的信息。为特定工具创建角色时,阅读该工具文档中的需求总是很有帮助的。

作者图片

Airbyte 提供了一个脚本来设置所有需要的权限,以便该工具将数据接收到您的仓库中。

分析工程师权限:

  • 访问 RAW、BASE、DATA_MART_DEV、DATA_MART_PROD 和 RDA
  • 可以从原始、基本和数据集市产品中查看和选择
  • 可以在 DATA_MART_DEV 和 RDA 中查看、选择和创建

分析工程师,或者任何编写 dbt 数据模型的人,应该是唯一能够访问原始数据库的人。但是,他们应该只能查看和选择,而不能创建或删除。这样他们就无法更改原始数据。

在我的数据仓库中,我将这个角色称为“转换器”。它由转换数据的人和工具使用。具有此角色的用户有权从原始数据库中读取数据,并向存储模型的所有其他数据库中写入数据。

分析师权限:

  • 对 BASE、DATA_MART_PROD 和 RDA 的访问
  • 可以查看和选择基本数据和数据集市产品
  • 可以在 RDA 中查看、选择和创建

因为分析师不创建基础模型,他们没有理由访问原始数据。他们需要的所有东西都应该放在基地里,减少意外删除原始数据的风险。此外,他们应该只拥有对 BASE 和 DATA_MART_PROD 的查看和选择权限,因为他们只是在查询中选择这些内容,而不是写入这些内容。

我把这个角色叫做“分析者”。他们不直接在数据仓库中转换任何数据,而是使用数据模型运行查询,并将它们写入“RDA”。

业务用户权限:

  • 对 BASE、DATA_MART_PROD 和 RDA 的访问
  • 可以查看并从中选择

业务用户应该能够查看数据和进行基本选择,但不能以任何方式修改数据。

我称这个角色为“报告者”,因为这个角色仅仅是报告数据,而不是写数据。Tableau 和 Looker 等数据可视化工具也使用这个角色。

创建角色和分配权限

为了在雪花中创建角色,您可以运行以下命令:

CREATE_ROLE <role_name>;

为了向雪花中的角色授予权限,您可以运行以下命令:

GRANT <privledge> to ROLE <role_name>;

最后,确保将您的角色分配给适当的用户。您可以通过以下方式实现:

GRANT ROLE <role_name> to USER <user_name>;

雪花的权限层次结构

您需要授予数据库、模式、表/视图和未来的表/视图一定的权限。Snowflake 的权限是独一无二的,因为您不能将权限分配给数据库,并期望它也应用于数据库中的模式和表/视图。

作者图片

授予数据库权限

首先,让我们回顾一下您可以在数据库级别授予角色的最重要的权限。

  • 监视器允许角色查看对象的详细信息;您需要将此权限授予所有希望查看数据库的角色。
  • 用法允许角色使用数据库;您需要将此权限授予任何希望查询数据库的角色。
  • CREATE 授予角色在数据库中创建对象的能力。这对于在数据库“BASE”、“DATA_MART_DEV”和“DATA_MART_PROD”上分配您的 TRANSFORMER 角色非常重要。您的 LOADER 角色应该被授予“RAW”权限,而 ANALYZER 角色应该被授予“RDA”权限。

向架构授予权限

现在,让我们回顾一下模式级别的重要权限。

  • 同样,您需要将数据库中模式的 MONITOR 权限授予角色。
  • 用法允许角色使用架构;您需要将此权限授予任何希望查询模式的角色。
  • CREATE 也存在于模式级别。您需要将它分配给我上面提到的数据库中模式的所有角色。

作者图片

授予表和视图权限

最后,这是事情变得不同的地方。表和视图拥有来自数据库和模式的独有特权。

  • SELECT 允许角色从表或视图中进行选择。您会希望将它分配给允许查询某个对象的每个角色。
  • INSERT 和 DELETE 都是应该授予您的 TRANSFORMER 角色在“DATA_MART_DEV”和“DATA_MART_PROD”上的权限。因为这是分配给 dbt 用户的角色,所以它需要能够对表进行更改。dbt 需要这些权限来运行增量模型。请记住,这些权限只适用于表,不适用于视图。

对于表、视图和模式,您还需要确保对模式或数据库中的未来表和视图授予权限。这将使您不必对创建的新对象授予访问权限。

-- for tables
grant select on future tables in schema FACEBOOK to role ANALYZER;-- for views
grant select on future schemas in database BASE to role TRANSFORMER;

结论

在运行任何雪花命令之前,最好记录您的雪花架构,尤其是每个角色的权限。如果您没有清楚地记录每件事,授予权限可能会令人困惑。我知道当你认为你已经授予了所有需要的权限,然后一个角色仍然不能访问你需要的权限时,会很沮丧。

慢慢来。不要因为沮丧就授予一个角色所有可用的权限。记录良好的雪花用户、角色和权限是安全数据仓库的关键。随着您的组织的成长,并且开始越来越依赖于您已经建立的数据生态系统,最初的工作会有很长的路要走。

更多分析工程领域的最佳实践,订阅我的时事通讯。

所以,你认为你可以数据科学?这里有 4 件事你应该知道。

原文:https://towardsdatascience.com/so-you-think-you-can-data-science-heres-4-things-you-should-know-cb953ead14d6

一些你应该知道的关于荣耀领域的重要事实。

照片由 JJ 英Unsplash

“哦,哇,你在研究数据科学?你已经准备好了。”

当我不情愿地默认人们关于我在攻读博士学位的问题时,我经常得到这样的回答。如果不是口头上的,那就用眼神。从技术上讲,我的项目叫做以人为中心的设计和工程,但我花了大部分时间来磨练我的数据科学技能,因为这是我的主要研究兴趣。

虽然我真诚地感谢人们的赞美,但我也认为它们源于对整个数据科学的误解。我不是天才,我认为自己很幸运能参加这个项目——我每天都在尽可能多的学习。在此过程中,我发现了一些关于数据科学的重要事实,我认为这个领域应该有更多的人知道。

在本文中,我将讨论数据科学幕后的一些鲜为人知的现实——我希望通过阅读它,您可以更清楚地了解进入该领域的真正意义。

1.不仅仅是“数据科学家”

数据科学的核心是一项简单的任务:从数据中获得更清晰的洞察力。这绝不是一个简单的或独特的技术技能。它包括许多不同的活动部分,并且从许多不同的学科中汲取灵感。

仅仅因为某人没有“数据科学家”的正式头衔并不意味着他们没有研究数据科学。根据它的定义,这个领域需要许多不同类型的工作者的帮助才能繁荣。以下是它们的随机样本(是的,那是数据科学参考):

  • 数据工程师:研究如何清理、存储和处理数据的基础设施。没有他们,有效和高效的数据科学几乎是不可能的。
  • UX 的研究人员:使用定量和定性技术的结合来收集关于产品用户的信息,并使用他们的发现来提出改进建议,以增强用户体验[2]。
  • 统计学家:他们是一群人中在数学方面有天赋的。如果你需要从头开始开发一个机器学习模型,或者找出正确的超参数来使用,是时候召集统计学家了。
  • 社会科学家:不可否认,这个问题有点争议,但是我决定把它包括进来,因为它经常被忽视。如果不是数据科学家本身,社会科学家至少是整个数据科学过程的重要组成部分,因为他们可以提供数据集背景下急需的洞察力,并就任何结论的伦理影响提供指导。

长话短说,术语“数据科学家”是一个笼统的术语,实际上可能包括以上任何一种。要点很简单:数据科学不仅仅是调整一些奇特的机器学习模型来神奇地解决你所有的问题 [3]。它有许多不同的方面,所有这些都在从数据中收集有意义的见解这一主要目标中发挥着作用。

如果你想要证明这一点,你需要做的就是去 LinkedIn 浏览一下。你会发现上面的标题用得相当流畅。有些人是“机器学习工程的负责人”,但拥有统计学博士学位,有些数据科学家以前的头衔是 UX 研究员,有些数据工程师决定离开高级模型,更多地进入数据管理的本质,等等。我很好奇你会在评论中发现什么。

2.你永远也不会知道的足够多

大学二年级时,我参加了一门基础概率论课程。但是因为那个学期我很忙也很累,所以我几乎没有去上课。

我至今后悔那个决定。由于缺少必备的知识,我在大学期间无法学习更高级的课程,而现在,作为一名研究生,我的知识基础中有一个缺口,我正在拼命地寻找时间来填补。概率方法的坚实基础是数据科学许多方面的重要先决条件。

然而,我最*意识到,也许我对自己逃课太苛刻了。如果我真的知道概率,那我今天还需要学些别的东西。数据科学是一个不断扩展的领域,总会有知识空白需要填补。

没有人能知道一切。这里有一些证据:

  • 我的导师在博士期间辅修了统计学——但她告诉我,在开始教授生涯后,她不得不花大量时间自学实用的统计方法,因为她课程中的理论知识都不适用于她的工作。
  • 大学时,我给我的一位教授看了一张我制作的图表。尽管他被认为是全国推动数据科学的领军人物之一,但他喜欢我的图表,而且之前从未听说过这种类型的编码(这是一张氯普图 [4】)。直到那一刻,我都不认为自己有能力创作出任何他会觉得新奇或有趣的东西。

这里的要点是:这不是你已经知道什么,而是你有多大的意愿继续学习。

3.这些数据可能没什么有趣的

当我开始攻读博士学位时,我的导师警告我说,研究是一项充满压力的工作,特别是因为一个强有力的原因:你可能花几个月甚至几年的时间研究一个问题,但随后发现你的解决方案不起作用或者根本不可行。

虽然所有的研究都是如此,但在数据科学领域,这是一个特别相关的事实。作为一名数据科学家,你的工作是发现数据中固有的已经存在的 T2。你不能从伦理上重塑它以适应你的需要;因此,不管你的最终目标是什么,你总是至少在冒险。你的发现可能会揭示出与你预期相反的东西,或者根本不会揭示任何东西。

或者,你也完全有可能搞砸原本是好的数据。我的一个朋友曾经和一位验光师一起在参加了两年的研究学习。在最后一次会议上,验光师问了一些总结性的问题,并兴奋地讨论她能从这些数据中学到多少。但是,当我的朋友回答其中一个问题时,验光师意识到她一直在错误地记录数据的一个重要方面——导致她所有的样本实际上都无法使用。

无论是由于人为错误还是数据中固有的错误,总有一天你的所有工作都将化为乌有。这可能感觉像是浪费投资,但请放心,这不是——你会从这个过程中学到很多东西,没有人能从你那里拿走。

这里有两个教训:小心,但要原谅自己。

4.这些钱并不值得

我想这在技术上不是数据科学特有的,但我提到它是因为数据科学似乎是人们加入“快速致富”努力的一长串领域中的最新一个。

但问题是。如上所述,数据科学是很难的,压力也很大。最重要的是,如果你对自己的工作没有激情,你就会陷入悲惨的境地。无论您从事理论统计、软件开发还是领域专业,您都需要确保从数据中获取重要见解的核心目标是一项让您兴奋的任务。

我个人可以担保这件事的重要性。2020 年夏天,我有了第一次技能实习。因为是在工业界,每小时的报酬比我以前从事的教学和研究工作要好。

然而,这项工作对我来说非常无聊和复杂。这是一个糟糕的组合,因为它会导致你不知道自己在做什么,也没有尝试和改进的欲望。虽然我的实习更多地是面向软件工程,但我认为数据科学也是这种情况的温床。

我不是说钱不重要。确实如此——但这是另一篇文章的主题。在这里,我只想强调,如果你鄙视你正在做的事情,金钱不会让你的日常生活变得更好。

如果你做数据科学,应该是因为你喜欢做数据科学。

最后的想法和总结

接受以上关于数据科学的现实让我对这个领域的视野更加清晰,我希望它也能为你做同样的事情。以下是对上述观点的快速回顾:

  • 术语数据科学包含了广泛的活动和职业——不要认为你不适合它,因为你不符合某些传统模式。
  • 总会有更多的东西需要学习——愿意和开放比以专家的身份进来更重要。
  • 做好准备,可能会从事一个漫长、令人厌倦的项目,最终结果令人失望。
  • 不要仅仅为了钱而进入数据科学。你需要享受它,否则你将永远痛苦。

祝您的数据工作好运,下次再见!

想擅长 Python? 获取独家,免费获取我简单易懂的指南在这里 。想在介质上无限阅读故事?用我下面的推荐链接注册!

https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------

我叫 Murtaza Ali,是华盛顿大学研究人机交互的博士生。我喜欢写关于教育、编程、生活以及偶尔的随想。

参考

[1]https://towards data science . com/the-three-building-blocks-of-data-science-2923 DC 8 C2 d 78
【2】https://boot camp . learn . utoronto . ca/blog/what-is-a-UX-research/
【3】https://towards data science . com/when-did-data-science-become-与-machine-learning-2d7d9f 同义

所以你得到了一个非常大的数据集。这是你清洁它的方法。

原文:https://towardsdatascience.com/so-youve-got-a-dataset-here-s-how-you-clean-it-5d0b04a2ed86

用 Python 进行数据清理的详细分步指南,并附有示例代码。

图片来自马库斯·斯皮斯克 (Unsplash)

在抓取、合并或直接从互联网上下载之后,你就有了一个数据集。你在想你能在上面运行的所有漂亮的模型,但是首先,你必须清理它。你可以有一百万种不同的开始方式,老实说,每次我开始的时候都会让我选择麻痹。

在处理了几个杂乱的数据集之后,下面是我如何构建我的数据清理管道。如果您有更有效的代码或对这些步骤的修改来减少偏见,请留下评论,我会将它合并到文章中!

查看我制作的这个概览图,并继续阅读每个步骤的更多细节。我提供的代码片段是用 Python 编写的,将主要使用 Pandas 和 matplotlib 库。

过滤器

如果您正在处理数据的子集,您希望确保数据代表感兴趣的子集。筛选特定列的特征以确保。

对于地理数据,使用感兴趣区域的 shapefile 和 geopandas sjoin 函数过滤掉外部数据。

# Dataframe of point coordinates
points_gdf **=** geopandas**.**GeoDataFrame(
    df[['uprn', 'LATITUDE', 'LONGITUDE']],
    geometry**=**geopandas**.**points_from_xy(df**.**LONGITUDE, df**.**LATITUDE),
    crs**=**'epsg:4326') # make sure shp_file crs is the same*# Filter for points within boundary*
points_in_boundary **=** geopandas**.**tools**.**sjoin(points_gdf, shp_boundary,         how**=**'right')

标准化缺失的数据标签

有时,丢失的数据被编码为“无数据”、“0”、“不适用”或只是一个空字符串。为了便于清理,将所有这些转换成 np.nan.

df **=** df**.**replace(['NO DATA','N/A', 0, ''],np**.**nan)

干净因变量

如果您预测的变量没有值,那么在最终数据集中包含该条目就没有意义。检查最大值和最小值是否在变量的逻辑界限内,以及所有行是否都属于同一数据类型。

删除重复条目

有重复表示数据输入可能有错误。在删除所有重复项之前,请询问:

为什么数据集中有重复项?

答案会改变你去除它们的策略。例如,如果在同一个数据库中根据一个惟一的属性标识符更新一所房子的记录,我们可能会发现最*的条目最相关。这是否意味着我们应该简单地扔掉所有的旧条目?使用旧记录来估算任何缺失的字段有意义吗?

在另一个场景中,我们可能有玩家的游戏记录。最*的条目可能是最新的,但是通过聚合过去的记录我们会得到更丰富的数据吗?游戏的机制使得只有最*或第一个记录重要吗?

不要这么快删除重复的!

检查每个变量的缺失值百分比

percent_missing **=** df**.**isnull()**.**sum() ***** 100 **/** len(df)percent_missing**.**sort_values(ascending**=False**)

如果缺少的值太多,您会希望删除该列。但是分界点是什么呢?

一般来说,丢失超过 50%的数据会使该列看起来毫无用处,但是在删除任何列之前要理解为什么会有这么多数据丢失

该列可能只在某些情况下被填充。例如,如果表单要求提供饮食限制,而您选择了“其他”,表单可能会提示您填写另一个字段来指定限制。在这种情况下,包含有用信息的缺失值的百分比会非常高。该信息可以被清除并与另一列合并以保留数据。

类似地,如果具有互补数据的两列具有高百分比的缺失值,则可以使用它们来精确地估算数据。例如,如果一列显示一所房子是否有太阳能热水,另一列显示太阳能电池板覆盖的屋顶的百分比,我们可以创建一列来显示太阳能电池板的缺失值。

每一列数据都是费了一番功夫才收集起来的,并且显示了一些可能很重要的东西。如果你决定放弃这个专栏,要清楚为什么,并确保你已经想清楚了!

检查每行缺失值的百分比

如果整行没有数据或数据很少,删除它可能比以后用估算数据填充整个条目更好。在删除行之前,一定要问清楚为什么行是空的!

percent_missing_rows = df.isnull().sum(axis=0) / len(df.columns)missing_plt = percent_missing_rows.hist()missing_plt.set_xlabel('Percent missing')missing_plt.set_ylabel('Number of rows')

每行 null 百分比直方图。[图片来自作者]

对于这个数据集,我没有删除任何行,因为我处理的调查数据后来添加了新字段,增加了早期数据中缺失的百分比。

按变量类型清理

我们希望确保每一列都有我们期望的数据。如果有很多列,那么按类型(广义地说):布尔型、日期时间型、数值型(int/float)、分类型和文本型来审计变量会更容易。

df**.**select_dtypes(include**=** 'INSERT_TYPE')**.**columns**.**tolist() 

布尔代数学体系的

将所有布尔列标准化,以 True/False 作为对象,而不是其他形式,如 Y/N 或 Yes/No。

日期时间

将该列转换为 datetime 对象,并在直方图中绘制日期,以确保它在逻辑范围内。

数字的

对于数字数据,绘制箱线图以获得分布的快照,并查看哪些变量具有不合理的最大/最小值,应该进行剪裁。但是在剪辑之前,请确保您理解变量的含义。

num_var **=** df**.**select_dtypes(include**=** 'number')**.**columns**.**tolist()plt**.**figure(figsize**=**(20,10))

**for** i, var **in** enumerate(num_var):
  plt**.**subplot(4,7,1**+**i)
  plt**.**boxplot(df[var]**.**dropna())
  plt**.**xlabel(var,fontsize**=**8, weight**=**'bold')

plt**.**tight_layout()

在这个关于房屋的数据集中,一个房子有 50 个壁炉和超过 60 个加热房间是没有意义的。这表明应该对这些变量进行剪裁。[图片来自作者]

audit_num **=** df[clip_var]**.**describe(percentiles **=** [0.01, 0.99])*# Clips the variables with the upper bound of the 99% confidence 
# interval for variables with many outliers variance (no lower clip)*plt**.**figure(figsize**=**(20,10))

**for** i, var **in** enumerate(clip_var):
  df[var]**.**clip(lower**=-**9999.0, upper**=**audit_num[var]['99%'], inplace**=True**)
  plt**.**subplot(3,4,1**+**i)
  plt**.**boxplot(df[var]**.**dropna())
  plt**.**xlabel(var,fontsize**=**8, weight**=**'bold')

plt**.**tight_layout()

剪切变量后的方框图。[图片来自作者]

绝对的

打印每个分类列的所有唯一变量,并确保这些值是它们应有的值。否则,合并列。如果类别太多,考虑将它们分组以降低复杂性。

文本

有时你会得到文本形式的数据,这是一种标准化的形式,但大多数是不同输入的一大堆东西。尽可能使用正则表达式来标准化条目,然后我们可以使用 CHAID(卡方自动交互检测器)来减少唯一值的数量。

通常,该算法会创建一个决策树。它将独特的反应随机分组到一个节点中,并确定节点中的哪个分裂使我们更接*准确预测结果。它使用的度量是卡方,这是一个统计测试,告诉我们两个分类变量是否独立。我们将获取完成的树中具有唯一响应的最佳分组的节点,并使用它们来减少列中唯一值的数量。

你可以在这里阅读关于方法的更深入的内容。下面的原码来自这里

**from** CHAID **import** Treechaid_dict **=** {}
**for** var **in** cat_var:
    *#Set the inputs and outputs*
    *#The imputs are given as a dictionary along with the type*
    *#The output must be of string type*
    *#I have assume all features are nominal, we can change the features dictionary to include the ordinal type*
    features **=** {var:'nominal'}
    label **=** 'VARIABLE_TO_PREDICT'
    *#Create the Tree*
    chaid_dict[var] **=** {}
    tree **=** Tree**.**from_pandas_df(df, i_variables **=** features, d_variable **=** label, alpha_merge **=** 0.0)
    *#Loop through all the nodes and enter into a dictionary*
    print('\n\n\nVariable: %s' **%** var)
    print('p-value: %f' **%** tree**.**tree_store[0]**.**split**.**p)
    print('Chi2: %f' **%** tree**.**tree_store[0]**.**split**.**score)
    **for** i **in** range(1, len(tree**.**tree_store)):
        count **=** tree**.**tree_store[i]**.**members[0] **+** tree**.**tree_store[i]**.**members[1]
        **if** count **!=** 0:
          rate **=** tree**.**tree_store[i]**.**members[1] **/** count
          print('\nNode %i:\n\tCount = %i\tRate = %f' **%** (i,count,rate))
          print('\t%s' **%** tree**.**tree_store[i]**.**choices)
        chaid_dict[var]['node' **+** str(i)] **=** tree**.**tree_store[i]**.**choices

作为一种启发,您可以按原样进行分组,但值得将其作为一个起点,看看如何对唯一的条目进行分组。

估算缺失数据

如果有丢失的数据,你就不能运行一个模型,所以这里有一些我们可以填充丢失数据的方法。

一种简单的方法是根据列中的其他值来填充缺失的数据。如果该列有倾斜的数据,则取中值(数值)或众数(非数值),以便从大多数数据中提取数据,而不会最终改变分布。如果该列有未分块的数据,基于同样的原因取*均值!

另一种方法叫做迭代插补,依次使用每个特征的数据来填充缺失数据。我们预测缺失值百分比最低的要素中的缺失值,就像我们在解决回归问题一样。然后,它使用清理后的要素来预测缺失值百分比次低的要素,直到所有要素都被估算完毕。见此处为教程。

对于分类数据或文本数据,您也可以通过将 np.nan 替换为‘MISSING’来将缺失数据视为一个数据类别。也许数据丢失的事实本身就具有预测性。

编码非数字数据

计算只对数字起作用,所以我们需要通过编码将文本转换成数字。

如果你有顺序数据,你可以使用标签编码将一个按字母排序的类别转换成一个数字序列。['A ',' B ',' C'] = [1,2,3]。

当顺序与数字的递增值相对应时,效果最佳,例如:['short ',' average ',' tall'] = [1,2,3]。但是当应用于像:['苹果','梨','香蕉']=[1,2,3]这样的无序数据时,模型得到的感觉是香蕉>梨>苹果,这是不正确的!

**from** sklearn.preprocessing **import** LabelEncoderlabel_encoder **=** LabelEncoder()
df[var] **=** label_encoder**.**fit_transform(df[var])

相反,我们使用一键编码,将数字转换成向量。所以数据像:['苹果','梨','香蕉'] = [[0,0,1],[0,1,0],[1,0,0]]。但是,在基数较高的地方(很多唯一值),这些向量会变得非常大,占用大量内存,所以要小心。

*# One hot encode non-ordinal variable* **from** sklearn.preprocessing **import** OneHotEncoder  
enc **=** OneHotEncoder(handle_unknown**=**'ignore')
df[var] **=** enc**.**fit_transform(np**.**array(df[var])**.**reshape(**-**1,1))**.**toarray()

准备建模!

这绝不是一个关于清理数据的详尽指南。根据您的数据集,您可以执行更多的步骤,但我发现这是一个至少对开始有帮助的管道。请让我知道,如果有其他步骤,我应该添加和其他功能,可能有助于使这个过程更容易。

至少现在,去运行一些模型吧!

测量特征重要性的 Sobol 指数

原文:https://towardsdatascience.com/sobol-indices-to-measure-feature-importance-54cedc3281bc

理解模型的输出在业务驱动的项目中起着重要的作用,Sobol 可以提供帮助

马丁·桑切斯在 Unsplash 上的照片

简介

每个数据项目自然包括一个初始阶段的预处理来获得:

  • 适当格式的数据,允许项目的下一阶段正常运行。
  • 高质量数据确保最佳性能。

这第二点包括数据科学家的两个众所周知的步骤:特征选择特征工程

  • 特征选择丢弃相对“不重要”的变量同时最小化信息损失的过程。
  • 特征工程是从现有变量中创建新变量的过程,这些变量在直觉上或“统计上”很重要。通常,变量的变换交互

然而,要执行这些步骤,一个关键问题出现了:如何衡量一个变量的重要性?

在本文中,我将介绍一个鲜为人知的方法来回答这个问题:Sobol 指数

衡量重要性

目标是排列每个输入对输出的影响。论文【2】提出变量的重要性通过两个因素依赖于其分布:

  • 其权重:其分布可以增加或减少其重要性。比方说,我们正在研究性别和其他因素在泰坦尼克号死亡概率中的重要性:在女性群体中,我们预计性别占主导地位,而对于男性,另一个因素可能更重要。
  • 相互作用:一个变量的分布会受到另一个变量分布变化的影响

通常有三种方法来衡量变量的重要性:

  • 通过线性回归*似模型,并通过 p 值分析权重
  • 执行更多的高级统计测试 (chi2,t student,fisher,ANOVA …)
  • 功能分解

Sobol 指数尤其基于后者——应用于方差函数分解

Sobol 指数

直觉

一个输入变量 X_i 的重要性由其负责的 Y 的方差的部分来衡量,即如果我们固定 X_i ,我们看(Y 的)方差减少了多少。如果显著下降,那么变量 X_i 测量的是 Y 的大部分方差,因此 X_i 是一个重要变量。因此,Y 的不确定性归因于X _ I的不确定性,因为它主要代表其方差。****

一阶

Sobol 指数有不同的顺序,反映了相互影响的变量数量。因此,一阶量化了由于唯一变量 X_i 导致的 Y 中的方差份额。如果有 p 变量,就会有 p 一阶指数。数学上,关系如下:

一阶 Sobol 指数

期望值 𝔼(Y|X_i)Y 的*均值,其中只有 X_i 的值是有条件的(固定)。

除以总方差 V(Y) 简化了结果的解释:指数越接* 1,变量(如果顺序为 1)或变量组(顺序为> 1)越重要。

k 阶的索引使得 k 个变量相互作用 X_i1X_i2 ,…,以及 X_ik 通过以与第一阶相同的方式固定它们的值。下一部分的目的是尽可能简单地描述到达广义公式(针对每一个订单)的路径。

通式

起点是 ANOVA 的功能分解。将其他假设(例如正交性)添加到这个定理中,Sobol 证明了这个分解是唯一的。他将定理中的方差积分,得到如下等式:

Sobol 的泛函分解是基于 ANOVA 的分解(在某些假设下)

最后,阶 k 的 Sobol 指数的正式定义是:

所有订单的 Sobol 索引(1 到 p)

这种方法的缺点是它的算法成本。的确,有“p 选 1”的一阶指数,有“p 选 2”的二阶指数或 p*(p-1)/2 指数,…,也只有一个 p 阶指数,综上,有 2^p — 1 指数。这种指数级的复杂性使得在高维空间中研究灵敏度变得困难。

幸运的是, Homma 和 Saltelli 已经找到了解决这个问题的方法:计算变量的总索引。该指数汇总了感兴趣的变量出现的所有指数。例如,对于 3 个变量,S_T1 = S_1 + S_12 + S_13 + S_123。

他们建立了以下关系:

总指数

【v(𝔼(y|x~i】)这一项是 X~i 的一阶效应,即这一项是 y 条件作用对所有变量的期望方差,除了x _ I所考虑的变量。

如果我们用一阶指数减去总数,我们就可以得到特定变量的指数之和大于 1 。当然,我们没有每一阶的精确值,但是这允许我们对更高阶的一组值有所了解。

在实践中

现实中,直接计算是不可能的。因此,我们使用估计器,特别是蒙特卡罗方法

后者基于来自原始数据集的 N 个示例的两个样本,从而使用了两个不同的矩阵,如下所示:

对于 p 变量的 N 示例的数据的 2 个样本

如果 i1、… ik 是我们感兴趣的指数的 k 变量,我们定义一个三阶矩阵等于 B ,但是用的值来表示 k 变量:

矩阵 B 中 k 个变量的值来自 A

3 个输出写如下:

输出符号

对于一阶,Sobol 的方法在于重写等式,如下所示:

Sobol 方法下的一阶指数

因此,可以估计每个量,以获得第 k 个变量的一阶指数:

第 k 个变量的一阶估计量(Y_i 是向量列 Y 的第 I 个元素)

以同样的方式,总索引被重写:

计算第 k 个变量总指数的 Sobol 方法

这导致以下估计量:

变量 k 总效应的估计量

根据定义, S_Tk 大于 S_k ,因为它包含了输入变量 X_k 的主要效应和所有交互作用

现在,让我们把这个方法应用到一个玩具的例子中!

应用

我将使用一个 数据集 ,根据一些天气变量(温度、降雨量(毫米)、能见度……)以及下表所示的其他变量,给出首尔每小时租赁的自行车数量

使用的原始数据集

预处理阶段将被省略,因为主题是索引。但是,必须提到一个步骤:需要在 0 和 1 之间标准化数据。事实上,ANOVA 函数分解依赖于一个强假设:函数的定义域必须在 0 和 1 之间,才能有一个唯一的分解(必要条件)。

代码可分为 3 块。

第一区

清理数据后,我实现了 3 个函数,分别创建矩阵 A、矩阵 B 和矩阵 AB。代码如下:

为了简单起见,为了生成两个数据样本,我应用了最简单的样本替换技术。(可选择其他样品技术)

第二区

然后,我创建了一个主函数,它将为一个特定的变量计算它的一阶效应和总效应,这样:

计算变量的 Sobol 索引(第一个和全部)的函数(在参数中称为“varaible_index”)

第三区

最后,我展示了一个简单的函数来绘制一个堆叠条形图,以方便地表示每个变量的第一阶效应和总:

plot 函数得到下图(X 是训练数据)

结果

将上述 3 个模块的应用结果应用于之前显示的数据集

经过几次测试和不同的数据后,我注意到对于 1000 个例子来说,估计量是无效的。从 5000 开始,我开始得到如上的不错的成绩。

Sobol 指数从的另一个角度提供结果:

  • 温度在每小时租赁自行车的数量中起着主导作用,因为几乎 80%的 Y 的变化似乎是由温度估计的。
  • 对于除温度之外的所有变量,由于相互作用 产生的 Sobol 指数在一阶上占优势。也就是说,单独来看,这些变量对输出没有主要贡献,但是它们与其他变量的相互作用使得变量更加重要
  • 值得注意的是,在没有提及标准偏差的情况下,数值分析仍然统计性较差。比如我们看到变量“露点温度”有一个负一阶,理论上不可能。如果我们有标准偏差和 p 值,我们可能会看到这个变量对模型来说可能不重要

结论

在一个项目中,这种方法可以用来支持其他更传统的测量变量重要性的方法(线性回归,统计测试)。此外,对于非科学观众来说,这个解释可能很难。然而,它通过估计器和统计效率进行计算的简单性可能会吸引那些想用另一种方法来衡量独立变量重要性的科学家。

注:每一个手工人物都是我亲手制作的。

来源

使用 Python 和 VADER 进行社交媒体情感分析——无需培训!

原文:https://towardsdatascience.com/social-media-sentiment-analysis-in-python-with-vader-no-training-required-4bc6a21e87b8

词典和基于规则的情感分析库。

照片由你好我是尼克Unsplash

介绍

建立一个模型可能比你想象的要容易得多。不是每个分类任务都需要机器学习模型。即使非常简单的方法也能给你带来好的表现。本文涵盖了**VADER**,这是一个基于词典和规则的情感分析模型。我们将首先了解什么是 VADER,最后评估它在分类任务中的性能。

什么是 VADER?

价感知词典和情感推理机或简称 VADER 是一个词典和简单的基于规则的情感分析模型。

它可以有效地处理词汇、缩写、大写、重复标点符号、表情符号(😢,😃,😭等。),等等。通常在社交媒体*台上采用来表达个人情感,这使得它非常适合社交媒体情感文本分析。

VADER 的优势在于评估任何给定文本的情感,而不需要之前的训练,因为我们可能需要机器学习模型。

VADER 生成的结果是一个由 4 个关键字 negneuposcompound 组成的字典:

negneu、pos 分别表示阴性、中性和阳性。它们的和应该等于 1,或者用浮点运算接* 1。

复合词对应于词典中每个单词的价分值之和,并且确定情感的程度,而不是与之前的值相反的实际值。其值介于-1(最极端的负面情绪)和+1(最极端的正面情绪)之间。使用复合分数足以确定文本的潜在情感,因为:

  • 一个正向情绪,复合≥ 0.05
  • 一个情绪,复合≤ -0.05
  • 一个中性情绪,复合在-0.05,0.05[ 之间

现在我们已经理解了主要概念,让我们深入到实现中。

如何使用 VADER?

本节的目标是为您提供所有的先决条件,如依赖性、数据集和 VADER 的实际实现。

如果您喜欢视频,可以观看本文的视频演示:

先决条件和基础知识

正如标题中提到的,我们将使用 VADER 库,为此我们需要安装[nltk](https://www.nltk.org/_modules/nltk/sentiment/vader.html) 下载,并按照以下说明导入词典。

vader_basics.py

SentimentIntensityAnalyzer.polarity_score()功能提供文本的极性,呈现之前解释的字典格式。为了能够执行预测,我们需要预先创建一个SentimentIntensityAnalyzer(行** 12 )的实例。**

让我们通过预测以下例子的潜在情绪来热身。

示例 1.py

# Output of example1
{'neg': 0.0, 'neu': 0.585, 'pos': 0.415, 'compound': 0.75}

观察例 1 :上一个结果显示该句没有任何负面信息(neg=0)。它有一些中性和积极的音调(neu=0.585 和 pos=0.415)。不过,总体情绪是积极的,因为复利> 0.05

示例 2.py

# Output of example2
{'neg': 0.0, 'neu': 0.373, 'pos': 0.627, 'compound': 0.8284}

观察例 2: 从这个例子可以看出,复合词跳到了 0.82,这使得句子比第一个例子的句子更加肯定。

# Output of example3
{'neg': 0.619, 'neu': 0.381, 'pos': 0.0, 'compound': -0.8449}

观察例 3: 从这最后一句话,我们可以看出,该句没有任何正面信息(pos=0)。它有一些中性和消极的音调(neu=0.424 和 neg=0.576)。总的来说,由于复合得分接*-1,它具有最极端的负面情绪。我的猜测是,去掉感叹词会让情绪不那么消极。为什么不试试呢:)

大型数据集上的性能

既然我们了解了基础知识,让我们尝试评估 VADER 在大数据上的性能。在此之前,我们将需要执行一些预处理。

加载数据进行预处理

我们将使用感知 140 网站上的这个 免许可 推特数据集,以便了解 VADER 做得有多好。

vader_read_data.py

数据集的前 3 行(图片由作者提供)

我们只对两个主要栏目感兴趣。

  • ****‘4’,对应推文极性(0:负极,2:中性,4:正极)。
  • “@stellargi”..右',对应实际推文。

下面的函数将这些列重命名为更容易理解的格式,然后在数字和极性的字符串格式之间建立对应关系,最后返回格式化的数据。

格式 _ 数据. py

下图对应于对原始数据集应用format_data()函数后的前 3 行(行** 1617 )。**

对原始数据应用 format_data 函数后的前 3 行(图片由作者提供)

VADER 的数据有多好?

在此之前,我们将使用以下助手函数,这些函数将立即返回极性( posnegneu ),而不是字典输出。

维德 _ 预测 _ 助手. py

行** 19 上,我们创建一个新列vader_prediction()对应 VADER 的预测。然后,在第 22 行我们显示了 5 行随机数据**

包含原始标签和 VADER 预测的 5 行随机数据(图片由作者提供)

从最初的极性栏和 VADER 的预测,我们可以最终生成运行这几条指令的性能(精度、召回和 f1 分数)。

维德 _performance.py

VADER 对数据的准确性和分类报告(图片由作者提供)

该模型似乎做得很好,因为它比随机猜测(精确度= 0.5)好得多!从每个极性的f1-分数中可以得出相同的观察结果。

结论

恭喜你!🎉 🍾您刚刚学习了如何使用 VADER 进行社交媒体情感分类。在进一步构建机器学习模型之前,VADER 可以成为一个很好的起点,并可以用作此类任务的基线模型。我希望您喜欢阅读这篇文章,并且它给了您执行分析所需的技能。请在下面找到更多资源来帮助您进一步学习。

欢迎在 LinkedIn 上添加我,或者在 Twitter 上关注我。讨论人工智能,人工智能,数据科学,自然语言处理的东西总是令人愉快的!

Google Colab 上的源代码

Github 上的 VADER 文档

NLTK 文档

再见🏃🏾

图和网络中的社会网络分析和谱聚类

原文:https://towardsdatascience.com/social-network-analysis-and-spectral-clustering-in-graphs-and-networks-40c8d878e946

图的中心性度量和划分技术简介

图片来源——由作者使用 Jupyter 笔记本准备。

1.介绍

1.1 图和网络的基础

网络被定义为由线连接的点组成的图。这些点是节点、顶点,甚至是角色,而连接被称为边。例如,当考虑大学中的学生的社交网络时,每个学生代表一个节点,并且任何一对彼此熟悉的学生表示他们之间的边(友谊)。假设我们将节点定义为 V(与顶点同义)为{V1,V2,V3,…,Vn},那么图的大小为|V|=n,即图中节点的数量。类似地,边被定义为给定节点对之间的关系。考虑将边定义为 E = {E1,E2,E3,…..,Em},那么边的数量表示为|E|=m。

图一。有向图的例子。网络或图中的边可以具有方向,例如,w.w.w(万维网)是有向图。边通常使用端点来表示,并且通常被定义为弧。在无向图中,这些定义方向的箭头通常是缺失的——这是作者准备的图像。

表示一个图很简单,但是我们需要处理某些组件。下面的网络是无向图的一个例子。对于图中的任何节点 V,例如 Sharanya,它通过边连接到的节点集被称为它的邻居,并被表示为 N(v)。例如 N(Sharanya) = {Tabish,Angel,Panini}。类似地,连接到任何节点的边的数量被称为该节点的度(在下图中表示为每个节点顶部的数量)。在下面的例子中,Sharanya 连接到三个节点,因此度为 3。

图二。说明了一个无向图。每个节点代表一个个体,而节点之间的连接则代表程度——作者准备的图像。

1.2 学位与分布

在剩下的文章中,我们将考虑图 2 所示的无向图的例子。在无向图中,度数的总和是图中边数的两倍。

例如,图 2 中的总边数是 6。如果我们对每个节点的度数求和(1+3+2+4+1+1) = 12,则定理本身成立。

在处理可拓图时,节点的度分布是一个需要分析的关键概念,被定义为度分布。例如,让我们考虑 R(d)作为上图中可能的度数序列。R(d) ={1,2,3,4}。我们将分数定义为图中节点总数上度数为“d”的节点数。这可以用来分析幂律度分布。许多用户在像脸书这样的社交网站上朋友较少,然而也有一小部分用户拥有大量的朋友。

计算:P1=3/6 = 50%(图中 3 个节点的度数为 1,6 个节点)。P2 = 1/6 = 17% (1 个度为 2 的节点,参考上图中的 Tabish,共 6 个节点)。

1.3 邻接矩阵

捕捉图中节点之间连接的矩阵表示是邻接矩阵。

Ai,j = 1,如果在节点 Vi 和 Vj 之间有边,否则我们将其标记为 0。这是表示社会网络、学术研究中的引用网络等的常用方法。该图的邻接矩阵如下所示。

图三。说明了一个无向图。每个节点代表一个个体,而节点之间的连接则代表程度——作者准备的图像。

图四。显示了上图的邻接矩阵以及每个节点的度数。图片由作者准备。

2.节点的中心性度量

中心性允许我们计算数据中每个节点的重要性。假设在墨尔本有一场澳大利亚和韩国之间的足球世界杯预选赛,组织者想向上面网络中最有影响力的人分发一些免费门票。量化中心性的一种方式是通过使用邻接矩阵计算每个节点的度,即给定行中所有 1 的总和。在上面的例子中,帕尼尼拥有最高的度,值为 4,表明他在网络中有多个连接。然而,这可能并不总是正确的方法。想象帕尼尼有多个朋友,但他的朋友只有更少的朋友。为了避免这样的问题,我们可以使用特征向量来计算中心性。

特征向量给每个节点一个与其所有邻居分数之和成比例的分数。给定一个邻接矩阵 A,V 是 A 的一个特征向量,如果乘以 A 并不改变 V 所指的方向,它只是将它缩放一个因子λ,即 A×V =λ×V

2.1 Zachary 的空手道俱乐部网络数据用 Python 中的 NetworkX

在这个例子中,我们将使用 Zachary 的空手道俱乐部网络数据来学习一些图形的基本概念。我们将研究图的节点、边、度、视觉表现等。我们还将学习如何使用谱聚类算法来执行图聚类。

# !pip install networkx%matplotlib inlineimport networkx as nx
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsimport warnings
warnings.filterwarnings(‘ignore’) # — — — — — — to get rid of warning messages# — — — — — — — Remove scientific notations and display numbers with 2 decimal points instead — — — — — — — pd.options.display.float_format = ‘{:,.2f}’.format# — — — — — — — Update default background style of plots — — — — — — — 
sns.set_style(style=’darkgrid’)

L 加载数据。请注意,数据是作为 networkx 库的一部分出现的。

# — — — — — — — — — — — -Reading Data — — — — — — — — — — — kn=nx.karate_club_graph()# — — — — — — — — — — — -The example below is a network from Zachry’s Karate club — — — — — — — — — — — # — — — — — — — — — — — -Display Number of Nodes — — — — — — — — — — — num_nodes = kn.number_of_nodes()print(‘number of nodes: ‘ + str(num_nodes))print(“-”*50)# — — — — — — — — — — — -Display Number of Edges — — — — — — — — — — — num_edges = kn.number_of_edges()print(‘number of edges: ‘ + str(num_edges))

输出 1。说明了网络中节点和边的数量。图片由作者使用 Jupyter 笔记本制作。

with sns.axes_style(‘darkgrid’):

 fig = plt.subplots(1, figsize=(12,10))

 nx.draw_networkx(kn, edge_color=’#a7a7a5', with_labels=True, font_color=’white’)

 plt.axis(‘off’)

输出 2。说明了数据的网络表示。图片由作者使用 Jupyter 笔记本制作。

2.2 度中心性

节点 v 的度中心性是它所连接的节点的分数。度中心性值通过除以简单图 n-1 中的最大可能度来归一化,其中 n 是 g 中的节点数。

一级方程式赛车。举例说明度中心性的计算。图片由作者使用 Jupyter 笔记本和 Latex 制作。

# — — — — — — — — — let us store the degree centralities for each nodes for a graph in a dictionarydeg_cen = {}graphs=[kn]for g in graphs:

 deg_cen[g] = nx.degree_centrality(g)print(deg_cen)

我们可以使用上面定义的公式进行计算。我们选择图中的第 4 个节点进行验证。

num_nodes = graphs[0].number_of_nodes()print(‘number of nodes: ‘ + str(num_nodes))list_nodes=list(graphs[0].nodes)print(list_nodes)num_neighbors = graphs[0].degree(list_nodes[4])print(‘degree of node ‘, list_nodes[4], ‘is ‘, num_neighbors )print(“Degree of Centrality:”, round(num_neighbors/(num_nodes-1),3))

2.3 中间性

该度量确定网络中的哪些节点充当其他节点之间的“桥梁”。这是通过首先识别所有最短路径,然后计算每个节点落在其中一条路径上的次数来实现的。

配方二。说明了之间中心性的计算。图片由作者使用 Jupyter 笔记本和 Latex 制作。

# Betweenness Centralitybetw_cen = {}
for g in graphs:
 betw_cen[g] = nx.betweenness_centrality(g)betw_cen

理解一个节点的最短路径序列。

paths= nx.all_pairs_shortest_path(graphs[0])path_5_1=nx.all_shortest_paths(graphs[0],5,1)for p in paths:

 if p[0]==5:

 print(p)
 print(“-”*50)

for p in path_5_1:

 print(p)

2.4 紧密中心性

图中任何节点(比如 v)的接*中心性可以通过在所有 n-1 个可到达的节点上找到到 v 的*均最短路径距离,然后取它的倒数来计算。

配方三。说明了亲密度中心性的计算。图片由作者使用 Jupyter 笔记本和 Latex 制作。

# Closeness Centralityclo_cen = {}for g in graphs:
 clo_cen[g] = nx.closeness_centrality(g)

clo_cen

我们可以使用上面定义的公式进行计算。我们选择图中的第 5 个节点进行验证。

s=0print(len(list_nodes))for node in list_nodes:

 if node!=5:

 shortest_path_length=nx.shortest_path_length(graphs[0],5,node)

 s+=shortest_path_length

print((len(list_nodes)-1)/s)

2.5 特征向量中心性

特征向量中心性使用其邻居的中心性来计算节点的中心性。对于节点 I,特征向量中心性是 A’X X =λX X,其中 A’被定义为具有λ特征值的图 G 的邻接矩阵。

# Eigen Vector Centralityeig_cen = {}for g in graphs:

 eig_cen[g] = nx.eigenvector_centrality(g)eig_cen

图 5。说明了现实世界问题中不同中心性测量的用例(迪士尼,2019)。图片由作者用 Excel 制作。

2.6 可视化图中的重要节点

# let us now sort the degree centrality measure and identify the important nodes.for ix, g in enumerate(graphs):

 temp_dict = {}

 for w in sorted(deg_cen[g], key=deg_cen[g].get, reverse=True):

 temp_dict[w] = deg_cen[g][w]

 print(“Sorted Importance of nodes in terms of deg_cen for Graph {} is {}”.format(ix+1, list(temp_dict.keys())[:5]))

 print()

对于图 1,根据 deg_cen,节点的排序重要性是[33,0,32,2,1]。

graph2 = kn
color = []fig = plt.subplots(1, figsize=(12,10))for node in graph2:
 if (node == 33 or node == 0 or node == 32 or node==2 or node==1):
 color.append(‘red’)
 else:
 color.append(‘blue’)

nx.draw_spring(graph2,node_color=color,with_labels=True)

输出 3。用重要节点说明数据的网络表示。图片由作者使用 Jupyter 笔记本制作。

3.谱聚类

谱聚类算法用于根据图的连通性将图分成 K 个组。谱聚类中涉及的步骤包括:

  1. 预处理:构造图的拉普拉斯矩阵。拉普拉斯矩阵是邻接矩阵和对角矩阵之间的差异。L = D — A。
  2. 分解——计算拉普拉斯矩阵的特征值和特征向量,然后我们需要将每个点映射到一个更低维的表示。
  3. k 表示算法—使用上述数据创建聚类组。

3.1 预处理

G_p1=graphs[0]A=nx.adjacency_matrix(G_p1)print(“ — — — — — — — — — -ADJACENCY MATRIX — — — — — — — — — — — -”)print(A.todense())print(“-”*50)print(“ — — — — — — — — — -DIAGONAL MATRIX = DEGREE OF EACH NODE — — — — — — — — — — — -”)print(G_p1.degree)L = nx.laplacian_matrix(G_p1).astype(float)print(“-”*50)print(“ — — — — — — — — — -LAPLACIAN MATRIX — — — — — — — — — — — -”)print(L.todense())

输出 4。说明了网络的拉普拉斯矩阵、邻接矩阵和对角矩阵。图片由作者使用 Jupyter 笔记本制作。

3.2 分解

import scipy as spw,v = sp.sparse.linalg.eigsh(L, k = 3, which=’SM’)print(w)
print(v)

3.3 K 表示聚类

请注意,我们已经创建了 3 个集群作为假设场景。然而,在现实世界的应用中,使用轮廓分数、间隙统计,甚至肘图来验证 K 的良好值是很重要的。对于图划分,更常见的度量是 F-Measure、Precision、Recall、Purity、Mutual Information 和 Normalized Mutual Information。

from sklearn.cluster import KMeansX = v*wkmeans = KMeans(init=’k-means++’, n_clusters=3, n_init=10)
kmeans.fit_predict(X)
centroids = kmeans.cluster_centers_
labels = kmeans.labels_
error = kmeans.inertia_fig = plt.subplots(1, figsize=(12,10))colors = ['#d7191c', '#ffffbf', '#2b83ba']
node_colors = [ colors[labels[v]] for v in G_p1.nodes()]
nx.draw(G_p1, node_color=node_colors, with_labels='True')

输出 5。用三个集群说明了数据的网络表示。图片由作者使用 Jupyter 笔记本制作。

4.参考

  1. 布兰德斯大学(2001 年)。中间中心性的快速算法。《数理社会学杂志》25 (2),163–177 页。https://doi.org/10.1080/0022250x.2001.9990249
  2. 在线创建图形并寻找最短路径或使用其他算法。(未注明)。https://graphonline.ru/en/
  3. 迪士尼,A. (2019 年 7 月 26 日)。社交网络分析:中心性测量。剑桥情报局。https://Cambridge-intelligence . com/keylines-FAQ-social-network-analysis/
  4. NetworkX。(2022).between ness _ centrality—NetworkX 2 . 7 . 1 文档。Networkx.org。https://networkx . org/documentation/stable/reference/algorithms/generated/networkx . algorithms . between ness _ centrality . html # networkx . algorithms . centrality . between ness _ centrality

关于作者:高级分析专家和管理顾问,通过对组织数据的商业、技术和数学的组合,帮助公司找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;可以和我在 上联系 推特

软件即服务:小型 IT 部门的游戏规则改变者

原文:https://towardsdatascience.com/software-as-a-service-the-game-changer-for-small-it-departments-f841b292b02a

软件即服务简介,与内部解决方案进行比较

Unsplash 上由 Austin Distel 拍摄的照片

软件即服务(SaaS)是云计算领域中的一个类别,其中软件可以在没有安装在本地计算机上的情况下使用。最终用户通过网站访问程序,因此只使用软件的功能。另一方面,硬件和 IT 基础设施的供应由提供商处理。

软件即服务是如何工作的?

软件即服务使用云环境向客户提供服务。根据应用程序的不同,软件提供商使用自己的服务器或云服务提供商来托管应用程序,存储数据并更新系统。

另一方面,最终客户只需要互联网连接,并通过网络浏览器使用应用程序。在大多数情况下,他与提供商签订了一份订阅协议,授权他在有限的时间内使用软件。

然后,提供商还面临着调整应用程序的任务,以使其尽可能*稳地为最终客户服务。在大量使用的情况下,必须相应地预订新的云资源,或者必须增加本地服务器。

内部部署和 SaaS 有什么区别?

随着云计算的普及,软件即服务变得更加流行。在此之前,公司购买他们使用一次的软件,然后在他们的本地设备上安装和维护它是很正常的。

对于公司来说,当他们必须决定是在云中还是在本地使用软件时,以下几点至关重要:

设置和维护

对于内部软件,内部 IT 部门需要负责安装、维护、保护,最后是硬件的扩展,以防更多用户开始使用它。这些任务可能无法由一个小团队来完成,这使得中型公司尤其困难。

对于软件即服务,您只需注册应用程序并选择订阅类型。在大多数情况下,甚至有机会在签署订阅之前免费试用。因此,设置和维护甚至可以由非 IT 人员来完成。

费用结构

在大多数情况下,与内部解决方案相比,SaaS 是更具成本效益的替代方案。费用是非常透明的,因为您只需支付固定的订阅费,该费用可能会随着用户的增加而变化。对于内部部署,通常对软件的一次性投资非常高,而经常性的维护成本较低。因此,从长远来看,内部部署可能会成为更便宜的选择,但前提是经常性成本保持在较低水*。

对信息技术人员的需求

SaaS 解决方案的主要优势是对训练有素的 IT 人员的需求低,因为大多数软件使用无代码前端,这使得即使没有技术背景的员工也很容易操作。这样,用户只需要互联网接入和一个打开的浏览器就可以使用该应用程序。对于内部部署,IT 人员需要随时待命,以便在出现中断或错误时做出反应。这意味着他们不能用于其他项目,整个部门需要发展。

集成其他应用程序

对于大多数 SaaS 应用程序,有一堆其他软件可以轻松集成。在某些情况下,订阅需要更改为更高级的订阅,但集成速度非常快。然而,如果所需的软件不在给定的堆栈中,可能需要很长时间,或者甚至不可能让制造商添加它。

在内部部署的情况下,新应用程序的集成通常非常复杂,可能需要大量的时间和资源。然而,有可能添加新软件,只要它在技术上是兼容的,并且不依赖于提供商的堆栈。

可量测性

由于软件即服务的透明成本结构,它还具有完美的可扩展性。新用户只会导致额外的订阅,这意味着更高的成本。但不需要改进硬件等。因为供应商会处理这些问题。

在内部部署的情况下,可伸缩性要难得多,因为通常有固定成本。在设置时,高成本正在发生,对于最初的几个用户来说可能不会真正改变。然而,可能存在一个用户阈值,此时需要进行另一次硬件改进来维持性能。再者,由于硬件昂贵,投资相当高。

哪些应用程序已经作为软件即服务提供?

由于软件即服务的诸多优势及其受客户欢迎的程度,现在几乎所有的新应用程序都以 SaaS 的形式提供。此外,多年来在内部提供的软件产品也正在被转换。这方面最著名的例子是微软 Office。

以下软件产品是 SaaS:

  • 销售力量
  • 微软 365
  • 网飞
  • 一款云视频会议软件
  • 松弛的
  • 特雷罗

软件即服务的优势是什么?

如前所述,使用软件即服务是有益的,因为它对企业来说更具成本效益。详细来说,还有以下优点:

  • 安装和维护的成本和工作量低
  • 快速部署,不损失安装时间
  • 可量测性
  • 自动无故障更新
  • 轻松扩展其他服务
  • 按用户付费,从而实现最大的成本透明度

使用 SaaS 有什么问题?

当使用软件即服务时,使用外部服务自然也会带来风险。特别是对于公司来说,必须仔细权衡与内部解决方案相比的优势和劣势。

在将软件作为服务使用之前,必须始终详细检查数据保护情况。根据软件的应用,敏感数据有时会离开公司。因此,必须确保信息也安全存储,并保证数据安全。这有时可能是一个耗时且昂贵的过程。

在软件运行过程中,服务的可访问性和性能会出现风险。SaaS 提供商负责确保软件始终可用,很少停机,并且及时进行更新。如果不是这样,采购公司可能会经历超出他们控制的昂贵的停机时间。

因此,应该仔细检查服务级别协议,并在签订合同之前重新协商。这些规则规定了 SaaS 提供商在出现故障时的行为方式,以及服务必须在多长时间内恢复运行。如果提供商无法满足这一服务水*,客户可能有权获得补偿,这取决于协议。

无论是内部解决方案还是 SaaS,提供商的后续变更都需要付出巨大的努力。累积的数据量必须迁移到新系统,员工可能需要重新培训。因此,软件和提供商的选择应该是经过深思熟虑的。

PaaS、IaaS 和 SaaS 有何不同?

除了软件即服务,其他服务也在“X 即服务”领域得到了发展。通常,在这些产品中,提供商专注于管理,客户不再需要承担很少甚至任何任务。

与此相反的是所谓的现场或内部软件。在这里,操作、数据、服务器以及更多的责任由最终使用系统的组织承担。尽管这种架构涉及大量工作和责任,但一些公司仍然依赖这种方法,因为它确保敏感数据不会离开公司。

借助基础设施即服务(IaaS),服务器、数据存储和网络的管理由云中的外部提供商负责。另一方面,客户通过接口访问和使用租用的基础设施。但是,用户仍然负责系统的其余部分,例如应用程序、数据或操作系统。这也意味着用户对可能的故障或维修承担全部责任。

下一个阶段是*台即服务(PaaS),除了基础设施之外,软件*台也在提供商的云中提供。例如,当应用程序要被编程时,使用该选项。它相当于由提供商提供的虚拟机。用户仍然可以控制已安装的程序,但所有其他责任都由提供商承担。

这种结构的最后一个阶段是软件即服务(SaaS),其中所有之前的阶段和软件本身都由云中的提供商运营。

为什么 SaaS 对数据科学家很重要?

在日常工作中,许多数据科学家已经开始使用软件即服务产品。这部分是因为一些程序只能作为 SaaS 使用,部分是因为根据用例它更有意义。

许多数据科学家使用的 SaaS 产品之一是谷歌分析。web 界面提供了直接评估 web 跟踪数据、显示数据并从中得出可能结论的可能性。谷歌分析也可以用作 SaaS 工具。即使你想自己保存数据,这也不会那么容易,因为从 Google Analytics 导出数据实际上是不可能的。只有现有的报告可以下载为 pdf,或者基础数据可以导出为 CSV。因此,在这种情况下,没有软件即服务产品甚至是不可能的。

然而,也有足够多的例子表明,从内部解决方案转向 SaaS 解决方案确实是有意义的。例如,在机器学习领域,已经出现了机器学习即服务的领域,其中购买了底层架构。

目前很多机器学习模型,比如变形金刚,都需要快速训练图映射。在传统的 CPU 上,训练通常需要多倍的时间。然而,使用显卡构建基础架构不仅更复杂,而且成本也很高。因此,求助于外部资源是值得的,这些外部资源的环境是专门为机器学习和数据科学设计的,因此能够 100%地专注于项目。

这是你应该带走的东西

  • 软件即服务(SaaS)是云计算领域中的一个类别,其中软件可以在没有安装在本地计算机上的情况下使用。
  • 对于用户来说,这尤其带来了低成本的优势,以及安装和维护费用的降低。
  • 除了 SaaS,还有基础设施即服务(IaaS)和*台即服务(PaaS)作为使用云的替代选项。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

软件开发几乎 100%与沟通有关

原文:https://towardsdatascience.com/software-development-is-almost-100-about-communication-1bb9bc60810f

…我们应该正确地生产高质量的产品

Unsplash 上由 Austin Distel 拍摄的照片

沟通通常不会与编程怪胎联系在一起,请原谅……软件工程师。有些人可能会认为开发人员是独自呆在地下室的害羞的书呆子,他们更喜欢自己的计算机而不是其他人的陪伴。我没见过多少这样的人。

事实上,软件开发中的沟通无处不在,我认为几乎 100%的软件开发都与沟通有关。

任何设计系统(广义定义的)的组织都会产生一个设计,其结构是组织的沟通结构的复制。
梅尔文·康威— 康威定律

在软件开发中,沟通几乎无处不在,在客户和用户之间,在项目团队中,在产品所有者、设计师、架构师、开发人员和技术解决方案测试人员之间。最后,当我们的开发团队给计算机下达解决问题的指令并“编码”我们的产品时。

这不是我们是否交流的问题,而是我们如何交流和交流什么的问题。在本文中,我们将探讨软件开发过程中可能出现的一些沟通挑战,并提供一些如何克服这些挑战的建议。

愿景,又名北极星

软件开发意味着实现某种能力差距的解决方案的过程,这种能力差距理想地以需求的形式来描述。需求可能来自各种来源,有不同的意图,并且与不同的涉众相关。他们是项目方向的最强驱动力之一,所以清楚他们并为他们建立一个框架是很重要的。

一切都从这里开始——我们需要有一个愿景,以便达成共识。有人称之为“北极星”,它将有助于为我们提供指导方针、界限和奋斗的总体目标。愿景在协调项目团队方面也起着重要的作用。任何不属于愿景的东西都应该被视为无效而拒绝。顺便说一下,在过去,这是一种有效的方法,可以终止任何关于不合适的需求的讨论。愿景将为我们提供“为什么”的信息,因此它将提供关于我们的目标受众及其目标的信息,这将有助于我们确立起点和方向。

好,那么你对你的产品有一个愿景,太好了!让我们来看看你可能会陷入的一些陷阱。

团队规模和直接联系

项目团队中的直接沟通是有成本的,这部分可以用直接点对点沟通所需的连接数来描述:connections = teamMembers * (teamMembers-1) / 2

加入团队的人越多,直接连接的数量就越多,开销也随之增加:

  • 3 个人— 3 个直接连接
  • 5 个人— 10 个直接连接
  • 10 个人— 45 个直接连接
  • 15 个人— 105 个直接连接

当然,我们试图通过每天的 scrums、每周的 demos、playbacks、retro-perspectives 和其他形式来弥补这一挑战,将点对点的交流转变为一对多的交流,但是直接交流的需求永远不会完全消失。

弗雷德·布鲁克斯早在 1975 年就描述了这一事实的逻辑后果:

给一个后期的软件项目增加人力会使它变得更晚。弗雷德·布鲁克斯的布鲁克定律

在现实世界中,我更喜欢在一个团队中与最多 5 个人一起工作,应该给这个团队一个具有挑战性但仍然可以实现的功能或能力,并且可以完全归这个团队所有。许多软件项目需要大量的劳动力;然而,模式应该保持不变:分而治之 —将一个较大的产品分成几个部分,由更小的团队分配和拥有,从而在团队内进行范围有效的沟通,并创建另一个级别的跨团队沟通,仅限于与需要标准化和治理的领域相关的功能-支配性主题—更多详细信息可在此处找到:

信息的丢失

几年前,我们在大学进行了一个有趣的测试,叫做“传递信息的方式”。一个人单独给另一个人讲了一个 10 点的故事,然后这个故事就这样流传下去。在整个小组经历了这一过程后,结果相当令人惊讶:大约三个点原封不动地保留了下来,七个点完全丢失了,还有几个点被创造性地添加了进去。信息的丢失非常明显。当我们考虑沟通时,我们需要考虑损失,这样我们才能更好地验证收到的内容或重复理解的内容。

透视约束

不同的人有不同的观点,通常会导致不同的结论、观点和优先事项——这些都不应该被认为是故事的全部。用户受众越多,就越应该考虑这些视角中的约束。你可能会看到一群潜在的非常直言不讳的用户对你的应用中的搜索行为尖叫,商业利益相关者对应用内仪表板小题大做,程序管理可能最关注工具的改进。希望能够看到全貌的可能是项目或投资组合的所有者和/或首席架构师。优先化现在成为几种力量之间的*衡行为,我在这里唯一的概括建议是确定什么是最大化业务成果真正需要的,而不是特定利益相关者群体想要的。在这种情况下,沟通显然是至关重要的,可以提出以下问题:

  • 用户和利益相关者需要什么来实现?
  • 他们目前是如何实现的?
  • 是什么让今天很难甚至不可能实现?

在这种情况下,用户研究是关键,但是我不建议进行解决方案的研究——而是研究问题。

无声信息和信息隐藏

无论我们是否认为沉默信息的原因仅仅是无效,而不是缺乏沟通,甚至是有意隐藏信息,关键后果都是相似的——团队成员之间不共享信息导致的风险,有时被称为总线因素

如果你意识到你团队中的某个人既是关键人物又是不可替代的 T2,那么是时候控制局面,敦促并促进沟通、文档和知识转移了。从我的经验来看,防止信息隐藏的最有效的方法是建立一种开放和尊重的工作文化,在这种文化中,所有团队成员的贡献都受到赞赏,反馈不断流动,微观跟踪不存在。一旦分享相对于隐藏的好处变得非常明显,公开有效的交流通常会自然而然地随之而来。

预期

从项目团队的角度来看,多个涉众不断地提出期望,这显然需要理解。以我的经验来看,“什么”通常需要最高层次的关注,但是“为什么”以及目的在达成共识和协议方面要重要得多,所有这些都必须符合我们的愿景。

另一方面,为了避免不可避免地导致不满的不切实际的期望,涉众的期望可以而且必须在软件开发项目中得到管理。开始时的知识量通常会在完成一个项目所需的时间长度上产生更高水*的不确定性。这种不确定性带来了风险以及范围和时间之间的权衡,所有各方都应该从一开始就意识到这一点。你可能会问,期望管理的有效沟通是什么样的?

这里的关键是对未知的透明度、对已知的明确承诺、交付团队创建的调整、对承诺的可靠交付、过程修正的灵活性,以及向期望的方向迈出的许多微小但稳定的步骤,这些步骤展示了持续的改进并由此创造了信任。就我个人而言,我从来不热衷于试图将范围锁定在时间上的项目计划——我的思维方式与瀑布不兼容。我们只需要一份计划,在地图上选择大致的路线,细节可以边走边想。然而,在你的计划中留些缓冲空间也无妨😜。

摘要

我们已经知道,在任何软件开发项目中,沟通都是一个普遍而关键的因素。解释了为什么远景是定位和构建需求的基础,我们已经看到团队规模、信息损失、视角约束、无声信息和期望是软件开发项目中的潜在挑战,可以通过高效和有效的沟通来克服。

进一步阅读

**** **https://www.hdwebsoft.com/blog/most-important-skill-software-developer-communication.html **

关于 Thomas Reinecke —在过去几年中,我作为 IBM 内部几个关键转型项目的首席架构师,拥有独一无二的机会来共同领导和影响 IBM 内部一些最复杂、最全面和最有影响力的活动,例如支持、销售和业务合作伙伴生态系统的转型。我一直是一个非常好奇和善于思考的人,所以多年来我一直试图理解沟通对软件开发的影响,显然我已经实践了😁。我本质上仍然是一名 IT 工程师,所以我最感兴趣的是事情的实际方面,我在 medium 上分享我自己的真实世界经验。

如果你喜欢这个故事,并想在 medium 上阅读成千上万个这样的故事,你可以成为 medium 会员,每月仅需 5 美元。如果你愿意支持我的写作,请使用我下面的推荐链接,我将免费获得你的一部分会员资格。

https://medium.com/@thomas.reinecke/membership】T5T6

https://twitter.com/reineckethefox

数据科学的软件工程工具和最佳实践

原文:https://towardsdatascience.com/software-engineering-tools-and-best-practices-for-data-science-6a484fea641

伟大的代码带来伟大的机器学习

谷仓图片Unsplash 上拍摄

如果你对数据科学感兴趣,你可能对这个工作流程很熟悉:你通过启动一个 jupyter 笔记本开始一个项目,然后开始编写你的 Python 代码,运行复杂的分析,甚至训练一个模型。

随着包含所有函数、类、图表和日志的笔记本文件变得越来越大,您会发现自己面前有一大堆完整的代码。如果你幸运的话,事情会很顺利。那对你有好处!

但是,jupyter 笔记本是万恶之源。它们隐藏了一些严重的陷阱,可能会将您的编码变成人间地狱。

本文讨论了 Jupyter 笔记本电脑何时以及为何会产生反效果,并介绍了一些可改善您的数据科学工作流程的软件实践。

事不宜迟,让我们来看看🔍。

Jupyter 笔记本的问题

Kelly SikkemaUnsplash 上拍摄的照片

如果您想让 jupyter 原型更上一层楼,事情往往不会如您所愿。以下是我过去使用这个工具时遇到的一些情况。

希望您对此耳熟能详:

  • 所有的对象(函数或类)都在一个地方定义和实例化,可维护性变得非常困难:即使你想对一个函数做一个小的改变,你也必须在笔记本的某个地方找到它,修复它,然后重新运行代码。你不想那样的,相信我。将逻辑和处理功能分离在外部脚本中不是很简单吗?
  • 由于其交互性和即时反馈,jupyter 笔记本推动数据科学家在全局名称空间中声明变量,而不是使用函数。这在 python 开发中被认为是糟糕的实践,因为它限制了有效的代码重用。它还会损害再现性,因为您的笔记本变成了一个保存所有变量的大型状态机。在这种配置中,您必须记住哪个结果被缓存,哪个没有,并且您还必须期望其他用户遵循您的单元执行顺序。
  • 笔记本在幕后格式化的方式(JSON 对象)使得代码版本化变得困难。这就是为什么我很少看到数据科学家使用 GIT 来提交笔记本的不同版本,或者为特定功能合并分支。因此,团队协作变得低效和笨拙:团队成员开始通过电子邮件或 Slack 交换代码片段和笔记本,回滚到以前的代码版本是一场噩梦,文件组织开始变得混乱。以下是我在使用 jupyter 笔记本两三周后,在没有正确版本控制的项目中常见的情况:
    analysis . ipynb
    analysis _ COPY(1)。ipynb
    分析 _ 复制(2)。ipynb
    analysis _ FINAL . ipynb
    analysis _ FINAL _ 2 . ipynb
  • Jupyter 笔记本有利于探索和快速原型开发:它们是 EDA 和快速特别分析的最佳工具。
    然而,它们肯定不是为可重用性或生产用途而设计的。如果您使用 jupyter 笔记本开发了一个数据处理管道,您最多只能说您的代码只能在您的笔记本电脑或 VM 上按照单元的执行顺序以线性同步的方式工作。这并没有说明您的代码在更复杂的环境中的行为方式,例如,更大的输入数据集、其他异步并行任务或更少的分配资源。笔记本实际上很难测试,因为它们的行为有时是不可预测的。
  • 作为一个将大部分时间花在 VSCode 上,利用强大的代码扩展林挺、样式格式化、代码结构化、自动完成和代码库搜索的人,当切换回 jupyter 时,我不禁感到有些无力。与 VSCode 相比,jupyter notebook 缺少实施编码最佳实践的扩展。

好了,伙计们,现在抨击够了。我真的很喜欢 jupyter,我认为它的设计非常棒。你肯定可以用它来启动小项目或快速原型想法。

但是为了将这些想法以工业的方式传播,你必须遵循软件工程的原则,而当数据科学家使用笔记本时,这些原则碰巧被遗忘了。

让我们一起回顾一下其中的一些,看看它们为什么重要。

让你的代码再次变得伟大的 8 个技巧🚀

这些技巧是从不同的项目、我参加的聚会以及与我过去共事过的软件工程师和架构师的讨论中收集来的。如果你有其他的建议和想法分享,请在评论中告诉我。

1 —遵循 PEP8 命名约定,使用林挺并格式化您的代码

弗洛里安·奥利佛Unsplash 上拍摄的照片

代码质量最重要的方面之一是清晰:清晰易读的代码对于协作和可维护性至关重要。

以下可能有助于您获得更清晰的代码:

  • 使用有意义的变量名,它们是描述性的和隐含的类型。例如,如果您声明一个关于属性(例如年龄)的布尔变量来检查一个人是否老,您可以通过使用 is_old 使它既具有描述性又提供类型信息。同样的道理也适用于你声明数据的方式:让它具有解释性。
**# not good ...** import pandas as pd
df = pd.read_csv(path)**# better!** transactions = pd.read_csv(path)
  • 避免除了你没人能理解的缩写和没人能忍受的长变量名
  • 不要硬编码【幻数】直接编码。将它们定义在一个变量中,以便每个人都能理解它们所指的内容。
**# not good ...**
optimizer = SGD(0.0045, momentum=True)**# better !**
learning_rate = 0.0045
optimizer = SGD(learning_rate, momentum=True)
  • 命名对象时遵循 PEP8 约定:例如,函数和方法名用小写,单词用下划线分隔,类名遵循 UpperCaseCamelCase 约定,常量全部大写,等等。点击了解更多关于这些习俗的信息。
  • 使用缩进和空格让你的代码呼吸。有一些标准约定,比如“每个缩进使用 4 个空格”,“单独的部分应该有额外的空白行”……因为我从来不记得这些,所以我使用了一个非常好的 VSCode 扩展,叫做 更漂亮的 ,当按 ctrl+s 时,它会自动重新格式化我的代码

在怀疑和不确定的时刻,我总是参考这个回购,它聚合了一组非常有用和实用的概念,以获得一个干净的代码。

2 —使您的代码模块化📁

当你开始构建你认为可以在一个或多个项目中重用的东西时,你必须将你的代码组织成逻辑函数和模块。这有助于更好地组织和维护。

例如,当你在一个 NLP 项目中工作,并且有不同的处理函数来处理文本数据(标记化、剥离 URL、词条化等。),可以把这些单元都放在一个名为 text_processing.py 的 python 模块中,并从中导入。你的主程序会轻松很多。

以下是我学到的一些关于编写模块化代码的好技巧:

  • 干:不要重复自己。尽可能概括和巩固你的代码。
  • 功能要做一件事。如果一个函数做多种运算,那么泛化就变得更加困难。
  • 在函数中抽象你的逻辑,但是 不要过度设计它:有一点点可能性,你最终会有太多的模块。运用你的判断力,如果你没有经验,看看流行的 GitHub 库,比如 scikit-learn ,看看他们的编码风格。

3 —重构您的代码📦

重构旨在重组代码的内部结构,而不改变其功能。它通常是在一个工作的(但还没有完全组织好的)代码版本上完成的。它有助于消除重复的功能,重新组织文件结构,并增加更多的抽象。

要了解更多关于 Python 重构的知识,这篇文章是一个很好的资源。

4 —让您的代码高效⏱

编写执行速度快、占用内存和存储空间少的高效代码是软件开发中的另一项重要技能。

编写高效的代码需要多年的经验,但这里有一些快速的提示,可以帮助你发现你的代码是否运行缓慢,以及如何提高它:

  • 在运行任何东西之前,检查算法的复杂性,以评估它的执行时间
  • 通过检查每个操作的运行时间来检查脚本的可能瓶颈
  • 尽可能避免 for 循环,对操作进行向量化,尤其是当你使用像 NumPypandas 这样的库时
  • 通过使用多重处理来利用计算机的 CPU 内核

5 —使用 GIT 或任何其他版本控制系统🔨

以我个人的经验,使用 GIT + Github 帮助我提高了编码技能,更好地组织了我的项目。因为我在与朋友和/或同事合作时使用它,它让我坚持我过去不遵守的标准。

图片由作者修改

使用版本控制系统有很多好处,无论是在数据科学还是软件开发方面。

  • 跟踪您的更改
  • 回滚到代码的任何先前版本
  • 团队成员之间通过合并和拉取请求进行高效协作
  • 代码质量的提高
  • 代码审查
  • 向团队成员分配任务并监控他们的进度

Github 或 Gitlab 等*台甚至更进一步,提供了持续集成和持续交付挂钩,以自动构建和部署您的项目。

如果你是 Git 新手,我推荐你看看这个教程
或者你可以看看这个备忘单:

图片由作者修改

如果你想具体了解机器学习模型如何版本化,可以看看这篇文章

6 —测试您的代码📐

如果您正在构建一个执行一系列操作的数据管道,确保它按照设计目的执行的一种方法是编写检查预期行为的测试

测试可以简单到检查输出形状或函数返回的预期值。

作者图片

为您的函数和模块编写测试有很多好处:

  • 它提高了代码的稳定性,使错误更容易被发现
  • 它防止意外输出
  • 它有助于检测边缘情况
  • 它防止将损坏的代码推向生产

7-使用🗞测井

一旦代码的第一个版本运行,您肯定希望在每一步都监控它,以了解发生了什么,跟踪进度,或者发现错误的行为。这里是您可以使用日志记录的地方。

以下是一些有效使用日志记录的技巧:

  • 根据您想要记录的消息的性质,使用不同的级别(调试、信息、警告)
  • 在日志中提供有用的信息,以帮助解决相关问题。
**import** **logging**
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

8 —使用可靠的原则。

如果您想编写更好的代码,并以可维护性、可读性和可扩展性为目标,请考虑 Robert C. Martin 的《坚实的原则》。

  • 单一责任:每个软件组件(一个类,一个方法,一个函数)应该只有一个责任。
  • 开闭原则。每个实体都应该对扩展开放,对修改关闭。这意味着添加新功能不会改变现有代码。
  • Liskov 替换:你需要能够用子类替换每个类。这意味着子类的接口应该以与基类相同的方式运行。
  • 接口分离:每个对象都应该有一个接口(=一组方法),在内聚性方面尽可能小。它应该做一件事。
  • 依赖倒置:与另一个类交互的类不应该知道它的内部工作方式。例如,如果您使用数据库,请不要在代码中使用原始查询。你需要将它们隐藏在抽象背后。

参考

结论

数据科学家通过制作不与公司系统和基础设施进行任何通信的报告和 jupyter 笔记本找到出路的日子已经一去不复返了。
如今,数据科学家开始开发可测试和可运行的代码,与 IT 系统无缝集成。因此,遵循软件工程最佳实践成为必须。我希望这篇文章能让你对这些最佳实践有一个大致的了解。

感谢阅读!

新到中?您可以每月订阅 5 美元,并解锁各种主题的无限文章(技术、设计、创业……)您可以通过点击我的推荐链接来支持我

https://ahmedbesbes.medium.com/membership

卡斯滕·怀恩格特Unsplash 上拍摄的照片

软件!在!太空!

原文:https://towardsdatascience.com/software-in-space-23a6c5aa0521

当人们想到太空探索时,他们往往会想到直冲云霄的火箭。但是成功的任务不仅仅是宇宙飞船。

是的,太空中已经有软件了!作者图片

当我们想到太空探索时,我们往往会想到宇航员、火箭或国际空间站。也许我们还会想到坐在中国或美国航天局办公室里的一组专家,当他们成功地在火星上着陆时,爆发出阵阵喜悦。

我们往往会忽略那些编写软件的人,这些软件让卫星在轨道上运行,让火箭在轨道上运行。许多人在火箭发射前筛选来自卫星传感器的数据或模拟火箭发射。他们也在做火箭科学。但是这样想就不那么直观了。

我们在媒体上看到的许多图像显示,专家团队在庆祝自己,或者酷的漫游者和火箭在外层空间巡航。它们满足了我们人类的好奇心和我们对跨越新的边界和扩展我们视野的永恒渴望。同时,这些类型的故事让事情变得足够简单。任何人都可以意识到人类已经在火星上登陆了一些漫游者。另一方面,需要大量的专业知识来了解火星车是如何建造的,驾驶它需要什么,以及哪些技术使它适合火星上的生活。

许多人想了解太空。他们想知道它看起来像什么,以及人类正在做什么来探索它。但是如果没有详细的知识,很难理解太空技术到底是如何工作的。

这就是成为一名软件工程师的好处。即使你不太了解物理,或者火箭材料,编码人员也能理解太空中使用了什么类型的软件技术,以及为什么。

虽然美国宇航局公开了很多代码,但很难找到空间技术软件工程师日常活动的细节。不过,埃隆·马斯克的项目中也有一些故事。考虑到在 SpaceX 或 Starlink 工作是许多工程师的梦想,这些将是我在这里的重点。

[## 艾是来找你工作的吗?

towardsdatascience.com](/is-ai-coming-for-your-job-2f593ab72b55)

Starlink,卫星计算自己的路线

早在 1945 年,当科幻作家亚瑟·C·克拉克第一次提出卫星电视时,这听起来像是一个白日梦。尽管花了三十年,这项技术最终成为现实。现在,许多人在他们的屋顶上有卫星接收器,并且认为它没什么。卫星相对于有线电视的主要优势是它们可以到达农村地区,这对于很长的有线电视来说是不划算的。

同样,卫星互联网对许多人来说听起来仍然像是白日梦。但是需求是存在的:四分之一的美国农村人认为他们缺乏高速互联网接入是一个主要问题。断断续续的互联网接入也是火车、飞机和轮船等移动物体的一个问题。任何试图在旅行中使用 WiFi 的人都可以证明这一点。

然而,这些问题可能很快就会得到解决。Starlink 是私营部门火箭先锋 SpaceX 的一个分支,正在为更广泛的互联网接入奠定基础。Starlink 互联网于 2015 年首次发布,如今已经在美国许多地方推出。

为了确保互联网不会太慢,卫星需要离地球非常*。因此,它们每次只能在头顶上停留几分钟。因此,与它们通信的地面天线需要经常改变与哪个卫星通信。并且卫星网络需要足够密集,以便在任何给定时刻为天线提供信号。

该网络由数百颗卫星组成;新的不断被添加,旧的不断被替换。因此,Starlink 软件负责人 Andy Bohn 团队没有时间将每颗卫星送入自己指定的轨道。相反,每颗 Starlink 卫星都自己导航。为了管理繁忙的交通状况,地球端的网络给每颗卫星一个位置,卫星把自己控制在它的位置上。

这个过程需要巨大的计算量。首先,卫星不仅有相互碰撞的风险。它们也可能与低轨道上的飞机和其他卫星相撞。Starlink 卫星已经参与了一半的太空中的*碰撞,其中两个物体彼此之间的距离小于 1 公里(0.6 英里),所以这种风险是真实存在的。

卫星需要处理大量数据。作者图片

第二,干扰的可能性导致大量的计算需求。当两颗卫星的信号重叠时,它们可能会失真,甚至相互抵消。为了避免干扰,需要将信号放入稍微不同的频段。但这并不像听起来那么简单,而且可能的频段数量有限。因此,波段过于相似的两颗卫星不能靠得太*。这一要求使卫星导航更加复杂。

你可能想知道为什么卫星的位置需要在地球上计算,而不是直接在卫星上计算。首先,如果卫星内部出了问题,去那里修理就困难多了。此外,在外层空间,事情比在地球上更容易出错。因为地球大气层外的太阳辐射强得多,比特更容易翻转。比特是所有计算机的 0 或 1 编码单位,当它们翻转它们的值时,会破坏整个软件程序。为了防止这种情况扰乱卫星的轨道,不同的机器共享软件,如果软件损坏,可以重新加载正确的副本。

Starlink 的软件是用众所周知的编程语言编写的。由于其可靠性和裸机编程能力,Starlink 在其卫星中使用 C++编写大部分代码。该公司还使用 Python 进行一些原型开发,因为它通常内置起来更快。这反映了开发者在自动驾驶汽车技术中的应用。

卫星互联网是一个非常雄心勃勃的项目,它伴随着许多困难的挑战。Starlink 无疑是这一领域的先驱,但其他公司和太空机构也在迅速跟进。一二十年后,它可能会像今天的卫星电视一样成为标准。

https://uxdesign.cc/product-designers-are-clashing-with-developers-and-its-ugly-59e9875d9866

SpaceX,还是努力不失败

同样雄心勃勃的是 SpaceX,Starlink 是它的一个分支。向外太空发射火箭、与国际空间站对接,或者瞄准火星都需要*乎完美的硬件和软件工程。测试当然可能会失败。但在最后的任务中,不允许出现任何差错。如果火箭系统的一部分不能正常工作,所有其他部分都需要补偿这一故障。

SpaceX 火箭的所有飞行软件都是围绕控制周期构建的。首先,读取所有输入,例如来自传感器的数据或来自地面的命令。然后,这些数据得到处理,重要的事情得到计算,如火箭的位置或生命支持系统的状态。然后程序进入睡眠状态几分之一秒,以节省计算能力,之后整个循环再次开始。

不同的子系统控制火箭的不同部分。为了防止大的灾难,这些需要彼此隔离。例如,如果驾驶飞机的导航系统出了问题,生命支持系统也不需要陷入混乱。如果有一件事出了差错,演出必须继续进行。

这种设置与许多其他科技公司的运营方式不同。以谷歌为例。他们记录每一次失败,选择那些看起来最重要的,并试图从中为未来吸取教训。换句话说,谷歌让失败发生,并试图从失败中吸取教训。

对于谷歌来说,这种方法非常有效。但是搜索引擎(以及翻译器、文档编辑器、云服务提供商等等)的操作与火箭略有不同。如果谷歌的一个进程失败了,也许一个搜索查询会返回可怕的结果。然而,如果载人火箭转向错误的方向,人类的生命将处于危险之中。

由于所解决问题的高风险性质,SpaceX 尽最大努力避免失败。虽然该公司的工程师确实接受火箭测试失败,但在这些情况下,他们几乎是故意让项目失败,以便为未来学习。然而,当火箭开始真正的任务时,一切都需要工作。这意味着即使火箭的一部分发生故障,它也必须保持完整。

虫子永远不会让太空任务失败。作者图片

测试、测试和测试代码

火箭软件需要尽可能的可靠。因此,毫不奇怪,美国国家航空航天局(NASA)和 T2 太空探索技术公司(SpaceX)的质量要求很高,尤其是与常规商业应用相比。精心设计的系统已经到位,以确保没有人通过将错误的东西与主分支合并来破坏代码。也就是说,SpaceX 的测试相关工具在软件开发领域都是前所未闻的。

在开发人员可以发出拉请求之前,他们需要满足一组详细的标准。在合并之前,代码要经过两次测试,在实际合并之后还要再测试一次。

SpaceX 的持续集成环境很大程度上基于 HTCondor,其元数据用 PostgreSQL 管理。此外,该公司使用 Python 进行后端测试运行,构建流程编排和 web 服务。对于这些 web 服务的前端,它使用 Angular,JavaScript 和一些 TypeScript。在集装箱化方面,SpaceX 使用 Dockers,以及一点点 Kubernetes。

因此,工具和语言的选择与你在陆地公司的期望非常相似。然而,满足质量要求和合并要严格得多。

构建帮助建造火箭和卫星的软件

除了部署在火箭和卫星内部和周围的软件,航天项目还涉及应用软件。这种类型有助于将火箭带到发射台,并准备发射,涉及供应链、制造、金融、库存等领域。

随着许多行业的趋势,SpaceX 的应用软件已经从单一架构转向微服务,具体来说就是从 AngularJS、C#和 MySQL 转向 Angular、PostgreSQL 和容器化。其优势在很大程度上与 SpaceX 的所有其他系统相同:如果一个部件损坏或等待维修,这种延迟不会对其他部件产生太大影响。

SpaceX 的应用软件部门与其他公司的同类部门不同之处在于,他们有四个非常不同的项目需要支持:向外太空运送货物的猎鹰、专注于载人航天的龙、专注于星际运输的 Starship 和卫星互联网的 Starlink。这个项目范围甚至使它与美国国家航空航天局有所不同。

与其他领域一样,太空技术公司使用的工具和遵循的趋势与其他公司基本相同,但项目的范围和种类比大多数地面公司要多得多。

换句话说,如果你是一名经验丰富的软件开发人员,并且正在考虑在 NASA、SpaceX 或 Starlink 工作,那么你不需要学习更多的工具和框架。但是你应该为更多样的任务、更高的质量要求和更紧张的工作做好准备。

我们应该大肆宣传太空飞行吗?作者图片

太空软件也让地球人受益

当我们甚至不能正确处理我们在地球上的问题时,我们有理由问为什么要探索外层空间。在持续的疫情,种族和社会不*等,洪水和野火,加上所有生活中的小问题,难道我们不应该把目光从天空移开一分钟吗?

不。这是真的,每一次火箭发射都燃烧了数量惊人的燃料。没错,每一个在星际飞船上工作的软件开发人员都不是在开发一个打败疫情的应用程序,或是为贫困社区开发一个支付处理器。诚然,投资于外层空间的每一美元并没有投资于公*的住房、更好的教育或保护野生动物。

那些火箭发射、工人和美元以其他方式回来。例如,美国国家航空航天局的开源软件可以帮助减少飞机排放,计算太阳能系统的大小和功率需求,或者优化风力涡轮机的效率。因此,通过为外层空间编写代码,一些开发人员可能只是在为其他领域变得更加绿色做出贡献。

此外,私营公司在太空中的崛起并不一定是件坏事。如果一小群富人因为把自己发射到月球或更远的地方而损失了一部分钱,那就这样吧。

通过制定明智的法规,我们确实需要确保这些人不会殖民太空和重复历史。这些法规的存在确保没有一个国家将太空称为自己的领土,每个国家都可以自由探索太空,不允许任何实体对太空或环境造成损害,等等。只要我们确保这些规则得到尊重,我们就不会拿不太富裕的人的未来冒险,我们不会浪费税收,我们可以获得开源代码的好处,我们可以满足人类对新领域的好奇心。听起来对我来说是三赢三赢。

本文原载于 内置于

成为 中等会员 对我的内容拥有完全访问权限。

太阳能电池板发电分析

原文:https://towardsdatascience.com/solar-panel-power-generation-analysis-7011cc078900

你的太阳能电池板能产生多少能量?做出你自己的计算来保持控制

玛丽安娜·普罗恩萨在 Unsplash 上的照片

在过去的几个月里,我一直在选择一家供应商为我的房子安装太阳能电池板。我注意到的是,所有供应商都使用他们自己的计算方法,而我的房子没有默认朝向,这让他们很难接受。我的处境很不幸,大部分太阳能电池板需要安装在垂直的墙上,而不是屋顶上。

因此,为了比较不同供应商的发电量,并对倾斜 90 度的电池板进行良好的计算,我开始自己进行计算。为此,我创建了一个计算方法,您可以为几个具有各自特征的配线架位置输入自己的配置。这些位置的结果将被合并。例如,不同的位置可以是你房子的前后屋顶。

获取太阳辐射信息

我开始做自己的太阳路径计算,但在 github 找到了优秀的 pvlib 包。它帮我解决了大部分问题,拯救了我的一天。

pvlib 包用于确定太阳辐射量和太阳能电池板的发电量。在我的案例中,它使用了欧洲委员会提供的 2005 年到 2020 年的太阳辐射数据。使用真实生活数据,而不是一直有阳光照射的一些最佳情况。

以下代码获取一组面板的数据:

获取太阳能电池板电网的太阳辐射和发电

这种方法采用位置(纬度、经度)和面板配置来获取辐射和功率数据。面板配置包括面板数量、方位角、倾斜角和峰值功率。else 语句和以下代码需要能够指定零个面板。当未指定面板时,库无法处理“0”的峰值功率。对于不同布局选项的比较,指定零可能是有用的。所使用的数据源包含 2005 年至 2020 年的数据。

一个请求产生一个数据帧:

获取 _ 面板 _ 功率 _ 数据的结果(图片由作者提供)

该数据框包含 2005 年 1 月 1 日到 2020 年 12 月 31 日之间每天每小时的太阳辐射和发电量。

请注意,方位角“0”是指南方,而不是北方。正方位是东方,负方位是西方。

面板位置

并非所有面板都可以放置在同一个屋顶上,因此我们希望能够指定几个位置的配置(例如,屋顶、棚子上、地面上、墙上等)。这种配置如下:

[{'name': 'front', 'tilt': 35, 'azimuth': 30, 'nopanels': 2, 'power': 0.385},
 {'name': 'back', 'tilt': 35, 'azimuth': 150, 'nopanels': 8, 'power': 0.385}]

可以指定每个部分的方位角(南= 0°)、倾斜度、面板数量和每个面板的峰值功率。如果使用不同的面板,则需要最后一个。

围绕obtain_power_panel_data方法构建了一个包装器,它接受配置并获取每个屋顶/部分的数据。

此方法接受包含面板位置的字典,并返回包含所有面板的太阳能数据的数据框。它还获得太阳位置(第 27-30 行)并将其添加到太阳辐射信息中。为了方便起见,增加了月份和季节栏。

一个示例用法是:

这指定了位置名称“front ”,两个面板在屋顶上以 35 度角向西南方向看。第二个位置朝向西北方向,包含 8 个相同角度为 35 度的面板。使用相同的太阳能电池板,峰值性能为 0.385 kW。

可以用一行代码创建字典,但是数组是为了可读性。生成的数据帧为:

综合太阳数据(作者截图)

这是我们评估所需的完整数据集;对于每组太阳能电池板,每天的每个小时都有太阳辐射和产生的电力可用。每个记录还包含太阳在天空中的位置。我们现在准备评估太阳能电池板的性能。

一天中的表现

首先,我们将评估一天的表现。为此,我们首先在数据集中搜索辐射最高的一天。然后我们画出这一天的发电量。每个屋顶和总发电量。

首先,我们搜索产量最高的一天。在这种情况下是 2020 年(第 35 行)。过滤今年的数据集(第 2 行),然后计算每天的发电量总和(列“P”)(第 3 行),返回一个数据框,其中日期为索引,发电量为列。用idxmax (第 4 行)找到最大值后,相应的日期以字符串形式返回,格式为 2020–05–28 第 5 行)。

这个日期和完整的数据帧被提供给plot_a_day方法,该方法产生:

一天的发电量(图片由作者提供)

在过滤给定日期的数据(第 7 行)后,生成了两个图。左图显示了一天内每单位时间的发电量,右图显示了一天内的累计发电量。单独的面板位置用虚线标出,所有面板的总数用实线标出。

第 9 行为图形创建了两个轴。第 10 行遍历所有面板位置。第 11 行过滤当前位置的数据,并在左图中用虚线绘制每小时的数据(day_data[day_data.location == name][‘P’])。第 12 行绘制了右侧图中的累积数据(…[‘P’].cumsum())。第 13 行和第 14 行将每日总数添加到图表的右侧。第 16 行到第 19 行做了同样的事情,但是是针对所有位置的合计。最后,第 21 到 31 行格式化轴、图表并添加适当的标签。

过去几个月的业绩

下一步是查看一年中几个月的表现:

这将生成每月发电量和全年累计发电量的概览:

一年的发电量(图片由作者提供)

代码遵循的结构与一天的图表大致相同,但过滤是在一年的水*上完成的,而汇总是在几个月内完成的。

此外,第 8 行计算每月的*均值,第 9 行将该值绘制为水*虚线,第 10 行在该行旁边绘制*均值。

多年来的业绩

数据框包含 2005 年至 2020 年的数据。这意味着我们可以计算多年来的性能(不考虑性能下降)。这模拟了太阳能电池板在此期间已经就位的情况:

导致:

历年表现(图片由作者提供)

同样,该方法从汇总数据开始,但现在是每年一次。然后(第 5-7 行)画出每月发电量和累计总和。添加每月的*均值(第 13-14 行),最后格式化图表。

正如我们在这张图表中看到的,每年的业绩远非稳定。在本例中,每年的发电量在 2800 千瓦时和 3200 千瓦时之间波动,具体取决于每年的天气情况。

奖励:季节性表现

有了太阳能电池板,你一年的总发电量就差不多了。但是在这一年中,会有一些惊人的结果,这取决于你的地理位置。太阳的路径在一年中是不同的,这会影响太阳能电池板的效率。当我们选取本文中的面板位置并绘制每个季节中的一天(发电量最高的一天)时,可以看到一些显著的结果:

季节性表演(图片由作者提供)

该图显示了一天中的太阳位置、太阳的高度以及每个电池板位置的发电量。在秋季和夏季,面板“背面”比面板“正面”表现更好(3 倍)。但在冬季和秋季,差异很小(约 1.2 倍)。“前”面板的性能在一年中相对稳定,除了秋季。

绘制这些图表有点复杂:

plot_seasons 方法过滤请求年份的数据(第 22 行)并确定最大发电功率(第 23-24 行),四舍五入到十位,这将用于为所有图表提供相同的 y 轴限值。接下来,为这四个图创建一个网格(第 26–27 行)。

对于每个季节,发电量最高的一天由get_best_day_of_season 确定,这一天绘制在四个网格区域之一。

plot_day_extended方法将轴分为左轴和右轴,并在左轴(第 9-10 行)绘制太阳高度和天顶,在右轴(第 11-12 行)绘制面板性能。第 13–19 行完成了图形的格式化。

结论

pvlib 库简化了太阳能电池板性能的计算。这使得比较不同承包商的报价更加容易。他们的报价都使用不同的方法来计算有效性,与上述代码,他们可以比较等效。

财务影响的计算不在此比较范围内,因为这高度依赖于经济环境和低点。比较产生的功率更透明。

完整的笔记本可以在 github 上找到。作为额外的奖励,它包含将地址地理编码为经度坐标的代码,以简化使用。可以在倒数第三个单元格中指定地址和面板位置。尽情享受吧!

遗言

我希望你喜欢这篇文章。要获得更多灵感,请查看我的其他文章:

如果你喜欢这个故事,请点击关注按钮!

免责声明:本文包含的观点和看法仅归作者所有。

像物理学家一样解决问题

原文:https://towardsdatascience.com/solve-problems-like-a-physicist-c42b795161c0

适用于数据科学和许多其他领域的分步指南

解决问题的能力和数据技能是 21 世纪的超级力量。在这篇文章中,我描述了我解决问题的科学方法,重点是与数据相关的问题。

图片由 BarbaraALane ( Pixabay )提供。

我是一名物理学家,但我离开了学术界,现在在数据领域工作。我做的一些事情通常被归因于“数据科学家”的角色,但我对这个被深深误解的术语有一点问题——因为许多自称的数据科学家完全缺乏科学部分。此外,我认为在各种数据工作角色(数据科学家/分析师/工程师等)之间划分界限会适得其反。)—毕竟,这些是强烈相互依赖的。这就是为什么我更喜欢把“面向数据的工作”作为所有需要良好数据技能的紧密交织的角色的总称。

我发现,我在学习期间和作为实验物理学博士后研究员的工作中所学到的东西,对以数据为导向的工作非常有帮助。这是因为物理学就是解决问题的。当然,物理学中有很多东西你可能不需要,比如一堆自然常数和广义相对论方程。但是如果没有解决问题的能力,所有这些东西都是毫无用处的。

但是什么是解决问题的能力呢?任何能力都包括知识和经验。你必须学习你需要的东西(比如编码和统计),你必须实际解决问题来积累经验。但是除此之外,拥有一个你可以应用的结构化的方法是很有帮助的。要遵守的准则。科学的方法。

我想了很多我自己解决问题的过程,我决定为你把它分成六个步骤。顺便说一下,这些在大学里都没有明确地教给我。以上是我根据多年的学习和经验总结出来的想法。这是你像物理学家一样解决问题的方法。

警告:解决复杂的问题需要大量的大脑活动。图片由 ElisaRiva ( Pixabay )提供。

1.明确目标

虽然看起来很明显,但这第一步是许多解决问题的尝试失败的地方。假设我向你提出了一个问题:气候变化。现在安全了吗?当然不是。我要你想出减轻温室效应的策略吗?或者我想让你预测一下,如果排放量保持在目前的水*,气候会如何变化。或者我可能对气候变化如何影响全球经济感兴趣。

如果你不知道别人对你的期望是什么,你就不可能表现得很好。这就是为什么你解决问题的第一步是明确目标。问这两个问题:

我应该回答哪些问题?

这些问题应该尽可能精确,以避免误解。你的作业可能不会以问题的形式呈现给你(例如,“为这个项目建立一个数据库。”),但是您可以通过提问来查看它(例如,“什么数据库架构最适合我们?”).

2)我在为谁准备结果?

这很重要,因为你应该始终考虑你的观众的背景和期望。向有全新背景的人解释复杂的分析与向营销人员展示相同的数据是非常不同的。

别忘了提问来明确你的目标。图片由 terimakasih0 ( Pixabay )提供。

2.检查您的数据

一旦你知道你想发现什么,下一步就是问自己做一个清单:

我有哪些数据?

也许更重要的是,我没有什么数据?

你知道这些问题的正确答案是没有答案吗?比如,“爱丽丝比鲍勃大两岁。查理比黛西小 1 岁,黛西比爱丽丝大 3 岁。鲍勃多大了?”要回答这个问题,你需要知道他们中至少一个人的年龄。

如果你运气不好,你应该解决的问题可能就是这样,只是更复杂,使它的不可解决性不那么明显。这就是为什么你必须经常检查你是否有解决问题所需的所有数据。如果没有,您有两个选择:

1)与你的客户或经理讨论缺乏数据的问题。

如果丢失的数据可以很容易地得到,那就是要走的路。如果没有,他们可能会重新考虑你应该回答的问题。但我猜他们更有可能希望你选择第二个选项:

2)对缺失数据进行假设。

这可能相当棘手,当然值得单独写一篇文章。这里我想强调的是,在假设的时候,透明度是超级重要的。清楚地传达你的假设,并尝试给出不确定性估计/可信区间。这对于判断结果的预期准确性非常重要。

检查一下你有什么数据,缺少什么。图片由 Alexas_Fotos ( Pixabay )提供。

3.分解和简化

让我们说,你有理由相信你有解决问题所需的所有数据。现在怎么办?下一步有两个重要的方法可以应用:分解和简化。我在这里一口气提到它们,是因为它们紧密交织在一起。我们将从分解开始。

我所说的分解是指把一个问题分解成更小的问题。例如,你的任务可能是回答这样一个问题:“到目前为止,我们发出的哪种类型的电子邮件是最成功的?”一种方法是首先想出一种方法来对所有的简讯进行分类(例如,使用决策树模型,然后计算统计数据,例如每个这样获得的简讯类型的*均阅读率和点击率。通过这样做,您将问题分解为两个相对独立的部分。

分解以两种方式让你的生活更轻松:

  1. 它给你的问题增加了结构。这让你更容易找到解决问题的方法,并解释你的解决过程。
  2. 当您将来面临类似的问题时,它会使重用代码变得更加容易。

现在我们来谈谈简化,它和分解一样重要。物理学家喜欢简化。有一种误解,认为物理学家喜欢做大量复杂的数学运算。我们没有。事实上,大多数物理学家在做数学时都很懒。这就是我们简化问题的原因。

为了简化问题,问自己这些问题:

1)在不太改变结果的情况下,我可以忽略什么?

例如,当你计算一个相对较慢、较重的物体(如篮球)的轨迹时,你通常会忽略空气阻力。这使得方程更简单,对结果的影响也很小。

我可以用更方便的方式描述不应该被忽略的特征吗?

物理学家经常面临这样的情况:给定条件的复杂性阻碍了寻找精确解。在这种情况下,我们做一个*似。一个很好的例子是摩擦:我们不用考虑两个接触物体表面上微观上的微小凸起,而是简单地使用一个称为摩擦系数的*均值。

当我提到特性和*似值时,您可能会想到主成分分析。这不是我在这里谈论的。PCA 当然有它的使用案例,但是它应该(如果有的话)在稍后阶段应用。解决过程的当前步骤发生在你的头脑中。为什么?因为如果你在寻找一个好的解决方案,没有什么比深入了解问题更重要的了。

简化的难点在于避免过度简化。确保你明白你的简化将如何影响你的结果,以及在什么情况下它们是有效的。继续摩擦的例子:当你处理的物体很小,它们的大小并不比它们的表面凸起大多少时,基于摩擦系数的计算将不再有效。

把你的问题分成小块。图片由 markusspiske ( Pixabay )提供。

4.想办法

如果你到了这里,你应该已经对你的问题有了很好的了解。你知道你想找出什么,你有什么数据,你的问题如何分解和简化。在这一点上,你可能会忍不住要打破你的关节,直接跳到实现中去。我反对这样做。

并不是说我是万里挑一的聪明人,总是有完美的计划。只是我经常犯“谁需要计划——让我们开始吧”的错误,我知道这是个坏主意。发生的情况是,你首先取得了快速的进步,然后在某个时候你意识到你忘记了一些重要的事情。然后,你倒退,重写你的代码,重新运行你的评估,模拟,或实验,最终浪费时间。很多时间。

首先,抑制住开始实施的冲动,仔细规划。我推荐以下几点,特别是对于要通过编码解决的问题:

  1. 把问题形象化。画一个流程图,概述达成解决方案所需的各个步骤。为这些步骤以及它们的输入和输出命名,这些可能会成为您代码的函数及其参数和返回值的名称。
  2. 从流程图的最后一步开始往回走,问自己:“考虑到前一步的结果,需要做什么才能实现这一点?”这将帮助您识别流程图中的错误,例如特定步骤所需的数据在执行流程的这一点上不可用。
  3. 对于每一步,记下到底需要做什么。尽量精确,对更复杂的步骤做一个有序的列表。

规划可能需要一些艰苦的思考。图像由 blend 12(pix abay)生成。

5.实施

有一个计划是好的,但是——令人惊讶的是——你也需要实施它来获得结果。不幸的是,这通常是最困难的部分。就智力挑战而言不一定——毕竟,在前面的四个步骤中,你已经对这个问题进行了大量的思考。但是写数百行代码或者(这是为物理学家准备的)一页一页地试图解决一个复杂的积分可能会非常乏味。

对于任何相当复杂的问题,在这个阶段你可能会经历一些麻烦。可能是这样

  1. 你的代码不能像预期的那样工作,你很难调试它。
  2. 您陷入了困境,因为您不知道如何处理一个困难的数学表达式,也不知道如何实现数据转换所需的算法。
  3. 你意识到你在之前的阶段忽略了一些重要的东西。

第(3)点可能被认为是最糟糕的,因为它迫使您离开实现阶段,重新访问前面的步骤。但它发生了,这不是世界末日。意识到有些事情出了问题,并以有意义的方式处理它,远比产生一个不正确的解决方案或完全放弃这个问题要好得多。

对于第(1)和第(2)点,我的建议是坚持下去。你已经走了这么远,所以现在不要放弃。继续以结构化的方式工作,彻底测试你的代码(或者检查你的计算)。如果你停滞不前,找不到取得进展的方法,你可能需要回到上一步,获得新的视角,从不同的角度解决问题。打个物理学的比方:如果基于粒子的问题描述对你不起作用,也许你需要切换到基于波的描述(波粒二象性允许你这么做)。

实施可能是一项艰巨的工作。图片由 powertools ( Pixabay )提供。

6.质疑你的结果

这可能是我列表中最被忽视和低估的一点。我怎么强调这一点都不为过,尤其是如果你自称是数据科学家的话。科学方法论的核心是试图证明某些事情是错误的。无论一个理论有多么复杂和数学上的美丽,如果任何人发现了与该理论相矛盾的东西(实验的结果或数学中的反例),该理论就被证明是错误的。而错误的理论显然不是科学的目标。因此,每个科学家都有义务尝试证明他们自己的理论是错误的,并质疑他们的结果。这也适用于数据科学家,但不幸的是,它经常被忽视,正如我在之前的文章中对的评论。

那么,你如何去质疑你的结果呢?我建议问自己以下几个问题。

1)我的结果现实吗?
因此,在看到结果之前,你应该有所期待。如果你得到的与你的预期相差甚远,是时候重新审视你的解决过程了。

2)我的解决方案真的能发挥应有的作用吗?

你可能听说过聪明的汉斯效应,这原本是一个心理学术语,在机器学习应用中也会导致错误的结果,正如这篇 Nature 文章中所解释的。本质上,所发生的事情是你构建了一些看似解决了一个给定问题的东西,但它实际上解决了另一个恰好与你的问题相关的问题。显著地改变你的输入参数,检查这样得到的解是否合理。

我是在欺骗自己吗?

如果你的结果看起来可疑的好,问问自己这个问题。你可能通过做出某些假设,无意中根据你最初的期望形成了你的解决方案。总是检查你的假设对你的结果有多大影响。

只有当你的解决方案通过了这最后一步,才是时候宣称你解决了问题。你用科学的方法做到了——像物理学家一样!

确保你不会被自己的数据魔法所迷惑。图片由 anncapictures ( Pixabay )提供。

感谢您的阅读。如果你觉得这篇文章很有趣,请考虑订阅以在未来获得更多这样的内容。如果你还没有中级会员,你可以在这里得到一个。

用图论和 Python 解决一个星空谷路径问题

原文:https://towardsdatascience.com/solving-a-stardew-valley-routing-problem-with-graph-theory-python-fd4471077b3a

探索“真实世界”用例的最短路径和最小生成树算法

伊万·班杜拉在 Unsplash 上的照片

介绍

在电子游戏星谷中,种植作物是创收和生产葡萄酒、泡菜和果冻等手工制品的好方法。我的温室里有一块 12x10 的作物,我一直在种植和收获。最*,我迷上了用图论优化我的收割路线的想法。

作者图片

为什么是图论?为什么不测试每条路线,计算出最快的一条?在许多现实世界的问题中,比如设计电网、规划飞行路线和优化互联网路由,有太多的可能性。我们需要更聪明地找到最佳路线。

在本文中,我将带您了解我如何将我的 Stardew Valley 农场概念化为一个图,我探索的最短路径和最小生成树问题的算法,以及这些如何启发了我用 Python 编写的算法,以找到收获作物的最快方式。

什么是图论?

简而言之,图论是研究由顶点(或节点或点)组成的图,以及它们如何通过边(或链接或线)连接或不连接。

作者图片

一些数学符号将有助于以后阅读算法:

  • 一个图可以定义为 G = (V,E) 其中 V 是一组顶点, E 是一组边。一个图中顶点或边的数目记为 |V||E|
  • 在一条边 e = (u,v)uv 是端点顶点,称它们彼此相邻。
  • 边可以具有表示为 w(u,v) 的权重,并且可以表示例如在v 之间穿行所花费的距离或时间。
  • 一个算法的总运行时间可以用大 O 符号来表示。这给了我们一个简化的、最坏情况下的表达式,表示算法如何随着输入(在这种情况下,| V || 和/或 |E| )的增加而增长。举个例子,如果我们有一个算法,在最坏的情况下对每个顶点检查一次,那么它的时间复杂度将是 O(|V|) ,或者干脆就是 O(V)**

在上面的图的定义中,一对顶点可以通过不超过一条边连接,并且不能包含环(其中边将顶点连接到自身)。我们将在接下来的用例中看到,这个定义经常需要扩展以解决现实世界的问题。

除了本用例所涵盖的内容之外,还有大量类型的图和算法,但希望本文能让您更好地理解如何用图的形式来概念化路由问题。

定义我们的 Stardew Valley 用例

我们可以把我的 12x10 的庄稼想象成一个图,其中每个庄稼是一个顶点,庄稼之间的可行走路径(在游戏方向上向右、向左、向上和向下)是边。

还记得我说过简单图形的定义经常需要扩展以解决现实世界的问题吗?这不是一个简单的图表。事实上,我们的图表有很多规则。

边覆盖

首先,让我们来谈谈我对这个图表的目标。我想找到一条接触所有农作物的路径,以最大限度地减少沿着这条路径旅行的时间(希望这比我以前只向左向右走的路线快)。我所描述的是边覆盖,即一个图的边的子集连接所有顶点,这可以用最小生成树来解决。

无固定来源

只要我有最快的收割路线,它从哪个顶点开始或结束都没关系。尽管我探索的算法可以找到从起点到终点的最短路径,但这更多是为了理解这种思维如何应用于我的问题。

连续的

我希望路径是一个连续的,可以在游戏中行走。

衡量过的

收割时在作物之间行走需要不同的时间,这些时间根据你行走的方向而变化(例如:向右收割比向左收割快)。这些时间充当边缘权重。

作者图片

多重图

因为边的权重是根据方向而变化的(即: w(u,v)w(v,u) 不一样),每对相邻的顶点从技术上讲都有两条边。每个方向一个。

未受指导的

我们的图是有向的还是无向的是一个棘手的问题。有人可能会说这是一个有向图,因为在两个顶点之间往返会导致两个不同的边权重。我决定将它定义为一个无向图,因为我们可以在任何时间向任何方向旅行,直到动态创建了一个边权重,两个顶点之间的边才是不同的。这也使得后面的最小生成树算法变得不那么复杂。

动态的

我们的旅行时间根据我们在一个给定的方向上走了多少步而变化。我们的边权重需要动态填充。

作者图片

剧透一下,没有一种算法能回答所有这些规则。最后,我自己写了一个(不太好的)算法,让我走了大部分的路。但是当我们探索不同的算法时,我们会看到它们是如何让我们更接*解决收割路线问题的。

Python 设置和 NetworkX

让我们设置我们需要的 Python 库,并使用名为 NetworkX 的库来绘制我们的图形。NetworkX 允许你存储图形的数据——顶点、边、权重等。—让建模和可视化变得轻而易举。

作者图片

在上面的示例中,我创建了一个顶点列表,在 for 循环中将顶点和边添加到图形中,将顶点放置在 12 层或 12 列中以模拟我的 12x10 裁剪面片,并使用 NetworkX 和 Matplotlib 绘制图形。

我现在已经忽略了边权重,因为它们对于路径是动态的。让我们探索一些有助于我们实现这一目标的算法。

让我们从这个问题的一个简单版本开始

寻找最短路径的广度优先搜索

让我们后退几步,想象我们正在处理一个简单的、未加权的图,其中所有边的权重都是 1。我们如何找到一个源或起始顶点 s 和所有其他顶点 v 之间的最短路径?这是一个单源最短路径问题。

最好使用的算法是广度优先搜索(BFS),它计算从一个顶点到下一个顶点的最短路径树,从 s 到任意 v 的最短路径是两者之间最短路径的总和。

该算法由以下值组成:

  • 这些是顶点的水*集合,其中 k 表示距离源顶点 s 的距离或步数。该算法将每个顶点 v 放置在水*集中。
  • 对于每个 v ,我们会找到最短的 d(s,v) 这是从 svP(v) 的距离,这是为了到达 v 而遍历的前一个顶点。

算法是这样的:

  • 初始化 L₀{s} 其中 d(s,s) 为 0。
  • i 初始化为 1。
  • 而上一级集合不为空时lᵢ-₁≦{ },让成为上一级集合 Lᵢ-₁ 的顶点。对于每一个不在一个水*集合中并且与相邻的vv,设置 d(s,v)i (第一次运行时,会被 s 。)********
  • 对于不可达顶点,设置 d(s,v) 为∞(无穷大)。

作者图片

现在我们有了从 s 到所有其他 v 的最短距离。从 s 到任意 v 的最短路径可以通过从 v 回溯所有 P(v) 找到。

总运行时间是 O(V+E) 因为我们在检查每个顶点和它们所有的邻居。这以线性时间运行(随着输入的增加,运行时间成比例地增长),并且是最快的路由算法之一。

让我们看看这与我们的图表规则有什么关系。

作者图片

BFS 将处理有向图和无向图(以及我们作为无向图的多重图),并提供从 sv 的连续路径。让我们在此基础上添加边权重,使我们更接*用例中的图表。

不是所有的边都是相等的

Dijkstra 的加权最短路径算法

Dijkstra 的算法是另一种单源最短路径树算法。它建立在 BFS 的基础上,允许边权重不为 1,最短路径被定义为具有最小总边权重的路径。

如果一个图中所有的边权重都是正的,那么 Dijkstra 是最好的算法。其思想是通过使用数据结构找到到 s 的最短距离的下一个【v】来找到从 s 到所有其他的最短路径。**

该算法由以下值组成:

  • Q 是一个优先级队列,包含每个的项目和关键字 (v,d(s,v)) 。该算法将为每个找到最短的 d(s,v)****
  • 为了更容易追溯最短路径,我们可以做一些修改。我们可以为每个分别存储 (v,P(v)) ,并且我们可以为已经在中访问和处理过的顶点存储 (u,d(s,u))**

算法是这样的:

  • d(s,s) 初始化为 0,将所有其他 v【s,v】初始化为∞。
  • S 初始化为空集 {}
  • Q 不为空,设vQ 中最小的【s,v】。从中删除 (u,d(s,u)) ,增加到 S 。(第一次运行时, u 会被 s 。)**
  • 对于每一个与相邻的中的vQ,如果【s,v】>d(s,u) + w(u,v) ,则将 d(s,v) 设为距离更短的 d(s,u) + w(u)****
  • 对于不可达顶点, d(s,v) 保持∞。

作者图片

要找到 s 和一个目标之间的最短路径,我们可以从d(s,v) 回溯所有 P(v) 的最短距离。对于一个已知的目标 v ,算法可以在从 Q 中删除后停止,因为它已经被访问、处理并且已经找到它的最短的 d(s,v)******

总运行时间是 O(VlogV + E) ,不包括修改,因为我们最多执行Q| V |次操作,并且对于 E 中的每条边,我们都要更新一次最短距离,可能是两次。 LogV 是如果使用斐波那契堆作为数据结构并且是一个很小的数。性能几乎可以和 BFS 一样好,而且它的运行时间也是线性的。**

现在我们增加了使用边缘权重的能力。非常好。

作者图片

负边权重怎么办?

如果我们处理的权重代表货币成本之类的东西,而边权重是负的呢?问题是可能存在负权重循环(循环是指路径的起始顶点和结束顶点相同)。我们可以重复遍历负权重循环,得到一条总权重为-∞的最短路径。然而,这并不一定是我们在实践中想要的!

这就是贝尔曼-福特算法发挥作用的地方。贝尔曼-福特计算在 s 和所有其他 v 之间的最短路径,其中路径使用最多 k 条边——从 k = 0 迭代到 k = |V| 。这有助于检测负体重周期(万岁!)但是运行时间更长(时间复杂度为 O(VE) )因为它创建了每个顶点的 |V|+1 版本和每个边的 |V| 版本。我们现在运行的是多项式时间,比线性时间慢。

我们用例中的图不包含负的边权重。这只是一个有趣的话题。

所有对最短路径算法

到目前为止,我们已经讨论了寻找从 A 点到 b 点的最短路径。有一些算法可以找到所有顶点对之间的最短路径。

Floyd-Warshall 算法使用动态规划——将问题分解成子问题并组合答案。其思想是,在v 之间的最短路径或者是在v 之间的最短已知路径或者是在k 加上在 之间的最短已知路径时间复杂度为 O(V ) ,是稠密图的最佳解。******

对于稀疏图,更好的算法是 Johnson 的,它运行一次 Bellman-Ford,将所有边权重(包括负权重)重新加权为正权重,同时保持路径的距离。然后运行 Dijkstra |V| 次。这与运行 Dijkstra| V |timesO(V logV+VE)具有相同的时间复杂度。

所有成对最短路径算法意味着我们不必使用固定的源。但是我们还没到那一步。

作者图片

网上有很多关于上述算法的 Python 实现指南。 NetworkX 对每一个也有一个 Python 函数。它们获取一个图 G 、源和目标顶点(如果是单个源)以及权重(可选)并返回最短路径中的顶点列表。

虽然上述最短路径算法可以用来拼凑一个更大的路径,提供边缘覆盖,这不会是最有效的练习。让我们继续讨论为边覆盖设计的最小生成树算法。

吃豆人在寻找最小生成树

Prim 最小生成树算法

生成树是构成接触图的所有顶点的树的边的子集。最小生成树是具有最小总边权重的生成树。

Prim 的算法类似于 Dijkstra,它会找到与生成树距离最短的下一个 v 并将其添加到生成树中。

该算法由以下值组成:

  • s 是任意的起始顶点。
  • Q 是一个优先级队列,包含每个 v 的项目和关键字 (v,w(u,v)) 。该算法将找到连接【v】S 的最小值【u,v】
  • S 是一个空集,其中顶点 v 将被添加到生成树中。
  • 为了更容易追溯最小生成树,我们可以为每个 v 分别存储 (v,P(v))

算法是这样的:

  • w(s,s) 初始化为 0,将所有其他 vw(u,v) 初始化为∞。
  • S 初始化为 {}
  • Q 不为空,设为最小 w(u,v)。从 Q 中删除 (u,w(u,v)) ,增加 S 。(第一次运行, u 会被 s 。)******
  • 对于每个中的 vu 相邻,如果【w(u,v)** <中的w(u,v】Q,设置**********
  • 记住,为了有一棵最小生成树,每个顶点都需要连通!我们不能有任何不可到达的顶点。

作者图片

为了找到最小生成树,我们可以回溯所有的 (v,P(v)) 这些就是我们的边。

总运行时间与 Dijkstra 相同—如果使用斐波那契堆 O(VlogV + E)

Prim 给了我们边缘掩护,但是它形成的树并不一定是游戏中可以行走的连续路径。

作者图片

让我们看看另一种最小生成树算法。

克鲁斯卡尔算法

Kruskal 算法背后的思想是,图的全局最小权重边在最小生成树中。它按权重将所有边按升序排序,并迭代选择最小权重的边(不会产生循环)添加到生成树中。

如果你的图不包含太多要排序的边,这是一个有效的算法。总运行时间是O(sort(E)+Eα(V)),因为我们按权重对 E 进行排序,并检查所有边。**

作者图片

克鲁斯卡尔让我们离图表的规则更远了。这证明了我需要一棵更像路径的最小生成树。

作者图片

NetworkX 还有一个寻找最小生成树的 Python 函数。它需要一个图 G ,要使用的算法和权重,并返回最小生成树中边的迭代器。

把它们拼凑在一起

农夫莉莉的算法

我们研究的算法中的两个核心思想告诉我们如何用动态边权重来解决我们的收获路由问题。

  1. 讨论的算法大多是贪婪算法。他们迭代地做出局部最佳决策,忽略了对未来的影响。BFS、迪克斯特拉、普里姆和克鲁斯卡尔一步一步地做决定,走向下一个最*的顶点。这并不总是可行的(我们很快就会看到),但它使合并动态边权重变得容易。
  2. 就像在 Dijkstra 和 Prim 中一样,我们可以使用一个数据结构来有效地做出局部最佳决策。这对于计算动态边权重和选择要行进到的下一个顶点最有意义。

我的算法由以下值组成:

  • s 是任意的起始顶点。
  • Q 是未访问的优先级队列,相邻顶点 v 并包含 (v,方向,w(u,v)) 。该算法将找到连接【v】S 的最小值【u,v】**
  • C 是一个列表,记录我们连续在某个方向上行进的次数。
  • 在每次迭代中,我们存储 上一个方向下一个方向 ,以便 C 能够保持计数。
  • 我们从旅行时间列表中得到 w(u,v)——每个方向一个列表右、左、上、下。下一个方向 和对应的 C 中的计数告知要查找哪个列表以及在列表中的位置。**
  • S 是一个空集,其中顶点 v 将被添加到路径中。

算法是这样的:

  • w(s,s) 初始化为 0,将 方向 初始化为空。
  • 初始化 C【0,0,0,0】为右、左、上、下。**
  • 初始化 S{}
  • V 不为空,设 uv 与最小的Q。从中删除 u 增加 (u,方向,w(u,v))S 。(第一次运行时,将会是 s 。)******
  • 设置previous _ directiondirection 中的最小值 Q**
  • 删除 Q 中的值,在 Q 中设置新值,包含 (v,方向,w(u,v)) f 或与相邻的每个v—计算 w(u)如果某个方向不可用或者已经被访问过,则将 v方向 设置为空,将【w(u,v) 设置为********
  • 设置next _ direction方向 中的最小值 Q**
  • 如果previous _ direction等于next _ direction,则在 C 中该方向加 1。否则,使 C 中的一切为 0,然后在下一个方向加 1。(第一次运行时,下一个方向将加 1。)**
  • 因为这是一个贪婪的算法,它总是对给定的 s 做出相同的决定。

作者图片

下面是 Python 代码。

我们可以使用 S 将路径作为边添加到我们的 NetworkX 图中,并将其可视化。我们将 G 初始化为多重图(有向多重图)以保持路径的方向。让我们也使路径和 s 不同的颜色,这样更容易阅读。

作者图片

正如我从我的第一次模拟中很快了解到的那样,当算法访问一个所有相邻顶点都已被访问过的顶点时,它有可能(并且很可能)被卡住,并且它无处可去。你可能会从代码中注意到,为了让它工作,我们真的应该把这个加到算法中:

  • 如果中的最小值【w(u,v)】,则意味着没有未访问的、相邻的顶点可用,停止算法。**

我们如何让算法做出更聪明的决定,使它不被卡住?我考虑了很多理论上的东西,但是超出了我想投入多少时间的范围。我稍后会介绍这些内容。但是现在,我想要一个快速的解决方案,添加这个很有效:

  • 如果 Q 中的最小值 w(u,v),意味着没有未访问的、相邻的顶点可用,则以 0.4 的代价在任何方向上两步走到下一个最佳未访问的顶点(走两条边且中间没有裁剪所需的时间)。如果这不可用,停止算法。
  • C 中的所有内容重置为 0。

这条路就是这样继续走下去的。

作者图片

现在这个算法给了我很多工作路径。

这是到达每个顶点的最快路径,从顶点 60 开始,总重量为 65.39 秒——比我过去的左右穿越路线节省了 6%。它首先利用了向右和向上的优势,这是最快的方向。当它不能再向右移动时,它就向左上下移动。它又回到起点,一直向右,然后又向上又向下。

作者图片

尽管这是该算法最快的路径,但它有一些反直觉的东西:它总是向左走,尽管向左是迄今为止最慢的方向。(更不用说,试图在游戏中行走是疯狂的。)但基于算法的逻辑,它正确地优先选择了向右和向上,它必须找到最快的方式向左返回。

我从中得到的是,上和下——相当于向右,最快的方向——是一个快速编队,在算法的大多数最快路径中都有某种形式。

我试着从离这里更*的地方开始——顶点 108——它离底部的温室入口更*。它可以上上下下,向右穿越——完全避免向左。

作者图片

在 64.53 秒的总重量下,这比从顶点 60 开始快 1%,它减少了从温室入口(中间底部)走到开始顶点的时间,并且在游戏中走起来更简单,更直观。更棒的是,比我以前的路线快了 8%。万岁!

但请注意,这条路径是根据我的人类直觉构建的,基于我在算法的最快路径中看到的内容。

这是算法从相同的起始顶点 108 开始所做的。

作者图片

它再次优先考虑先右后上,根据其逻辑这是正确的,但我们开始看到它的局限性。

进一步的考虑

如前所述,这是一个让我走了大部分路的算法。人类的直觉找到了一条更快的路径,我相信更复杂的算法会找到更快的路径。这是一个贪婪算法本身是不够的例子。

这里有一些关于如何进一步调整算法的思考。

  • 使用启发式算法,使算法有方向感或目标感,访问总权重最小的每个顶点。这有助于对不一定是局部最优的决策进行优先级排序,但有助于降低整体最小权重。
  • 使用动态规划来解决更小的子问题,并将答案放在一起以解决整个图。于是思考变成了更小的子问题应该是什么才能比贪婪算法更有效率?**
  • 保持算法的贪婪性,但内置了当算法停滞时回溯多个步骤并做出下一个最佳决策的能力。接下来的思考变成了it 应该如何决定后退多少步并做出不同的决定?**
  • 在每一步使用一个单源最短路径算法,每次找到最快的下几步,而不仅仅是下一步。

结论

我们已经看到了图论是如何成为构建和解决问题的强有力的方法。

更多的时间可以用来完善算法,特别是如果问题每次都需要一个新的最优解(例如:像导航应用程序)。

然而,将人类的直觉添加到不完美的算法中以找到更好的解决方案,并在真实世界条件下进行测试,可能同样有价值。

虽然这是一个相对小规模的问题,但思考和解决问题的过程可以扩大。在“现实世界”中,即使是很小的路线优化也有可能带来巨大的收入增长或成本节约。

请注意:在本文中,我忽略了能够一次收获三行的游戏机制,以及一些顶点是洒水器,目的是以可理解的方式演示图论。我还假设我在游戏中测量的旅行时间在每次游戏加载时都是相同的。

参考

[1] 广度优先搜索,麻省理工学院开放课件

[2] 贝尔曼-福特,麻省理工学院开放式课程

[3] 迪杰斯特拉,麻省理工学院开放式课件

[4] 动态规划:所有对最短路径,麻省理工学院开放课件

[5] 贪婪算法:最小生成树,麻省理工学院开放课件

[6] D. Galvin,图论符号 (2009),离散数学 2009 年春季

[7] G. Cattaneoa,P. Faruoloa,U. Ferraro Petrillob,G. F. Italiano,维护动态最小生成树:一项实验研究 (2010),应用数学,2010 年 3 月

【8】什么是大 O 批注?,可还原

使用 5 个鲜为人知的 Python 库解决复杂的 NLP 任务

原文:https://towardsdatascience.com/solving-complex-nlp-tasks-with-5-simple-python-snippets-libraries-7d4dfab131e8

一口大小的 Python 代码块

随着非结构化数据的增长,实现文本处理或 NLP 任务的解决方案变得越来越迫切。

在本文中,我为您的下一个 NLP 项目编译了 5 个有用的 Python 食谱。我们还将查看定期出现的各种用例,以利用这些代码块。

在这个练习中,我们将使用图层 WikiText 数据集——一个包含从维基百科上的一组高质量文章中提取的超过 1 亿个令牌的公共数据。使用{ 图层 }可以直接拉取该数据集。

您还可以在这里找到更多带有 WikiText 数据 的项目。

(1)使用{textstat}检查文本数据的度量

Textstat 是一个易于使用的库,用于计算文本数据统计,如阅读时间和字数。它还为你文本分配一个可读性分数,以帮助确定它的复杂程度和等级。

Textstat 等级级别

代码片段 :

## curText = doc_set[27310]
curText = '''george washington johnson was born on may 27 , 1811 near georgetown in scott county , kentucky , the son of major william and betsy payne johnson . major johnson died soon after the close of the war of 1812 , in which he was a participant , and george johnson was reared in the home of his stepfather , john allen.'''

用户案例/应用 :

当给利益相关者写论文、博客或报告时,你会希望确保内容简明易懂。Textstat 提供的度量可以帮助测量文本的属性。如果你对媒体上的文章进行了评估,你会发现短小、易读的文章比冗长的半学术性文章更受欢迎。

**** * 加入我们的 YouTube 社区🎦** 【数据与 Kat 谈判】 😄我帮你提升数据科学、分析和编码水*:

(2)用{pyspellchecker}纠正拼写错误

Pyspellchecker 顾名思义,提供纯 Python 的拼写检查和纠正。它使用一种 Levenshtein 距离算法来查找和比较原始单词的排列,然后确定最有可能的正确结果。

代码片段 :标记出错别字commentyragimimick

这是发现并纠正了错别字的返回结果,

{‘commentyra’: ‘commentary’, ‘gimimick’: ‘gimmick’}

现在,下面的一行程序使我们能够用正确的单词替换错别字:

用例/应用:

直截了当,识别并纠正拼写错误!当 NLP 项目带有包含错别字的杂乱文本数据时,Python 中的拼写检查器/校正器将是数据清理和数据预处理的有用工具。

图片来源:Pixabay

(3)用{下一个单词预测}进行下一个单词预测

下一个单词预测是一个生成式预训练转换器 2,又名 GPT-2,用于语言建模。这是一个方便的工具来实现一个心血来潮的下一个单词预测器。

代码片段 :

curText = “falafel has become popular among vegetarians and vegans , as an alternative to meat @-@ laden street foods , and is now sold in packaged mixes in health @-@ food stores . falafel ‘s versatility has allowed for the reformulating of recipes for meatloaf , sloppy 
joes and spaghetti and meatballs into vegetarian [MASK]”

我们根据之前的上下文预测了'素食者'之后的前 5 个下一个最有可能的单词,代码吐出了这 5 个单词/字符串:[‘dishes’, ‘and’, ‘,’, ‘-’, ‘recipes’]。现在,与我们的维基文本数据中的原始文本相比,

falafel has become popular among vegetarians and vegans , as an alternative to meat @-@ laden street foods , and is now sold in packaged mixes in health @-@ food stores .falafel ‘s versatility has allowed for the reformulating of recipes for meatloaf , sloppy joes and spaghetti and meatballs into vegetarian [dishes].

单词“***dishes***”被正确预测为最有可能出现的单词,这难道不令人惊讶吗!

用例/应用:

下一个单词预测是自然语言处理的基本任务之一。你可以每天在 WhatsApp、电子邮件、Facebook Messenger 或谷歌搜索等应用上使用它。这种自动完成功能为用户节省了时间,也可以被虚拟助手用来完成句子。

(4)创建有效的词云

Word Cloud 是一个简单而强大的可视化工具,可以量化和突出显示您基于文本的见解。生成一个词云并不是一个困难的过程;现在,让我们使用来自 WikiText doc_set[8319:8334]的文本列表来揭示这个非结构化子数据集的核心内容。

代码片段(Part1): 文本清理和构建 N 元模型

这里,我们创建了一个短语长度为 2-5 个单词的 N 元模型。

代码片段(Part2): 字云

“Super Big Power Crystal”, “Wumpa Fruit”, “Coco Bandicoot”!你认为这些维基页面是关于什么的?任天堂的派对游戏Crash Boom Bang!!

作者可视化

用例/应用:

人脑更喜欢视觉表现,而不是其他任何形式。您可以将最重要的信息转换成文字云,帮助您的受众定量地理解非结构化数据,而不是向您的利益相关者展示长篇大论的文本。

(5)语义相似度分析

的字典编纂相似度的语义相似度基于它们的意思来衡量文档/句子/短语的相似度。最有效的方法是使用强大的转换器对 句子进行编码,得到它们的 嵌入 ,然后使用 余弦相似度 计算它们的距离/相似度得分

为了证明,我们有以下三个句子,

sentences = ['''in italy , plum cake is known by the english name , baked in an oven using dried fruit and often yoghurt''',

 '''plum cake in the united states originated with the english settlers. this original fruitcake version of plum cake in the united states has been referred to as a reigning " standard american celebration cake through the time of the civil war''', 

 '''the song received positive reviews , peaking at eighteen in the united states. the song 's accompanying music video was also received warmly''']

代码片段 :

计算两个嵌入之间的余弦距离给出了“相似性”分数,

Similarity between Sent1 and Sent2 = 0.703 ## Both about plum cakes
Similarity between Sent1 and Sent3 = 0.249
Similarity between Sent2 and Sent3 = 0.462 ## moderate; mentioned US

用例/应用:

它广泛应用于信息检索和文本摘要,例如,从多个文档中提取前 N 个最相似的句子。相似性分数也可以用来减少维度和寻找相似的资源。

本文到此为止。所有的代码都可以在这个 Colab 笔记本里找到。继续尝试这些,并在下面评论哪一个是你最喜欢的?✨

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

你可能会发现其他博客也很鼓舞人心,

</6-sql-tricks-every-data-scientist-should-know-f84be499aea5> </4-hidden-gems-for-idiomatic-pandas-code-99c706e6ce93>

解决数据问题:初学者指南

原文:https://towardsdatascience.com/solving-data-problems-22ab8ac792bd

翻倒的卡车(中国云南红河)——图片由作者提供

我喜欢和对学习用数据解决现实世界问题感兴趣的学生一起工作。新学员在学习过程中经常会遇到挫折,因为他们希望高效地解决问题,但却要花大部分时间面对错误信息和进行谷歌搜索。让我给你省点悬念吧——那些问题不会完全消失。

如果你处理数据,你会看到许多错误信息,并发现自己在谷歌上花费时间。这些问题不会消失。然而,有了正确的资源和实践,你解决问题的技巧将变得更加有效和高效。本文提供了一组不同的策略,帮助您在处理数据时培养解决问题的技能。

在本文中,我使用电子表格作为工作示例,因为我认为电子表格是学习数据的理想起点,正如我在的一篇早期文章中所讨论的。然而,这里介绍的策略是软件不可知的,因此它们适用于您可能正在使用的任何软件。

了解你的问题

在你能解决一个问题之前,你必须先了解这个问题。避免一头扎进软件。确保你确切地知道你想要做什么。例如,假设您需要创建一个唯一的 ID,它基于 1)名的首字母,2)姓的首字母,3)他们居住的邮政编码,以及 4)一个四位数的随机 ID 的组合。邮政编码和四位数的随机 ID 用破折号隔开。

您查看任务旁边提供的数据,很快发现您没有正确的数据。数据是存在的,但必须从几个不同的领域提取并拼凑在一起:

数据是模拟的。图片作者。

许多新的学习者将开始搜索过程,好像存在一个单一的函数来解决这个问题。但是,在现实中,大多数问题都是通过一系列精心安排的步骤解决的。在寻找任何功能或采取下一步行动之前,花时间了解你需要完成什么。写出期望的输出有助于揭示解决问题所需的步骤。

如果您不熟悉学习数据,请尝试为剩余的行提供所需的结果。对于随机数字,你可以随意编造。

数据是模拟的。图片作者。

把问题分解成小步骤

解决问题的基本策略之一是将问题分解成尽可能小的步骤——原子步骤。试着描述每一步。不要写任何代码或开始寻找神奇的公式。用简单的语言做笔记。您可能不知道要使用的正确术语,这不是问题。尽量用简单明了的语言描述你需要做的事情。

数据是模拟的。图片作者。

本例中的动词——选择组合连接生成——不是正确的函数。没关系。把你想做的事情写出来,可以帮助你更好的理解问题。

确定你能解决的部分问题

有些问题看起来让人不知所措。你能多接*想要的输出?支持你能解决的问题的要素。一个有用的策略是创建助手列。这些柱子很像一个储存容器,允许你保存溶液的一部分。例如,让我们假设您知道 LEFT 函数,并且可以很容易地捕获名和姓的首字母。并且,如果您知道左边的函数,您可能会计算出右边的函数来捕获邮政编码。也许你也知道 CONCAT。在处理问题的剩余部分时,将这些值存储在一个 helper 列中。

数据是模拟的。图片作者。

在这一点上,想想你的拼图缺少的部分。为了达到你想要的结果,你需要解决哪些具体的事情?虽然你没有解决整个问题,但要承认你在解决部分问题上的成就。认可微小的成就对于建立效能感至关重要。自我效能是参与式学习的基础。

制定有效的搜索

一项会给你的职业生涯带来回报的技能是搜索技能。搜索技能包括制定有效的搜索和有效地找到最相关、信息丰富和用户友好的资源。下面的例子展示了一个基本的搜索策略。用这些元素写一个简洁的句子,然后去掉所有的填充词。

图片作者。

当你参与搜索过程时,你将开始建立一个语言库,并找到提供丰富答案的网站。StackOverflow 将是你必不可少的资源。事实上,我猜想你的几乎所有问题都已经在这个网站上被问过和回答过了。要发展的技能是有效地搜索和应用解决方案。我将讨论向 StackOverflow 发帖,但是在你第一次发帖之前,我们还需要讨论一些问题。

阅读文档!

迟早,你可能会听到有人告诉你“阅读文件!”这是一个合理的建议,因为你可能会错过一个小而重要的细节。例如,VLOOKUP 函数会绊倒许多学生,因为这些列必须以特定的方式排序。你在处理数据时可能用到的每一个软件都会有一些相应的文档。该文档提供了几乎所有内容的完整描述,包括用于操作数据的函数。文档内容丰富,但并不有趣。被预先警告。

开始学习如何浏览您正在使用的任何软件附带的文档。当你学习一个看起来有用的新函数时,花时间去学习它的参数和功能。高质量的文档通常包括小玩具示例,这是学习如何应用它们的极好起点。下面是 Google Sheets 的文档中 VLOOKUP 的一个例子:

图片来自 Google Sheets 文档。

用玩具例子调试

预计您将处理大型数据文件。但是,在解决问题时,当你做一个小的玩具例子时,这个过程会更有效率。通过将数据限制在几行和几列,消除环境中的大量干扰,帮助您只关注问题的本质部分,您可以轻松地创建一个玩具示例。

如果您可以让您的解决方案处理几行数据,您就可以更容易地将该解决方案推广到更广泛的数据集。此外,如果数据集很大,可以节省大量处理时间。有时文档中包含玩具示例,就像 Google Sheets 文档中包含的许多函数一样。这种发现式的学习方法非常有效。

图片来自 Google Sheets 文档。

不要忽略错误消息

每个人都害怕错误消息,但是错误消息可能是有益的。错误消息通常包含解决问题的线索,或者在执行搜索时提供附加信息。在一些软件环境中,错误信息可能会直接显示出来,比如 Google Sheets 中的这个例子。电子表格中的错误消息通常隐藏在一个小图标后面。所以,无论你使用什么软件,一定要知道错误信息在哪里。

图片作者。

寻求帮助时遵循最佳实践

根据我的经验,数据社区在开发我的数据技能和解决具体问题方面帮助很大。几乎每一种处理数据的软件都有一个活跃的社区。寻求支持时,花些时间精心设计你的问题,以遵循最佳实践和你正在使用的资源的礼仪。请记住,有人自愿花时间来帮助你。以清晰有效地传达您的信息需求的方式提问。StackOverflow 有一个写好问题的极好的帖子,我强烈建议在发布到网站之前阅读。如果你偏离了这些建议,你可能会收到非常严厉的回应。

当你从其他网站寻求帮助时,这里有一些你可以遵循的一般提示。

1.清晰简明地解释你试图解决的问题。

2.指明您使用的软件——如果软件在本地运行,您还应该指明您使用的是 Mac、PC 还是 Linux。

3.说明你在解题时已经尝试过的。人们对那些只想快速得到作业问题答案的学生的帖子感到沮丧。证明你已经付出了努力。提供这种背景可以帮助支持人员从你停止的地方继续下去,使这个过程更有效率。

4.提供您收到的错误消息。

5.尽可能提供一个可复制的工作实例。你可以创建一个小玩具数据集来演示你的问题。但是,请始终确保您共享的文件不包含任何敏感数据。模拟或伪造的数据总是更好的选择。如果无法共享文件,请附上截图。当你没有完整的背景时,解决问题是困难的。

6.如果你在使用降价格式的在线论坛或讨论板上发帖,请使用降价格式!Markdown 很容易学习,也让帖子更容易阅读。这里有一个简单的降价指南让你开始行动。

后续步骤

当你继续发展你的数据技能时,小心不要因为得不到你想要的结果而感到气馁。当你调试问题和寻找解决方案时,你会学到很多东西。只要确保当你没有发现你在某些问题上取得进展时,你能接触到更广泛的数据社区。

物理学信息神经网络导论

原文:https://towardsdatascience.com/solving-differential-equations-with-neural-networks-afdcf7b8bcc4

PyTorch 实践教程

照片由达维德·马泽基Unsplash 拍摄

在过去的几十年里,人工神经网络已经被用于解决各种应用领域的问题,例如计算机视觉、自然语言处理等等。最*,在科学机器学习(ML)社区中出现了另一个非常有前途的应用:使用人工神经网络解决偏微分方程(PDEs),使用通常被称为物理信息神经网络 (PINNs)的方法。PINNs 最初是在开创性的工作[1]中引入的,今天它们不再局限于一个纯粹的研究主题,而是在行业中也获得了牵引力,足以进入 2021 年著名的 Gartner 新兴技术炒作周期。

偏微分方程在从流体动力学到声学和结构工程的许多工程和基础科学领域中起着至关重要的作用。有限元建模(FEM)方法是工业中普遍采用的标准求解器。尽管 FEM 方法很受欢迎,但它们也显示出一些局限性,例如它们对于大型工业问题的计算成本(主要是由于所需的网格尺寸)以及利用外部数据源(例如传感器数据)来驱动 PDEs 解的问题。

本文中讨论的 PINNs 方法被认为是 FEM 方法的一种有前途的替代方法,可以弥补其中的一些局限性。这种方法与标准的监督式 ML 截然不同。事实上,它不是纯粹依赖数据,而是使用 PDE 本身的物理属性来指导训练过程。已知的数据点可以很容易地添加到基于物理的损失函数之上,以加快训练速度。

这篇文章简单介绍了 PINNs 背后的主要概念,然后展示了如何从头构建一个 PINN 来求解一个简单的一阶常微分方程。为了构建神经网络,我将使用令人惊叹的 PyTorch 库。我们开始吧!

基于物理学的神经网络是如何工作的

为了更好地理解 PINNs,让我们从选择一个微分方程开始。为了简单起见,在本文中,我们将重点放在逻辑微分方程上,这是一个著名的一阶常微分方程,用于模拟人口增长:

模拟人口增长的一阶 logistic 微分方程

这里,函数 f(t) 表示随时间的人口增长率 t 并且参数 R 产生最大人口增长率,并且它强烈地影响解的形状。为了完全指定该方程的解,需要施加边界条件,例如在 t = 0 处,例如:

logistic 方程的边界条件。

尽管这个方程的解可以很容易地通过分析得出,但它代表了一个简单的游戏场来说明 PINNs 是如何工作的。下面解释的所有技术都适用于更复杂的常微分方程和偏微分方程。然而,对于更复杂的场景,为了获得良好的收敛性,将需要额外的技巧。

PINNs 基于 NNs 的两个基本属性:

  • 已经正式证明[2]神经网络是通用函数逼*器。因此,一个神经网络,只要它足够深和有表现力,就可以逼*任何函数,因此也可以逼*上述微分方程的解。
  • 使用自动微分 (AD)计算神经网络输出相对于其任何输入(当然还有反向传播期间的模型参数)的导数既简单又便宜。AD 实际上是最初让神经网络如此高效和成功的原因。

这些都是很好的特性,但是我们怎样才能让神经网络真正学习到解决方案呢?PINNs [3,4]背后有一个令人惊讶的简单但非常聪明的想法:我们可以构造 NN 损失函数,使得当最小化时,PDE 自动得到满足。换句话说,最重要的损耗贡献取为微分方程的残差,如下所示:

微分方程残差。

其中 f _NN(t) 是具有一个输入的神经网络的输出,其导数使用 AD 计算。很容易看出,如果神经网络的输出符合上述方程,实际上就是在解微分方程。为了计算来自 DE 残差的实际损耗贡献,需要在方程域中指定一组点(通常称为同位点,并评估均方误差(MSE)或其他损耗函数,作为所有选定同位点的*均值:

由一组配置点上*均的微分方程残差给出的损失贡献。

然而,仅基于上述残差的损失并不能确保方程有唯一的解。因此,让我们以与上述完全相同的方式将边界条件添加到损失计算中:

加入 MSE 损失的边界条件损失贡献。

因此,最终损失简单地写道:

在优化过程中,这被最小化,并且 NN 输出被训练以考虑微分方程和给定的边界条件,从而逼*最终的 DE 解。

PINN 框架非常灵活,使用上面介绍的思想,人们可以添加更多的边界条件,包括更复杂的条件,例如对 f(x) 导数的约束,或者使用具有多个输入的 NN 处理时间相关和多维问题。

现在让我们看看如何用 PyTorch 构建一个简单的神经网络来构建这样的损失函数。

从零开始建设 PINN

神经网络

PINN 的主要成分当然是神经网络本身。在这篇文章中,我们选择了一个基本的神经网络架构,它由一堆带有标准激活函数的线性层组成。由于我们有一个独立变量,即时间 t ,NN 应该将一个特征作为输入,并返回一个输出,该输出表示给定当前模型参数的最佳 DE 解决方案猜测。下面是这种架构的 PyTorch 实现,其中神经元和隐藏层的数量作为输入(超级)参数。

PyTorch 实现了一个简单的 PINN 架构。

PINNs 是一个非常活跃的研究领域,并且已经设计了更加复杂并且通常是针对问题定制的神经网络架构。关于这些架构的讨论超出了这篇介绍性博客的范围。

建立损失函数

既然我们已经定义了我们的通用函数逼*器,让我们建立损失函数。如前所述,这是由 DE 残差项和边界条件项组成的,其中 DE 残差项用作物理意义上的正则化,边界条件项驱动网络在无限可能的解中收敛到所需的解。

首先,需要选择一组协同定位点。因为我们正在解决一个非常简单的问题,所以我们可以在时域中选择一个均匀间隔的网格:t = torch.linspace(0, 1, steps=30, requires_grad=True)或者我们可以在优化器的每次迭代中从时域中随机采样新的同位点。对于更复杂的问题,同位点的选择极其重要,并且会强烈影响结果。

为了计算模型输出及其导数,我们使用了最新的functorch软件包,该软件包通过将参数从模型本身解耦,使 PyTorch 模型完全起作用。关于functorch的全面介绍请参考这篇文章

PyTorch 的函数方法在处理 NN 输出相对于其输入的(高阶)导数时非常方便,PINNs 通常就是这种情况。在下面的代码中,我们首先使用functorch使上面的模型函数化,然后我们生成正向传递和梯度计算的函数形式。

注意,对grad的调用可以无限制地组合,从而允许计算关于输入的任意阶导数。使用上面定义的函数,MSE 损失可以很容易地计算为每个同位点的 DE 贡献和边界贡献之和。注意,给定正向传递和衍生的函数性质,损失函数也必须将模型params作为输入参数:

就是这样!上面定义的自定义损失确保在训练过程之后,NN 将逼*所选微分方程的解。现在,让我们看看它是如何工作的。

用 PINNs 解微分方程

PyTorch 目前没有提供支持上述 API 函数的优化器。然而,令人惊叹的 PyTorch 社区来拯救我们,人们可以使用[torchopt](https://github.com/metaopt/torchopt) 获得大多数 PyTorch 优化器的功能版本。这个库的界面非常直观,任何 PyTorch 用户都会很快熟悉它。我们的功能模型的基本训练循环如下所示。请注意,我们在每次迭代中随机采样解域。

让我们看看一些结果。我们使用学习率为 0.1 的 Adam 优化器,在每个优化时期从域中均匀采样 30 个训练点。考虑到所选微分方程的简单性,100 个历元足以几乎完美地再现最大增长率设置为 R = 1 的分析结果:

用物理信息神经网络方法求解逻辑斯谛方程。还显示了一组随机训练点。

在上面的图中,在 100 个均匀间隔的点上评估解决方案,每个时期的损耗变化(其中 y 轴是对数标度)如下所示:

使用 Adam 优化器的损失函数值的演变。该图在 y 轴上有对数刻度。

这里我们解决了一个非常简单的一维问题。对于更复杂的方程,收敛就不那么容易了。特别是对于与时间相关的问题,过去几年已经设计了许多有用的技巧,例如将解域分解为使用不同神经网络求解的不同部分,对不同损失贡献进行智能加权以避免收敛到*凡解等等。我将在以后的帖子中介绍其中的一些技巧,敬请关注。

这篇文章对物理学神经网络进行了简单的高级介绍,这是一种解决(偏)微分方程的有前途的机器学习方法。虽然需要进一步的进展才能使 PINNs 常规应用于工业问题,但它们是一个真正活跃和令人兴奋的研究领域,代表了标准微分方程求解器的一个有前途的替代方案。

这篇帖子的完整代码可以在 GitHub 上找到。如有任何问题或评论,请随时通过 LinkedIn 联系我。

参考资料和进一步阅读

[1] Raissi M,Perdikaris P,Karniadakis GE .物理学信息神经网络:一种深度学习框架,用于解决涉及非线性偏微分方程的正向和反向问题。计算物理学报(2019)

[2] Kurt Hornik,Maxwell Stinchcombe 和 Halbert White,多层前馈网络是通用逼*器,神经网络 2 ,359–366(1989)

[3] George Em Karniadakis,Ioannis G. Kevrekidis,,Paris Perdikaris,Wang 和刘洋,物理学通知机器学习,《自然评论物理学》 3 ,422–440(2021)

[4]本·莫斯利,那么,什么是物理信息神经网络呢?—本·莫斯利

解决 IBM 的量子开放科学奖

原文:https://towardsdatascience.com/solving-ibms-quantum-open-science-prize-806dc982a47f

一个实用的方法来确定是否从事量子计算的职业

量子机器学习要不要入门?看看 动手量子机器学习用 Python

量子计算可能是 21 世纪最具颠覆性的技术。这是一种基于量子力学的不同形式的计算,它有望解决我们用经典计算机无法解决的问题。其中一个问题是分子的模拟。

这是 IBM 量子开放科学奖的挑战。他们想让我们用 trotterization 模拟一个三粒子分子。

分子不是静态的,而是进化的动态系统。所以,悬而未决的问题是:它们是如何进化的?

作者图片

Trotterization 是回答这个问题的最先进的方法。它利用了这样一个事实,即时间片的快速交替收敛到给定的哈密顿量,代表量子态的总能量。

这似乎只有拥有博士学位的物理学家才能理解。

作者图片

没错:量子计算并不完全是一个适合初学者的领域。但是,你不需要成为物理学家(或数学家)。所以,不要让任务描述把你吓跑了。

仔细观察即将到来的挑战会发现一个惊喜!

实际问题不是使用 Trotterization 模拟三粒子系统的海森堡模型哈密顿量。事实上,IBM 提供了这种模拟的工作示例。

相反,问题是当前的量子计算机容易出错,并且对环境中的噪声敏感。因此,即使我们知道模拟分子的算法,我们仍然需要应对计算误差。

不幸的是,我们不能简单地纠正这些错误,因为我们没有足够的资源。我们能做的最好的事情就是减少误差对计算的影响。

本质上,IBM 挑战说:

这里有一个非常酷的模拟粒子的量子算法。但是在我们现在的量子设备上不行,因为噪音太大。请帮助我们想出一个在我们嘈杂的设备上工作的算法。

当然,我们可以用不同的方式应对这一挑战。例如,我们可以致力于改进量子化算法和/或使用备用量子位。IBM 希望我们使用的量子计算机有七个量子位,但模拟算法只需要三个。

我们在解决方案中使用的另一种策略是在后处理步骤中减少错误。这种策略旨在基于有噪声的测量和外推模型来预测可观测值的无噪声值。

作者图片

诸如零噪声外推和 Clifford 数据回归等策略已经显示出前景,但是当然这种方法的性能取决于外推模型。

一般一个模型有一个简单的形式: y=f(x) 其中 x 为自变量(有噪值) f 为模型, y 为因变量(无噪值)。如果 xy 是单号,我们可以用我们高中的知识创造 f 。例如,如果 f 是一个线性函数,我们可以通过线性回归来创建模型——画一条最适合数据点的线。

作者图片

但是 IBM challenge 根据量子态断层扫描的保真度来评估我们的提交。这是一个基于一系列测量计数重建量子态的过程。

Qiskit 中的测量计数以字典的形式出现。这些是键-值对,其中键表示测量的状态,值表示我们观察该状态的次数。一个三量子位系统有八种不同的状态,从 000,001 到 111。

尽管可观测值的期望值和测量值紧密相关,但从计算角度来看,它们是不同的东西。在这篇文章中,我们更详细地观察了可观察到的现象。

这对外推模型的形式有影响,因为我们有八个输入和八个输出值——我们需要一个多维模型。

我们创建的模型由一系列修改器组成。考虑上图中指定的一个量子位的例子。无噪声计数为0: 5001: 500。噪声计数为0: 4121: 588。对于每个可能的测量,我们计算一个修正值,将噪声值变为无噪声值。这种情况:0: 500/412=1.211: 500/588=0.85。给定噪声计数,我们可以使用修饰符来计算无噪声计数。

因此,当我们再次运行电路并测量0: 4181: 592时,修改器会告诉我们真实值是0: 418*1.21=5061: 592*0.85=503。非常接*!

上面的例子有一个重要的先决条件。必须能够计算无噪声计数来计算修正因子。事实上,我们可以很容易地在经典电脑上模拟一个三量子位元系统。因此,我们可以计算无噪声计数,测量噪声计数,并计算修改量。有了这样的修改器,我们可以获得 90%以上的保真度!

但是,我们会错过挑战的目的。对量子计算机的大肆宣传源于这样一个事实,即它们可以计算经典计算机不能计算的东西。如果我们可以经典地计算一些东西,那么经历使用量子计算机的挣扎根本没有意义。在这种情况下,我们不需要首先处理错误。

所以,我们需要想出不同的修饰词。

trotterization 算法由许多 trotter 步骤组成。这些是我们用来划分系统进化的时间片。我们使用的步骤越多,模拟就越精确,但也容易出错。

每个 trotter 步骤由三个子电路组成,分别命名为 XX、YY 和 ZZ。

因此,整个算法包含 3 倍 trotter 步骤的子电路。对于 12 个快步,有 36 个子电路。现在,我们要做的是根据每个电路覆盖两个子电路的无噪声计数来计算修改量。

这些电路包含

  • 一个 XX 和一个 YY 支路
  • 一个 YY 和一个 ZZ 支路
  • 一个 ZZ 和一个 XX 支路

这三个回路加在一起的长度相当于两个快步。因此,如果我们计算 6 次(trotter 步数除以 2),那么我们覆盖的门与整个电路相同。

因此,八个状态中的每一个都有 3*6=18 个修饰符。这 18 种改性剂的乘积形成最终改性剂。

使用这些修改器,我们得到了 12 个快步的大约 50% 的保真度。因此,我们以 30%的保真度满足参加 IBM 量子开放科学奖的参赛标准。

凭借 96%的无噪声保真度和 11%的绝对保真度,我们实现了 46.8%的总体误差降低。

这种方法仅仅建立在后处理的基础上。我们不使用任何额外提供的量子位。虽然我们肯定错过了一些潜力,但它允许我们将这种方法与其他团队提出的解决方案结合起来。

这篇文章结束了我们三个月前开始的旅程。我们开始一周接一周地剖析这个挑战。我们学到了很多关于量子错误缓解方法的知识,并且我们编写了 Python 代码——很多!

起初,我不确定我们是否能达到 30%保真度的准入标准。我更高兴的是我们达到了 50%。

你可以在这里找到这篇投稿。所有代码都是开源的,可以在 GitHub 上获得。

量子机器学习要不要入门?看看 动手量子机器学习用 Python

在这里免费获得前三章。

解决多臂强盗问题

原文:https://towardsdatascience.com/solving-multi-armed-bandit-problems-53c73940244a

土匪,图像由 Dall-E 2。

一种强大而简单的应用强化学习的方法。

强化学习是一个有趣的领域,它正在游戏、机器人和金融市场中成长并取得巨大的成就。一个很好的事情是,你不必从深度 Q 学习代理开始!对于某些问题,基于强化学习的原理实现一个简单的算法就足够了。在这篇文章中,我将深入研究多臂土匪问题,并用 Python 构建一个基本的强化学习程序。

强化学习

我们先来解释一下强化学习。这有点像在现实生活中学习:当一个蹒跚学步的孩子清理他的玩具时,他的妈妈很高兴,她给他一颗糖。但是如果一个蹒跚学步的孩子打了他的妹妹,他的妈妈会生气,他会受到惩罚。蹒跚学步的孩子采取行动,并因其行为受到奖励或惩罚。

你可以把蹒跚学步的孩子比作强化学习代理。在强化学习系统中,代理与环境交互。代理选择一个动作,并以状态(或观察)和奖励的形式接收来自环境的反馈。奖励可以是正面的,也可以是负面的,比如糖果和惩罚。这种循环一直持续到代理以终止状态结束(游戏结束)。然后新一集的学习就开始了。代理的目标是在一集里最大化总的回报。示意性地看起来是这样的:

强化学习:代理通过选择动作和接收观察(或状态)和奖励来与环境交互。图片作者。

什么是多臂土匪问题?

想象一下,你今晚想和一个好朋友去餐馆吃饭。轮到你选择餐馆了。当然,你想要一个有好食物(和好服务,但是为了简单起见,我们只看食物)的餐馆。你选择你最喜欢的餐馆,因为你知道你喜欢那里的食物吗?或者你会选择一家新餐馆,因为这家新餐馆的食物可能会更好?选择一家新餐馆,你也有更高的风险得到负面的回报(糟糕的食物)。

这是一个多臂土匪问题的例子。在每一个时间点,你都必须做出选择(也就是行动),你可以决定利用你的经验,选择你熟悉的东西。但是你也可以探索新的选择,拓宽你的经验(和知识)。

餐馆的经历。在每个时间点,你选择一家餐馆。绿点代表积极的体验,红点对应消极的体验。你会选择积极体验比例最高的餐厅,还是尝试一家新餐厅?图片作者。

多臂强盗这个名字来源于赌场的老丨虎丨机,一个独臂强盗对应一个老丨虎丨机。因此,对于多台老丨虎丨机,您需要知道拉动哪个杠杆才能获得最大金额。你可以决定尝试一个新的老丨虎丨机,探索,或者你可以利用你过去的经验。探索和利用对于任何强化学习问题都是重要的概念,在接下来的段落中会有更多的介绍。

一个双臂强盗?艾莉森·巴特利在 Unsplash 上的照片

现实生活中的多武装土匪

除了选择餐馆或吃角子老丨虎丨机,还有其他现实生活中的应用,多臂土匪是有用的。它们已经证明了自己在推荐系统和异常检测等方面的价值。以下是一些实际例子:

评选最佳广告

A/B 测试在网络广告中很常见。在 A/B 测试中,人们会看到不同的广告。点击量高得多的将是最成功的,其他的将被删除。用于选择最佳广告的多臂土匪工作如下。行动是选择一个广告。在开始时,选择广告的概率是相等的(探索阶段),但是将被改变成有利于具有更高点击率的广告(开发阶段)。所以一个更成功的广告会被更多的展示,最终成为唯一的一个。这种方法的优点是你可以在过程的早期就开始利用你的知识,如下图所示:

测试了三个广告,看哪一个表现最好。多臂强盗在过程的早期收到选项 A 的积极反馈,并开始利用他的知识(从第二周开始)。图片作者。

临床试验

华法林是一种抗凝血药(它可以防止血凝块)。很难正确给药。适当的剂量因人而异。如果初始剂量不正确,这是很危险的,可能会导致中风或内出血。一个(上下文相关的)多臂土匪在这方面帮了大忙,他学习并给病人分配了适当的初始剂量。

推荐系统

在处理向用户推荐最感兴趣的产品时,多臂土匪可以帮忙。这有点像 A/B 测试:你想向用户展示他们感兴趣并愿意购买、放在购物图表中或点击的产品。如果用户点击了一个产品,这意味着这是一个好的产品展示给这个用户(或积极的回报)。通过将此应用于相似的顾客群,就有可能找到向一群顾客展示的最佳商品。

还有更多的应用。甚至有可能使用多臂土匪进行更好和/或更快的机器学习!它们用于算法选择、超参数优化(作为贝叶斯优化的改进)和聚类。这显示了该技术的多功能性。

布鲁诺·凯尔泽Unsplash 上拍摄的照片

建立多武器强盗代理

当处理一个多武装匪徒问题时,有什么好的方法可以探索和利用?有不同的技术。首先,让我们看看 MABPs 的一些关键概念。

行动价值函数与后悔

让我们看看吃角子老丨虎丨机的例子。如果 3 台机器的奖励为 1 的概率分别为 0.16、0.21 和 0.65,那么采取的最佳行动是概率为 0.65 的机器(最高)。

老丨虎丨机示例。图片作者。

多臂强盗不知道概率,所以首先它会探索,直到它发现只拉老丨虎丨机 3 的杠杆就能最大化整体回报(概率 0.65)。一个行动的价值等于预期报酬,即等于该行动的概率:

一个行动的价值等于该行动的预期回报,等于该行动的概率。

因此,3 台老丨虎丨机的动作值分别为 0.16、0.21 和 0.65。

遗憾是多臂大盗没有得到的累积奖励,因为探索。吃角子老丨虎丨机示例 100 轮的最佳累积奖励将是 0.65 * 100 = 65(仅选择最好的机器)。但是在探索过程中,多臂强盗也会选择其他机器。所以累积回报可能是 0.65 * 60 + 0.16 * 15 + 0.21 * 25 = 46.65。这样的话,后悔就等于 65-46.65 = 18.35。

多武器土匪特工

多臂强盗特工的目标是最大化一集的总奖励。代理可以通过不同的方式探索和利用,并获得不同程度的成功。下面,我将讨论其中的一些。

随机最优代理人
一个不太聪明的代理人会在随机选择每个行动。不那么聪明,因为它只探索,不使用发现的知识。

相反的是一个最优代理,它每次都选择最优的行动。这是不现实的,因为如果我们知道每个强盗成功的概率,我们总是会选择最高的一个,没有必要实现一个代理。这是一个比较(现实的)代理表现如何的好方法。下面描述的所有代理的性能介于随机代理和最优代理之间。

随机最优代理。随机代理随机选择一个强盗,而最优代理只选择成功概率最高的强盗。随机代理给出一个基线。图片作者。

Epsilon agents 处理勘探和开发的另一种方式是通过选择“勘探值”Epsilon。如果我们选择ε等于 0.2,大约 20%的行动将被随机选择(探索)。对于其他 80%的动作,代理选择目前为止最好的动作(剥削)。它通过选择行动的最高*均奖励来做到这一点。这个代理叫做ε贪心

ε贪婪代理。图片作者。

对常规 epsilon greedy 智能体的一个改进是在开始时探索更多,随着时间的推移探索更少。这很容易实现,通过添加一个衰变参数,这给了我们ε贪婪衰变代理。如果该参数等于 0.99,则每次迭代,ε的值以因子 0.99 减小。这确保了会有一些探索,但随着时间的推移会越来越少,这是有意义的,因为代理获得了知识,当他的知识增加时,探索的需求就会减少。

因为上面描述的 epsilon 代理在很大程度上利用了 T17,甚至在开始时,还有另一种可能的改进。通过将每个动作的初始奖励值设置为可能的最大奖励值(例如,当奖励可以是 0 或 1 时,设置为 1),代理将更喜欢它没有尝试过这么多的动作,因为代理认为奖励很高。这个代理是乐观ε贪婪(带衰变)。这确保了在开始时防止利用不太好的动作。

上置信区间智能体
上置信区间智能体是知道如何处理勘探和开发的智能体。它使用所有可能盗匪的*均奖励进行剥削,就像艾司隆特工一样。然而,一个重要的区别是它如何处理探索。这是通过添加一个参数来实现的,这个参数使用了到目前为止的总时间步长,以及某个土匪被选中的次数。例如,当我们处于时间步骤 100 时,代理仅选择一次动作,该参数为高,确保代理将选择该动作。这样,置信上限代理自然地探索它不常探索的行为,同时也利用他的知识。以下是置信上限代理的动作选择公式:

置信上限代理的动作选择。图片作者。

伯努利·汤普森智能体 最终的智能体使用了与之前的智能体完全不同的方法,尽管直觉感觉是一样的。当一个强盗被选中的次数越多,汤普森的代理人就越有信心得到奖赏。对于每个土匪,代理跟踪成功的数量 α 和失败的数量 β 。它使用这些值为每个土匪创建一个概率模型。使用 beta 分布从所有盗匪的分布中采样一个值,汤普森代理选择这些采样值中的最高值。

为了更好地理解 Bernoulli Thompson 代理,让我们看看下面的强盗:

代理从等于 1 的 α 和等于 1 的 β 开始,对于所有盗匪,这等于均匀的β分布(被选择的概率相等):

5 次试验后,分配、行动和奖励如下所示:

机器 0 和 1 具有相同的分布,它们都被选择两次,一次成功,一次失败,而机器 2 被选择一次,奖励为 0。

20 次试验后,如果只看成功率,机器 0 看起来比机器 1 好:

如果我们快进到 500 个动作,代理非常确定机器 1 是最有回报的一个,并且分布的*均值几乎等于机器成功的真实概率(0.25,0.5 和 0.1):

代理的比较

在下面的图表中,对代理进行了测试。对于不同数量的试验和盗匪,该实验可以产生不同的结果。很明显,具有衰减的乐观 epsilon 代理、置信上限代理和 Thompson 代理优于其他代理。这正是我们所期望的,因为他们比其他代理更聪明地处理探索。

六个强盗的实验结果(除了随机和最优代理),这是 50 轮 2000 次迭代的*均累积奖励。点击放大。图片作者。

Python 代码

你可以在 my Github 上找到不同的多臂土匪代理的实现。有一个测试脚本(笔记本文件)对它们进行比较并绘制结果。

多臂强盗的缺点

基本多兵种土匪都想一直在同一个动作中选择。多臂强盗无法应对不断变化的环境:如果老丨虎丨机的概率发生变化(或者你最喜欢的餐馆换了一个新厨师),多臂强盗需要从头开始学习(或者你需要增加探索参数)。

当你有一个以上的状态时,你应该切换到上下文土匪。情境强盗试图根据环境找到最佳的行动,因此他们可以与不同的状态一起工作。

结论

多臂匪是应用强化学习的最基本方式之一。它们被广泛应用于不同的领域:金融、广告、健康以及改善机器学习。多兵种强盗特工可以用许多不同的方式进行探索和剥削。他们可以使用随机技术,如 random 和 epsilon 代理。或者他们可以使用更聪明的方法,比如乐观 epsilon 代理、置信上限代理和 Bernoulli Thompson 代理。最终,这一切都是为了发现最佳行动,并最大化整体回报。

在赌场,你会选择哪个代理人做帮手?🤩

卡尔·劳Unsplash 上拍摄

相关文章

用 IBM Watson NLP 快速解决 NLP 问题

原文:https://towardsdatascience.com/solving-nlp-problems-quickly-with-ibm-watson-nlp-42fa50900a47

让我们探索 IBM Watson NLP 提供的一些现成的 NLP 模型

来源: Pixabay

作为数据科学家,在开始开发模型之前,通过尝试查找和下载开源模型或自己开发模型,将所有这些方便地用几行代码实现不是很好吗?这正是我今天在这篇博文中要向你展示的。使用 Watson NLP ,您可以获得大量 NLP 用例的最先进的预训练模型,这些模型可以让您在几个小时内启动并运行。如果需要的话,这些模型还可以用定制的领域特定知识进行再培训。

在这个例子中,我们将采用情感分析的 NLP 用例;我们需要做三件事。

  1. 提取文本的情感。
  2. 提取自定义目标提及。
  3. 提取目标词的情感。

对于这个例子,我们将使用 IMDB 上关于《权力的游戏前传,龙之家》的评论。以下是评论。

这种情况经常发生在一部非常成功的电视剧取得成功后制作续集或前传的时候……一旦设定了基准,就很难遵循,如果不是不可能的话……这里的情况就是如此..这个规则也有例外,比如旧的原版系列的《太空堡垒卡拉狄加》翻拍版,其中的继任者在每个指标上都超过了原版..因此,它发生了,但不是在这里与 GOT…马上,最初的开放场景与龙和年轻的女孩谁是我想,这显示了版本的 Daneerys,所有这些场景显示了一个 CGI 严重缺乏相比,GOT…颜色 palete 看起来褪色,缺乏细节,真的,它看起来像一个电脑渲染的背景,不会愚弄任何人…是的,这是电脑渲染,但一个好的 CGI 不会引起人们的注意..它会让你相信…但在这里不是这样的…开场中的龙看起来很狂野,因为原版中的龙是以最高级别的电影质量呈现的…坦率地说,这里的龙看起来很可怜,缺乏细节,威胁或纯粹的存在…它不存在…在这场表演中有 10 条龙,也许这是一窝中最矮小的,但他们应该做得更好..至于表演,最初我对演员阵容感到失望,正如许多人评论的那样,我没有感觉到与这里的任何人有直接的联系……在费力地看完这部剧的前半部分后,我要说的是,原版《GOT》的氛围和氛围的元素在那里,并注入了一些场景, 尤其是那些小公主和她的父亲以及父亲和达蒙的对话…它不是完全不存在,它是存在的,但以一种更轻的预算节目的形式,演员、效果和布景较低…它不是那么糟糕,你不能进入它,我建议暂停肯定会来的判断,直到你在系列中有几个节目…几乎没有任何新的系列不需要几个节目 大部分新演员感觉僵硬和呆板,因为演员们刚刚进入角色,还没有完全融入角色,没有达到令人信服的程度…是的,这部剧也在做 wokeness 主题和政治正确的多样性舞蹈,就像这些天的所有事情一样 ..这就是它,它是新世界意识的一部分,它注入了一切…当作家和演员试图符合并使故事符合这些当前趋势时,它看起来太“强迫”而不是故事情节的自然表达…也就是说,如果你喜欢 GOT,我也喜欢,那么这当然值得一看,看看它是如何演变的…当一个最喜欢的系列被进一步表达时,为什么要抱怨呢?享受它的本来面目,那些不想看到它的人,打开别的东西……就这么简单!

对于这个练习,我们将需要语法分析模型(处理基本的自然语言处理操作,如标记化、词汇化、词性标注等),一个基于规则的提及模型,检测目标关键字在哪里被提及,以及一个目标情感模型。

下载并加载模型。

import watson_nlp
from watson_nlp.toolkit.sentiment_analysis_utils.output.document_sentimet import predict_document_sentiment# download and load the Syntax model
syntax_model = watson_nlp.download_and_load(‘syntax_izumo_en_stock’)# download and load the Sentence sentiment model. 
sentiment_model = watson_nlp.download_and_load(‘sentiment_sentence-bert_multi_stock’)# download and load the Target sentiment model
targeted_sentiment_model = watson_nlp.download_and_load(‘sentiment_targeted-cnn_en_stock’)

目标情感

我们将首先配置基于规则的模型,以提取评论中提到的目标。我们将设置目标颜色、龙、权力的游戏(GoT)、CGI 和演员。这可以通过创建一个包含两列标签条目的 CSV 文件来完成。条目将包含文本中出现的关键字。这需要是单词的基本版本,因为算法也进行词条匹配。比如说;如果您将其设置为鼠标,该算法也将检测鼠标的提及。Label 将包含您要对其下的目标进行分组的标签。比如说;关键词猫和狗可以在同一个标签动物下。

import os
import watson_nlp
module_folder = “NLP_Dict_Module_1” 
os.makedirs(module_folder, exist_ok=True)# Create a table dictionary
table_file = ‘features.csv’
with open(os.path.join(module_folder, table_file), ‘w’) as features:
 features.write(“\”label\”,\”entry\””)
 features.write(“\n”)
 features.write(“\”COLOR\”,\”color\””)
 features.write(“\n”)
 features.write(“\”DRAGON\”,\”dragon\””)
 features.write(“\n”)
 features.write(“\”Game of Thrones\”,\”GOT\””)
 features.write(“\n”)
 features.write(“\”CGI\”,\”CGI\””)
 features.write(“\n”)
 features.write(“\”ACTOR\”,\”actor\””)
 features.write(“\n”)# Load the dictionaries
dictionaries = watson_nlp.toolkit.rule_utils.DictionaryConfig.load_all([{
    'name': 'feature mappings',
    'source': table_file,
    'dict_type': 'table',
    'case': 'insensitive',
    'lemma': True,
    'mappings': {
        'columns': ['label', 'entry'],
        'entry': 'entry'
    }
}])# Train the rule based model on the above targets
custom_dict_block = watson_nlp.resources.feature_extractor.RBR.train(module_folder, 
language='en', dictionaries=dictionaries)

既然提取目标提及的规则库模型已经被训练,我们称之为情感分析模型。请注意,模型需要按顺序调用,因为一个模型的输出需要成为另一个模型的输入。我们从语法模型开始,然后是提及模型,最后是目标情感模型,它将语法和提及模型的输出作为输入。

def **get_lemma**(target):
    “””
    Gets the lemma of a target text.
    “””
    lemmas = [token[‘lemma’] if token[‘lemma’] != “” else token[‘span’][‘text’] for token in syntax_model.run(target, parsers=(‘token’, ‘lemma’)).to_dict()[‘tokens’]]
    return “ “.join(lemmas)def **get_text_label**(text):
    “””
    Gets the label of a text from the target feature list csv. 
    “””
    text = get_lemma(text)
    try:
        label = feature_df[feature_df[‘entry’].str.lower() == text.lower()][‘label’].values[0]
    except:
        label = None
    return labeldef **extract_mentions**(text):
    “””
    Extracts the spans where the target features have been mentioned in the text.
    “””
    mentions = defaultdict(list)
    for mention in custom_dict_block.run(text):
       mentions[get_text_label(mention.text)].append((mention.begin, mention.end))

    return mentions.values()def **target_sentiment_of_line**(text):
    syntax_result = syntax_model.run(text, parsers=(‘token’, ‘lemma’))

    targetMentions = extract_mentions(text)
    targeted_sent_result = targeted_sentiment_model.run(syntax_result, targetMentions, show_neutral_scores=False)
    return targeted_sent_result

我们现在可以将文本传递给上面定义的target _ opinion _ of _ line函数,并获得下面的结果(我们得到一个 JSON 响应,但为了可读性,我将它格式化为 excel 文件)。

target _ opinion _ of _ line函数的输出,Excel 格式

对于每个目标,我们得到一个总分数,以及检测到目标的每个句子的分数。例如,在四个句子中检测到 GOT,并且总体情绪是积极的。然而,第一次提及 GOT 被检测为负面,其余提及为正面。

情感得分可以在-1 和 1 之间。-1 是最消极的情绪,1 是最积极的情绪,而 0 是中性的。值-0.4 不如值-0.98 负。类似地,值 0.3 不如值 0.99 积极

总体情绪

我们也通过调用如下所示的句子情感模型来获得文本的整体情感。

def **sentiment_of_line**(text):
    # Run the syntax model on the input text
    syntax_prediction = syntax_model.run(text) # Run the sentiment model on the result of syntax
    sentiment_result = sentiment_model.run_batch(syntax_prediction.get_sentence_texts(), syntax_prediction.sentences) # Aggregate the sentiment of all the sentences in the text
    document_sentiment = predict_document_sentiment(sentiment_result, sentiment_model.class_idxs, combine_approach="NON_NEUTRAL_MEAN") return document_sentiment

在将文本传递给函数 perspection _ of _ line 时,我们得到的总体情绪是负面的,得分是-0.424164。

结论

总之,对我来说,Watson NLP 的亮点是能够在工作中快速开始使用 NLP 用例,而不必担心收集数据集,从头开发模型。这有助于快速起床和跑步。如果需要,我们可以很容易地用领域特定的数据重新训练模型。

用 120 行代码求解诺诺姆图

原文:https://towardsdatascience.com/solving-nonograms-with-120-lines-of-code-a7c6e0f627e4

从左到右怎么走?这篇文章解释了这一切。图片作者。

拼图,组合和解决方案 gif。

在这篇文章中,通过计算每一行的选项,展示了一种没有任何错误的解决 Nonograms 的方法。这不是最有效的解决方法,在文章的最后你可以找到更多关于最快方法的信息。在我看来,这是最有趣的方式,因为它没有犯错的余地。作为奖励,你会学到更多关于计算问题的知识!让我们跳进来吧!

游戏解说

一个 Nonogram(又名 Griddler,由数字绘制,韩杰或 Picross)是一个你试图揭示一个图片的难题。这是一个有简单规则的逻辑难题。你有一个方格,每个方格都应该变成黑色或白色。网格边上和顶部的数字显示了应该在每一行和每一列中的黑色方块组。

求解过程

让我们从一个空的 Nonogram 开始:

空 Nonogram。图片作者。

第三列的值为 5、2 和 6。这表示有五个、两个和六个填充方块的集合,按此顺序,在连续的集合之间至少有一个空白方块。因为 5 + 1(白方)+ 2 + 1(白方)+ 6 等于 15,这条线很好解,只有一个可能的解:

第三列只有一个可能的解决方案,因为值和空方块加起来是 15。图片作者。

圆点是帮助标记,表示这些方块应该是空的。

我们继续。最下面一行包含一组 14 个黑色方块,因为整个行有 15 个方块长,所以我们可以填充除第一个和最后一个方块之外的所有方块:

填满最后一行的方块:因为应该有一组 14 个填满的方块,我们可以确定填满这些方块。图片作者。

我们可以使用这些实心方块,因为它们与底部的列值相对应:

使用底部行的黑色方块:它们只能属于底部的列值。所以第二列有五个黑色方块,第四列有三个,依此类推。图片作者。

明白了吗?通过使用一些逻辑很容易解决这个 Nonogram!你可以自己试试这个或者继续看,让电脑帮你解决。

剧透提示:最后会有一张 gif 展示这个 Nonogram 的求解过程。

以编程方式求解 Nonogram

在了解了游戏规则之后,如何才能让计算机用数学解出 Nonograms 呢?

该计划包括三个步骤。第一步只发生在最开始,也是最复杂的一步,而第二步和第三步很容易理解,会一直重复,直到谜题解开。

第一步。计算每行和每列的所有选项

我们首先计算所有选项,为每一行和每一列选择黑色方块(下一节将解释如果事情变得更复杂,该如何做)。这只会发生在游戏开始的时候。假设我们有一排五个正方形,我们想用一组三个正方形来填充它:

一组 3 个黑色方块的所有选项。图片作者。

这给了我们右边的三个选项。

第二步。填充只有一种可能性的方块

如果您查看某一行的选项,发现某个正方形只有一种可能性,因为在所有选项中正方形都是黑色的,或者在所有选项中正方形都是白色的,所以正方形只能用这种颜色填充。这个例子说明了这一点:

将 3 个一组放置在一排 5 个正方形中,在左侧您会看到放置该组的所有选项,在中间您会看到中间的正方形被标记,因为对于所有选项,该正方形都被填充。这个正方形应该是黑色的。图片作者。

第三步。从相应的行或列中删除选项

在填充一个正方形(或使其变成白色)后,我们可以查看相应的行或列,并删除该正方形是另一种颜色的所有选项。因为这些选择已经不可能了。示例:

如果我们确定最后一个正方形(我们发现由于列值的原因它应该是白色的),那么第三个选项就不再可能了,所以我们将它从这一行的选项中删除。图片作者。

如何计算每行每列的所有选项?

在上面的例子中,选项的计算很简单,我们将三人组向左滑动,直到到达行尾。但是,如果我们采用一个更复杂的值列表,并试图找到所有可能的选项,会发生什么呢?另一个例子:

将 3、2 和 6 个黑色方块放在一排 15 个方块中的所有选项。图片作者。

在上面的例子中,我们将组 3、2 和 6 排成一排,每排有 15 个方块。我们需要至少 13 个方块来组合这些组:
6 + 1 个白方+ 3 + 1 个白方+ 2 = 13 这意味着我们还剩 2 个空方块(15 减 13 = 2)。

值得注意的一件有趣的事情是,对于总选项的数量,你只需要知道剩下的空方块的数量和你得到的组的数量。直观的证明:

取三个组,每组一个,最后有两个空方块,我们得到的选项数与组值为 3、2 和 6 的例子中的选项数完全相同。你看到模式了吗?图片作者。

好的,我们有进展了!在本例中,我们看到,如果我们有三组值(左边是 6,2,3,右边是 1,1,1),还有两个空方块,那么可能的选项总数是 10。

如果我们忘记了组与组之间的白色方块(无论如何,这些方块都应该在那里),我们可以使用组合来计算选项的数量。我们得到了 5 个位置(3 组+ 2 个空方块),需要从 5 个中选择 3 个(我们要放置 3 组)。在这里你可以找到更多关于组合的信息。使用 python 和 itertools 很容易计算出所有的组合。

from itertools import combinationsopts = combinations(range(n_groups+n_empty), n_groups)

从组合到选项,再到选项的可视化。图片作者。

这也适用于 3、2、6 值,只是我们现在在以下位置放置了更多的 1:

组变得有点大,但是组合和以前完全一样。

就是这样!这适用于所有的价值观。我们只需要剩下的空方块的数量和组的数量来使用组合创建所有可能的选项。

解决方案 gif

在创建这个程序之后,不需要手动解决一个 Nonogram。来解几个 Nonograms!🤓

先说一个小的:

作者 Gif。

它能解决我们开始的 Nonogram 吗?

作者 Gif。

是的,当然!

最后一个,一个真正大的 Nonogram 呢?

作者 Gif。

工作速度也很快!😀👌

密码

想用这个程序解决诺诺图吗?下面你可以找到 python 代码的要点。您只需要将行值指定为列表的列表,将列值指定为列表的列表。如果你调用NonogramSolver类,它将开始解析你的 Nonogram。您可以通过指定savepath变量来选择保存所有步骤。

结论

我们创建的 Nonogram 求解器有一些好处:它永远不会填充一个没有完全确定的正方形。这种方法的缺点是计算时间。如果谜题变得更大,行和列就有更多的选项,解决谜题的时间也会变得更长。另一个缺点是它不能用多解来求解 Nonograms。

幸运的是,有些人已经解决了这些问题。因此,如果你正在寻找一个超级快速的非诺姆解算器,能够处理有更多解的非诺姆解算器,你可能会觉得这篇文章很有趣。

有关系的

别忘了 订阅 如果你想在我发表新文章时收到电子邮件。

用量子计算机解决最优化问题出奇的容易

原文:https://towardsdatascience.com/solving-optimization-problems-with-a-quantum-computer-is-surprisingly-easy-b89afd3bbbb3

使用量子*似优化算法(QAOA)解决图划分问题

量子机器学习要不要入门?看看 动手量子机器学习用 Python

你可以在 下的Kaggle上访问这篇文章中使用的数据集。

作者图片

假设您有一个计算密集型任务。例如,你打算从头开始训练 GPT-3。但是你不想等几百年才能完成训练。为了加快速度,你必须将所有需要的计算并行化。但是,当然,你不能简单地用 GPU 来解决问题。但是,您必须指定如何在可用的 GPU 之间分配计算。此外,你必须收集所有的解决方案部分,并把它们放回一起,以获得一个训练有素的 GPT-3。这些任务,分离和合并,需要沟通。

这个问题是图划分问题的一个实例。所以,我们用图来表示这个问题。然后,一个节点代表一个需要的计算,一条边代表它们之间的通信。

此外,我们可以使用权重来表示相关计算或通信的成本。假设我们有不止一个处理器来执行计算。由此产生的问题是如何分配它们。为了有效地并行化这样一个问题的计算,我们需要在处理器之间*衡计算,并最小化所需的通信。我们需要把图分成子图。

形式上,我们要求将节点𝑁集划分为两个大小相等的子集,以最小化连接这两个子集的边的数量。

Qiskit 在他们的qiskit_optimization.applications模块中提供了这个问题公式的实现。它允许我们用几行代码解决问题。

from qiskit_optimization.applications import GraphPartition

graph_partition = GraphPartition(use_network(8))

qaoa_optimizer = MinimumEigenOptimizer(QAOA(
    quantum_instance=QuantumInstance(Aer.get_backend("qasm_simulator"))
))

result = qaoa_optimizer.solve(
    graph_partition.to_quadratic_program()
)

print("solution:", graph_partition.interpret(result))
print("time:", result.min_eigen_solver_result.optimizer_time)
#CAPTION Solving the graph partitioning problem
solution: [[4, 5, 6, 7], [0, 1, 2, 3]]
time: 18.636014461517334

首先,我们导入问题公式GraphPartition(第 1 行)并创建一个基于八节点网络的实例(第 3 行)。

备注: *use_network* *draw_graph* 功能是创建指定规模网络的便利功能。为了完整起见,你可以在本文末尾找到它们的实现。

我们创建一个相应的优化器(MinimumEigenOptimizer)并提供一个特征求解器的实例(这里是QAOA)和一个QuantumInstance(第 5-7 行)。最后,我们基于它的二次程序表示(第 10 行)来解决这个问题(第 9 行)。结果我们得到了两个数组。让我们看看他们。

draw_graph(use_network(8),  ''.join(str(int(x)) for x in result.x[::-1]))
#CAPTION Display the solution

作者图片

乍一看,分离似乎是合理的。我们给每组分配了四个节点。然而,它不是最佳的。直觉上,我们期望有两个连接组的解决方案。但是,第一组被分成了两半。但是,看看这三个节点023。它们的边缘重量很大,所以切割它们的成本很高。但是,如果我们的目标是两个完全相连的组,我们将不可避免地穿过这个子组。

另一方面,两个节点23本身具有非常高的权重。因此,将它们放在同一个集合中似乎不是最佳的。让我们尝试改进解决方案,指定对QAOA(第 5-7 行)的重复次数,并重复该过程。

qaoa_optimizer = MinimumEigenOptimizer(QAOA(
    quantum_instance=QuantumInstance(Aer.get_backend("qasm_simulator")),
    reps=3
))
#CAPTION Specify repititions
solution: [[1, 5, 6, 7], [0, 2, 3, 4]]
time: 45.2559118270874
draw_graph(use_network(8),  ''.join(str(int(x)) for x in result.x[::-1]))
#CAPTION Display the solution

作者图片

同样,该解决方案没有分离节点23。但是,它减少了切割边的数量(3,4 之前)及其总重量(16,18 之前)。值得看看GraphPartition类的实现(见源代码)。

我们在下面描述的函数to_quadratic_program中找到相关代码。

class GraphPartition(GraphOptimizationApplication):
    def to_quadratic_program(self) -> QuadraticProgram:
        mdl = Model(name="Graph partition")
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name=f"x_{i}") for i in range(n)}
        for w, v in self._graph.edges:
            self._graph.edges[w, v].setdefault("weight", 1)
        objective = mdl.sum(
            self._graph.edges[i, j]["weight"] * (x[i] + x[j] - 2 * x[i] * x[j])
            for i, j in self._graph.edges
        )
        mdl.minimize(objective)
        mdl.add_constraint(mdl.sum([x[i] for i in x]) == n // 2)
        op = from_docplex_mp(mdl)
        return op
#CAPTION The graph partitioning problem in Qiskit

该函数首先定义Model(第 3 行)、变量(第 4-5 行),以及未指定的边的默认权重(第 6-7 行)。

重要的部分是定义我们要最小化的目标函数(第 8-11 行)(第 12 行)。只有当一条边连接两个不同类的节点时,它才对这条边的权重求和(第 9 行)。这里,ij表示边缘的位置(在第 10 行中定义)。x[i]x[j]表示这些节点的分配。他们不是0就是1。所以。只有当x[i]x[j]不同时,术语(x[i] + x[j] - 2 * x[i] * x[j])才是1。否则整个学期都是0

此外,这个程序包含一个约束。所有节点分配的总和必须等于节点总数除以 2。因此,正好一半的节点必须在类0中,另一半在类1中。

显然,这个目标函数没有考虑节点的权重。因此,让我们自定义它并考虑节点的权重。

class NodeWeightedGraphPartition(GraphPartition):
    def to_quadratic_program(self) -> QuadraticProgram:
        mdl = Model(name="Graph partition")
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name=f"x_{i}") for i in range(n)}
        for w, v in self._graph.edges:
            self._graph.edges[w, v].setdefault("weight", 1)
        objective = mdl.sum(
            self._graph.edges[i, j]["weight"] * (x[i] + x[j] - 2 * x[i] * x[j])
            for i, j in self._graph.edges
        ) + mdl.sum(
            self._graph.nodes[i]["weight"] * (1-2*x[i])
            for i in self._graph.nodes
        )**2
        mdl.minimize(objective)
        mdl.add_constraint(mdl.sum([x[i] for i in x]) == n // 2)
        op = from_docplex_mp(mdl)
        return op
#CAPTION The extended graph partitioning problem

我们创建一个新的类NodeWeightedGraphPartition,它有父类GraphPartition。所以,我们只需要覆盖我们想要改变的函数——to_quadratic_program函数。

我们所做的唯一更改是将节点的权重添加到objective函数的计算中(第 11-14 行)。除了切割边的权重,我们将所有节点的权重相加,每个乘以(1-2*x[i]),其中i是节点的索引,x[i]是节点的类。术语1-2*x[i]要么是1(如果x[i]0)要么是-1(如果x[i]1)。通过对这些值求和,我们最终得到两个类的节点权重之间的总差值。因为这个可以小于零,我们取*方。因此,我们对来自两个同等权重类别的解进行惩罚。

让我们看看结果如何。

node_graph_partition = NodeWeightedGraphPartition(use_network(8))

qaoa_optimizer = MinimumEigenOptimizer(QAOA(
    quantum_instance=QuantumInstance(Aer.get_backend("qasm_simulator"))
))

result = qaoa_optimizer.solve(
    node_graph_partition.to_quadratic_program()
)

print("solution:", node_graph_partition.interpret(result))
print("time:", result.min_eigen_solver_result.optimizer_time)
solution: [[0, 3, 6, 7], [1, 2, 4, 5]]
time: 1.92722749710083
draw_graph(use_network(8),  ''.join(str(int(x)) for x in result.x[::-1]))
#CAPTION Display the solution

作者图片

我们获得了与以前完全不同的解决方案。现在,节点23被分配到不同的类。但是,切割边缘的总数(6)和它们的重量(63)增加了。

结论

最重要的是要清楚我们要求我们的算法优化什么。

量子计算是一个强大的工具,在未来将变得越来越重要。尽管大多数关于这个话题的资源都非常依赖数学,但你并不需要成为数学家来理解它。像 IBM 的 Qiskit 这样的库,在避免使用量子经典优化算法的大部分复杂性方面做得非常出色。

然而,这些库并没有让您摆脱对优化目标的理解。你需要确保明确你要解决的问题。如果你在问题表述上马虎,你可能会得到错误问题的正确答案。

https://pyqml.medium.com/membership

不要错过下一集,订阅我的子栈频道

量子机器学习要不要入门?看看 动手用 Python 学习量子机器

在这里免费获得前三章。

附录

下面的清单包含了本文中使用的便利函数的源代码。我们使用“漫威宇宙”数据集。在attribute 3.0 un ported(CC BY 3.0)许可下,它可以在 Kaggle 上免费获得

def select_heroes(size):
     return dict(item for cnt, item in enumerate({
        'MEDUSA/MEDUSALITH AM': 0,
        'ARCLIGHT/PHILLIPA SO': 1,
        'WOLVERINE/LOGAN ': 2,
        'SCARLET WITCH/WANDA ': 3,
        'ARAGORN': 4,
        'OVERMIND/GROM': 5,
        'BATTLEAXE/': 6,
        'ION/': 7,
        'PINK PEARL/': 8,
        "BLACK PANTHER/T'CHAL": 9,
        'PRINCESS ZANDA': 10,
        'INFERNO III/SAMANTHA': 11,
        'MOTHER NIGHT/SUSAN S': 12,
        'VIPER II': 13,
        'IRON MAIDEN/': 14,
        'CARNIVORE/COUNT ANDR': 15,
        'HULK III/BRUCE BANNE': 16,
        'DEADPOOL/JACK/WADE W': 17,
        'BLIND AL/ALTHEA': 18,
        'WEASEL/JACK HAMMER': 19,
        'MONTGOMERY': 20
    }.items()) if cnt < size)
#CAPTION Select a subset of heroes

def use_network(size):
    data = pd.read_csv('./data/hero-network.csv')
    heroes = select_heroes(size)

    # filter the relations from the raw data
    relations = data[(data.hero1.isin(heroes.keys())) & (data.hero2.isin(heroes.keys()))]
    relations = pd.DataFrame(np.sort(relations.values, axis=1), columns=relations.columns)

    # build unique relations with counts
    relations['relation'] = [' - '.join(x) for x in np.sort(relations.to_numpy(), axis=1)]    
    summed_relations = relations.groupby(['relation']).size().reset_index().rename(columns={0:'count'})
    summed_relations = pd.merge(summed_relations, relations, on='relation', how='right').drop_duplicates(subset=['hero1', 'hero2'])

    # count the overall appearences of the heroes
    count_series1 = data.groupby(['hero1']).size()
    count_series2 = data.groupby(['hero2']).size()
    appearences = count_series1.add(count_series2, fill_value=0)

    # create the graph
    graph = nx.Graph()
    graph.add_nodes_from(heroes.values())
    graph.add_edges_from([(
            heroes[rel[1][2]],
            heroes[rel[1][3]]
        ) for rel in summed_relations.iterrows()])

    # add node weight
    for hero in heroes:
        graph.nodes[heroes[hero]]["weight"] = appearences[hero]
        graph.nodes[heroes[hero]]["name"] = hero

    # add edge weights
    edge_weights = dict((
            (heroes[rel[1][2]], heroes[rel[1][3]]),
            rel[1][1]
        ) for rel in summed_relations.iterrows())
    nx.set_edge_attributes(graph, edge_weights, "weight")

    return graph
#CAPTION Create the network graph

def draw_graph(graph, solution=None, seed=None):
    labels = dict( (key, f"{key}-{val:.0f}") for (key, val) in nx.get_node_attributes(graph, 'weight').items())
    edge_labels = nx.get_edge_attributes(graph, 'weight')

    plt.figure(1,figsize=(16,6)) 
    plt.tight_layout()
    plt.subplots_adjust(bottom=-.5, right=0.5, left=-.5)

    node_color = ["b" if solution[-1-i] == '0' else "y" for i in range(len(solution))] if solution is not None else "#ABABAB"

    pos = nx.spring_layout(graph, seed=seed)
    nx.draw_networkx_nodes(graph, pos=pos, node_color=node_color, node_size=800)
    nx.draw_networkx_edges(graph, pos, width=4, edge_color ="#CDCDCD")
    nx.draw_networkx_labels(graph,pos=pos, labels=labels, font_color="#000", font_size=12)
    nx.draw_networkx_edge_labels(graph, edge_labels=edge_labels, pos=pos)
    plt.show()
#CAPTION Draw the simple graph

使用 Python 中的 NetworkX 解决德国的旅行推销员问题

原文:https://towardsdatascience.com/solving-the-travelling-salesman-problem-for-germany-using-networkx-in-python-2b181efd7b07

使用 Christofides 算法,在访问德国 16 个联邦州的首府城市时,发现穿越每个城市的最短路线。

我已经在德国住了六年了。德国是由 16 个联邦州组成的,到目前为止我已经访问了 5 个州的首府。最*,我想到了一个想法:

我想穿越所有 16 个联邦州的首府,并且从柏林开始,到柏林结束,每个城市只去一次。这样做的最短路线是什么?

这类问题被称为旅行推销员问题(TSP) 。顾名思义,旅行推销员会挨家挨户,或在一个特定的地域内挨家挨户地推销自己公司的产品或服务。TSP 处理寻找恰好一次覆盖所有点的最短路线,因为这将是在成本、时间和能量方面最有效的路线。

图片由安德鲁·怀斯Unsplash 上拍摄。

TSP 是一个 NP 难问题

决策问题是一个有“是”或“否”答案的问题。可能有不同的复杂度等级来解决它。比如:

  • P 是一个复杂类,其中决策问题可以通过确定性图灵机(DTM)在多项式时间内解决。对于某个非负整数 k ,多项式时间算法的执行时间为 O(nᵏ) 级,其中 n 是输入的复杂度。

可以用多项式时间算法解决的问题称为易处理问题。这种算法的例子有线性搜索 (O(n)) 、二分搜索法 (O(log n)) 、插入排序 (O(n )) 、合并排序 (O(n log n)) 和矩阵乘法 (O(n )) (Bari,2018 和 Maru,2020)。

  • NP (非确定性多项式时间)是指一组可以用非确定性图灵机(NTM)在多项式时间内解决的决策问题。一个 NTM 可以有一组动作来解决问题,但是每次执行的转换都是不确定的(随机的)。一旦为 NP 问题提供了潜在的解决方案,DTM 可以在多项式时间内验证其正确性(Viswarupan,2016)。

一个算法,今天是非确定性的,如果明天有人找到它的解,它也可以是确定性的(Bari,2018 和 Maru,2020)。这意味着 DTM 在多项式时间内可以解决的问题也可以由 NTM 在多项式时间内解决。因此, P 是 NP 的子集。

  • NP-hard 是“至少和 NP 中最难的问题一样难”的决策问题的复杂性类别。一个问题 HNP-hard 当 NP 中的每一个问题 L 都可以在多项式时间内 约化H;即假设求解 H 需要 1 个单位时间,那么 H 的解可以用来在多项式时间内求解 LHL (是或否)的解法也必须相同。

TSP 是一个 NP 难问题。这意味着,如果有大量的城市,在“合理”的时间内评估每个可能的解决方案是不可行的(Hayes,2019)。

哈密尔顿/哈密尔顿循环

要解决旅行商问题(TSP),首先需要理解哈密尔顿圈(也称为哈密尔顿圈)的概念。一个哈密尔顿循环是一个图循环(闭环),它通过一个图恰好访问每个节点一次(Mathworld,2022a)。这是以威廉·罗恩·汉密尔顿爵士的名字命名的,他通过一个叫做哈密尔顿难题的游戏引入了这个概念。

在具有n个节点的图中,如果我们将顺时针和逆时针路径视为两条不同的路径,则可能的哈密尔顿圈的总数由(n-1)!给出。如果我们认为顺时针和逆时针路径相同,就有(n-1)!/2可能的哈密顿圈。

让我们以四个城市为例:柏林、杜塞尔多夫、汉堡和慕尼黑。我的目标是穿越这四个城市,起点和终点都在柏林,同时正好经过这四个城市之间的一次。本例中可能的唯一路径(哈密尔顿圈)的数量由(4–1)给出!= 3!= 6.

在下面的代码中,cities是这四个城市的列表,但是startend是柏林。其他三个城市定义为*rest。这三个城市的排列一次占据所有三个城市,导致所有可能的汉密尔顿循环。这显示在下面的 Python 代码中。

Python 代码生成除柏林之外的三个城市的排列,一次取全部三个。图片作者。

在下面的要点中,make_tsp_tree函数首先创建一个 Hamilton 路径列表,然后从这些路径列表中创建一个有向前缀树,然后通过删除根节点和 nil 节点返回 graph 对象G。使用 Graphviz 的点布局设置G中每个节点的位置,Graphviz 在有向图中设置层次结构。

这四个城市列表的哈密尔顿路径如下图所示:

示例中四个城市的汉密尔顿路径。图片作者。

在德国地图上对 16 个州的首府进行地理编码和绘图

我把德国 16 个州首府的列表定义为capitals。使用一个叫做地理编码的过程,我可以得到所有 16 个城市的coordinates。在这个故事中详细描述了使用 geopy 包进行地理编码的过程。

capitals = [‘Berlin’, ‘Bremen’, ‘Dresden’, ‘Dusseldorf’,
            ‘Erfurt’, ‘Hamburg’, ‘Hannover’, ‘Kiel’,
            ‘Magdeburg’, ‘Mainz’, ‘Munich’, ‘Potsdam’,   ‘Saarbrucken’, ‘Schwerin’, ‘Stuttgart’, ‘Wiesbaden’]

使用 GeoPandas 包,我在名为germany的地理数据框架中提取德国地图。为了绘制germany中的capitals,首先我使用 NetworkX 创建一个图形对象G。然后我创建 16 个节点,每个城市一个。接下来,我为positions创建一个每个城市的节点和坐标的字典。我为labels创建了另一个节点和首都名称的字典。下面的截图给出了代码和生成的地图。

使用 GeoPandas 和 NetworkX 在德国地图上绘制 16 个联邦州的首府。图片作者。

德国任何两个首都之间所有可能的路径

下一步是确定和绘制德国任何两个城市之间所有可能的路径。由于我们的列表中有 16 个城市,因此任意两个城市之间的可能路径数为ⁿCᵣ = ⁶C₂ = 120。在现有的图形对象G中,我添加了任意两个节点之间的边,除了它本身,如下所示:

for i in nodes:
     for j in nodes:
          if i!=j:
               G.add_edge(i, j)

用带有最*添加的边的germany绘制更新的图形对象G,如下所示:

所有可能的路径(120)由德国 16 个联邦州中任意两个城市之间的蓝色边缘表示。图片作者。

我创建了H作为G的备份供以后使用。

接下来,我计算德国所有两个城市之间的欧几里德距离,这些信息作为边的权重存储在G中。因此,G现在是一个完全加权图,其中一条边连接每对图节点,并且每条边都具有与之相关联的权重。

H **=** G**.**copy()

*#Calculating the distances between the nodes as edge's weight.*
**for** i **in** range(len(pos)):
     **for** j **in** range(i **+** 1, len(pos)):

        *#Multidimensional Euclidean distan* dist **=** math**.**hypot(pos[i][0] **-** pos[j][0], pos[i][1] **-** pos[j][1])
          dist **=** dist
          G**.**add_edge(i, j, weight**=**dist) 

城市之间的距离可以在下表中以矩阵形式观察到。在下表中,在主对角线上观察到的城市之间的距离为零。主对角线上方的所有条目都反映在对角线下方的相等条目中。因此,它是一个中空(零对角线)对称矩阵的例子。

德国 16 个联邦州首府之间的距离矩阵。图片作者。

求解 TSP 的算法

为了找到解决方案cycle,NetworkX 对无向图使用默认算法 Christofides 算法,作为解决方案cycle一部分的边列表存储为edge_list。这在下面的代码中实现:

cycle **=** nx_app**.**christofides(G, weight**=**"weight")
edge_list **=** list(nx**.**utils**.**pairwise(cycle))

克里斯托菲迪斯算法

Christofides 算法在距离形成一个度量空间的情况下找到 TSP 的*似解(它们是对称的,并且遵守三角形不等式,即在 ABC 中,a+b≥c ) (Goodrich 和 Tamassia,2014)。这种算法是以塞浦路斯数学家尼科斯·克里斯托菲德斯的名字命名的,他在 1976 年发明了这种算法。到目前为止,该算法提供了解决 TSP 的最佳*似比(克里斯托菲德斯,2022)。

下面给出了该算法的基本步骤。这些步骤已经在 NetworkX 这里的源代码中实现。我还说明了我们的问题的算法逐步实施。

  1. G 的最小(重量)生成树 T

加权、连通、无向图的最小生成树(此处为G)是由连接所有连通节点(此处为 16 个)的边的子集组成的图,同时最小化边上权重的总和。

NetworkX 使用一个 Kruskal 算法来寻找最小生成树(NetworkX,2015)。对于德国及其 16 个联邦州的首都来说,情况如下:

最小生成树( T) 为德。图片作者。

2。在 T 中做一组奇数度 O 的节点。

在下一步中,创建一组名为 O 的节点,其中每个节点的度数都是奇数。从上面的树中,我们看到柏林、波茨坦、斯图加特和施韦林的度都是 2,即偶数。因此,这些节点在新的集合 O 中被移除,如下所示:

T 中奇数度(O)的节点集。图片作者。

3。从 O 的顶点给出的诱导子图中找到一个 最小权完美匹配 M

为了有一个完美匹配,在一个图中需要有个偶数个节点,并且每个节点恰好与另一个节点相连。因此,完美匹配是包含 n/2 条边的匹配(最大可能)(Mathworld,2022b)。

最小权重完美匹配 M 计算完美匹配,使匹配边的权重最小化,如下图所示。

由 O. Image 的顶点给出的诱导子图中的最小权完美匹配(M)。

4。将 MT 的边缘组合在一起,形成一个相连的多联图 H

在下一步中,该算法将步骤 1 中的T**的边与步骤 3 中的 M 的边组合起来,以形成连通的多图 H

步骤 1 中 T 的边和步骤 3 中 M 的边的联合产生作者的 H. Image。

5。用 M 和 t 的边建立一个欧拉电路

下一步,Christofides 算法使用 MT 的边缘构建欧拉电路。欧拉回路是有限图中恰好访问每条边一次的轨迹。欧拉电路中的每个节点都必须有偶数度(麻省理工开放式课程,2016)。哈密尔顿圈和欧拉圈的区别在于,哈密尔顿圈恰好经过每个节点一次,结束于初始节点,而欧拉圈恰好经过每个边一次,结束于初始节点。

注:一个循环可以是汉密尔顿循环,也可以是欧拉循环。

6。通过跳过重复的节点,将欧拉循环转换为哈密尔顿循环。

在我们的 TSP 的例子中,在欧拉电路中可能有一些重复的节点。这种节点的度大于 2,即该节点被访问了不止一次。Christofides 算法利用一个快捷函数从欧拉电路中移除重复节点,并创建一个cycle。因此,TSP 的解决方案是通过使用cycle中节点之间的连续边列表来实现的。

Christofides 算法产生的解的权重在最优值的 3/2 以内(麻省理工学院开放课件,2016)。

德国 TSP 解决方案。图片作者。

使用 NetworkX 直接实施 TSP 解决方案

使用如上所述的克里斯托菲迪斯算法,TSP 的解由cycle给出。由于cycle只包含了capitals城市的指数,我得到了表示城市顺序为tsp_cycle的求解路线,如下图所示。

如下面截图中的[17]所示,我在德国地图上用蓝线标出了任意两个城市之间所有可能的路径。红线代表edge_list,它包含城市之间的边列表,这些边是解决方案cycle的一部分。

获得针对德国的 TSP 的解决方案路线,并将其绘制在德国地图上。图片作者。

下图通过移除两个城市之间的蓝色(所有可能的)边,仅用红色绘制德国 TSP 的解决方案,提供了一个更清晰的解决方案。

德国 TSP 的解决方案路线,用红边表示。图片作者。

用独特的颜色为每条边绘制解决方案

我想让我的解决方案更吸引人一点,为城市之间的每条边绘制一种独特的颜色。

计算机显示器上使用的任何颜色都可以表示为可见光谱中 RGB(红、绿、蓝)的比例(Mathisfun,2021)。因此,在 Python 中,任何颜色都可以表示为#RRGGBB,其中 RR、GG 和 BB 具有从 00 到 ff 的十六进制值。#000000 代表白色,而#ffffff 代表黑色。

我创建一个名为get_colors(n)的函数,根据十六进制的#RRGGBB 的随机值创建一个随机颜色列表。

*import random
get_colors = lambda n: list(map(lambda i: “#” +”%06x” % random.randint(0, 0xffffff),range(n)))*

在下面的代码中,我将get_colors(16)作为edge_color进行传递。

*fig, ax = plt.subplots(figsize = (20, 12))germany.plot(ax = ax, color = “whitesmoke”, edgecolor = “black”)# Draw the route
nx.draw_networkx(G, pos = positions, labels = labels, 
 edgelist=edge_list, edge_color=get_colors(16), width=3,
 node_color = “snow”, node_shape = “s”, node_size = 300,
 bbox = dict(facecolor = “#ffffff”, boxstyle = “round”,ec = “silver”),
 ax = ax)plt.title(“Travelling Salesman Problem Solution for Germany”, fontsize = 15)
plt.axis(“off”)*

结果,我得到了下面的图,这是丰富多彩的,比前面的更有吸引力,解决方案中的每个边都有独特的颜色。

适用于德国的 TSP 解决方案,每条边都有独特的颜色。图片作者。

使用叶子绘制解决方案

也可以使用包在交互式传单地图中绘制上面的图。

为此,我将coordinates修改为folium_coordinates,将cycle修改为route。这些都是相同的数据,但是采用了新的格式:一个 GPS 坐标列表(纬度和经度)的列表,与 folium 兼容。

接下来,我在北纬 51 度、东经 10 度的位置创建了一张地图m1,这是德国的大概坐标。我选择 OpenStreetMap 图块和 6 点的zoom_start。我在地图上每个城市的坐标处添加一个标记。最后,我使用反映 TSP 解决方案的route在地图上绘制折线覆盖图。

生成的图显示在交互式传单地图中:

德国 TSP 的解决方案路线使用 lyus 绘制。为每个城市添加标记,并在地图上绘制解决方案路线。图片作者。

结论

这篇文章的问题陈述是寻找最短的路线穿越德国 16 个联邦州的首都城市,从柏林开始,到柏林结束,同时访问中间的每个城市一次。我首先描述了解决任何决策问题的不同复杂性类别:P(多项式时间)、NP(非确定性多项式时间)和 NP-hard。接下来,我讨论了哈密尔顿循环的概念。

我使用地理编码找到了德国 16 个联邦州的首府城市的坐标,并使用 GeoPandas 包将它们绘制在德国地图上。我添加了地图上任意两个城市之间的所有边。接下来,我演示了 Christofides 算法如何通过它的逐步实现为旅行推销员问题提供解决方案。最后,我使用 NetworkX、GeoPandas 和 Matplotlib 包为这个问题绘制了一个清晰的解决方案。我还用独特的颜色绘制了每个边缘的解决方案,并使用 follow 包绘制了活页包装中的解决方案。

这个故事中的分析实现可以在这个 GitHub 存储库中获得。如果你想进一步了解 Python 图形可视化的基础知识,你可以参考下面的故事和本系列的其他相关故事。

*

感谢您的阅读!

参考

巴里,2018。 NP 难和 NP 完全问题

克里斯托菲德斯,2022 年。旅行商问题新启发式算法的最坏情况分析

古德里奇和塔玛西亚,2014 年。算法设计与应用| Wiley

海耶斯,2019。用 Python 解决旅行推销员问题。

丸,2020。 P,NP,NP 难和 NP 完全问题|归约| NP 难和 NP 难|多项式类

马图塞克,1996 年。多项式时间算法。

Mathisfun.com,2021 年。十六进制颜色。

数学世界,2022a。哈密顿循环

数学世界,2022b。完美搭配。

麻省理工学院开放式课程,2016。 R9。*似算法:旅行推销员问题

NetworkX,2015。最小生成树。

Tutorialspoint,2022。非确定性图灵机

维斯瓦鲁潘,2016。 P vs NP 问题。*

在 Gurobi 中求解两阶段随机规划

原文:https://towardsdatascience.com/solving-two-stage-stochastic-programs-in-gurobi-9372da1e3ba8

泰勒·维克在 Unsplash 上的照片

两阶段随机服务器群问题的公式化和求解

随机规划(SP)是一个框架,用于建模包含不确定性的优化问题[1]。在许多情况下,SP 模型采取两阶段问题的形式。第一阶段包括寻找最优的确定性决策。这些决定是基于我们知道是确定的信息(也就是此时此地的决定)。考虑到我们第一阶段的决策,第二阶段涉及做出依赖于随机性的决策(也称为求助决策)。优化问题旨在最小化第一阶段决策导致的损失(或最大化利润)加上第二阶段决策导致的预期损失。从数学上讲,这可以写成以下格式:

2SP 通用表格(图片由作者提供)

在本文中,我将演示如何使用 Gurobi 来制定和解决其中的一个问题。

激励范例

让我们用一个例子来透视这个框架:一个服务器场问题。假设我们正在尝试为公司设计一个服务器群,考虑到我们拥有的空间和购买服务器的前期预算,我们需要安装 CPU、GPU 和 TPU 核心来帮助处理公司的计算资源。我们对每个资源下个月的需求以及它们的分布有一个大概的估计。为了简单起见,我们将忽略下个月以后的几个月(尽管可以通过一个多阶段随机程序来处理这个问题)。

未来运行服务器将花费少量成本,但如果我们不能满足需求,我们将不得不利用云计算资源,这将大大增加我们的运营成本。
在这个例子中,我们的第一阶段决策(此时此地的决策)是购买每种资源的数量。第二阶段(追索权)决策是购买云计算的数量。我们的目标是最小化服务器群的运营成本。这个优化问题可以写成如下:

服务器群 2SP 公式(图片由作者提供)

其中 x 表示我们将购买每个内核的数量, y 表示我们将利用多少云计算, i 是我们的安装成本, B 是我们的安装预算, s 是内核的大小, S 是我们的可用空间, c 是我们的云计算成本, d(xi) 是我们的随机需求。

解决 SP

为了解决上面的问题,我们将实现一种叫做样本*均*似的技术【2】。这种方法包括通过蒙特卡罗抽样生成随机变量的大量( K )潜在实现(场景),并将每个实现视为一个单独的子问题【2】。这有助于将随机性离散化,从而在不损失太多信息保真度的情况下使其可解。这意味着我们将有 K 个变量,而不是只有一组第二阶段变量,第二阶段值的期望值是我们实现的*均值。这样,我们的公式变为:

SAA 重构(图片由作者提供)

虽然该公式可以在各种编程语言和线性编程求解器(例如 SCIP、CPLEX、CVXPY)中实现,但我们将专注于使用商业级、最先进的数学编程求解器 Gurobi 来解决这个问题。Gurobi 有针对 Python、MATLAB、R 和 Java 等语言的 API,但是它的大多数用户更喜欢使用 Python API [4],所以我们将在这里使用它。

首先,我们将导入我们的包(numpygurobipy)并为我们的问题初始化(组成)数据。

这个问题中i是投资成本,s是每个项目占用的空间,B是我们的预算,S是我们的最大空间。另一个值得注意的变量是每个场景中的需求d_xi。然后我们可以用古罗比来解决我们的问题。首先,我们将使用gp.model()定义我们的模型,设置目标(最小化)以及初始化我们的决策变量。

从上面的例子中可以看出,我们的需求(d_xi)和我们的第二阶段决策变量(y)都不是一维向量,而是一个矩阵,因为这两个变量在我们的公式中都有一个场景索引。在此之后,我们可以定义我们的目标函数:

Gurobi 的一个优点是,在定义约束和目标时,它支持numpy矩阵乘法运算,如果考虑矩阵乘法(例如i @ x)。Gurobi 还支持求和运算,这也降低了进入门槛。这里我们计算第一阶段的成本,然后取第二阶段的*均成本。

我们现在可以继续定义我们的约束条件:

第一行代码确保安装内部服务器的成本不会超出我们的预算B。第二行确保我们只购买我们有空间的服务器。第三行代码确保场景 k 中安装的服务器数量和利用的云计算资源数量足以满足场景 k 中的需求。

现在问题已经完全定义好了,我们可以简单地通过调用m.optimize()来解决问题并得到我们的最优决策:

最佳变量值存储在我们定义的每个变量中,可以通过variable_name.x访问。虽然我们可能已经获得了 xy 变量的最优值,但是我们只关心我们现在可以做出的决定( x )。

限制

这种方法为我们提供了高质量的解决方案,但也有缺点。

第一个不利之处是,随着场景数量的增加,模型将变得更加难以求解。例如,我们只需要做出 6 个真正的决策,但是使用样本*均*似值使我们的模型做出了 153 个决策(50 个场景中的 3 个第一阶段,3 个第二阶段)。这意味着分解算法(如线性 SPs 的 Benders 分解[3])通常需要用于并行化计算。

另一个考虑是,不幸的是,Gurobi 不是自由软件。虽然 Gurobi 确实提供了许多学生和研究人员利用的学术许可证,但不属于大学的个人可能会发现很难获得该软件的访问权限。Gurobi 的一个开源替代方案是 SCIP [5]。两者共享相似的建模语言,因此 Gurobi 用户可以很容易地适应 SCIP,反之亦然。出于个人偏好以及 Gurobi 的快速求解性能,我选择用 Gurobi 而不是 SCIP 来演示这个概念。这并不意味着 SCIP 就低人一等。事实上,许多专注于开发或改进优化算法的优化研究人员更喜欢 SCIP,因为它的开源特性允许用户调整求解器的每个部分,从而允许他们尝试新的方法。

资源和参考资料

这个问题的完整代码可以在这里找到。

作品引用

[1] A. Philpott,什么是随机规划。(2022),随机规划学会。

[2] A. J. Kleywegt,A. Shapiro,T . Homem-de-Mello随机离散优化的样本*均逼*法 (2002),SIAM 优化杂志。

[3]尼尔森,泽尼奥斯。随机线性规划的可扩展并行 Benders 分解 (1997),并行计算第 23 卷,第 8 期。

[4]古罗比。从古罗比开始 (2022),古罗比网站

[5] Bestuzheva 等人。艾尔。SCIP 优化套件 8.0 (2021),在线优化

Python 初学者的一些基本图像预处理操作

原文:https://towardsdatascience.com/some-basic-image-preprocessing-operations-for-beginners-in-python-7d297316853b

Unsplash 上的 Shubham Dhage 拍摄的照片

OpenCV 初学者:移动或*移、调整大小和裁剪

Python 用户最常用的图像处理和操作库之一是 openCV。对于图像分类、物体检测或光学字符识别,在人工智能世界中,任何与图像相关的工作大多数时候都需要某种图像处理和操作。我决定在 OpenCV 上写几个教程。

在本教程中,我们将重点介绍 OpenCV 的几个基本功能。这些都是基本的,有时也会非常有用。我们将通过例子来学习它们。所以,让我们准备好弄脏你的手吧。

在开始之前,这些是我们今天将使用的库。

import cv2
import matplotlib.pyplot as plt

我们将根据需要导入其他库。

本教程将使用一个简单的笑脸图像。请随意从以下链接下载图片:

https://github.com/rashida048/OpenCV/blob/main/smiley.jpg

在这行代码中,cv2.imread 函数用于读取图像并将其存储为数组。

image = cv2.imread('smiley.jpg')

如果您对 OpenCV 完全陌生,下面是“image”变量的样子:

 array([[[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       ...,

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]]], dtype=uint8)

图像可以使用。imshow()函数。Matplotlib 和 OpenCV 库都有。imshow 函数。

plt.imshow(image)

作者图片

*移、移动或移位

我们将从如何移动图像开始。本教程将使用一个简单的笑脸图像。

定义如何移动图像并将其保存在移动变量中。让我们将图像向右移动 100 像素,向下移动 30 像素。为此,move 变量将如下所示:

move = np.float32([[1, 0, 100], [0, 1, 30]])

在这里,1 和 0 是固定的。在[0,2]位置的 100 将使图像向右移动,在[1,2]位置的 30 将使图像向下移动。

shift = cv2.warpAffine(image, move, (image.shape[1], image.shape[0]))
plt.imshow(shift)

作者图片

如果你想把图像向左上方移动,你只需要使用负号:

move = np.float32([[1, 0, -100], [0, 1, -30]])
shift = cv2.warpAffine(image, move, (image.shape[1], image.shape[0]))
plt.imshow(shift)

作者图片

使用 imutils 库也可以达到同样的效果。下面是语法:

import imutils
moved = imutils.translate(image, -100, -30)

它将生成与上一张完全相同的图像。如您所见,使用 imutils 稍微简单一些。

调整大小

缩放操作在图像预处理中非常常见。我们根据需要增加或减少图像的高度和宽度。以下是 OpenCV 中调整大小的语法:

resized = cv2.resize(image, image_shape, interpolation = cv2.INTER_AREA)

我用“插值”作为 cv2。地区间。对此还有其他选择。我们稍后会谈到它们。先说 image_shape。重要的是要注意长宽比,使图像看起来不失真。为此,有必要将输出图像的纵横比保持为输入图像的纵横比。

如果我们希望输出图像的高度为 500,则输出图像的图像形状需要为:

rsz = 500.0 / image.shape[1]
image_shape = (500, int(image.shape[0] * rsz))

同样的,如果我们需要输出图像的宽度为 300,我们计算输出图像的高度。

rsz = 300.0 / image.shape[0]
image_shape = (int(image.shape[1] * rsz), 300)
resized = cv2.resize(image, image_shape, interpolation = cv2.INTER_AREA)

这都是关于图像形状的。

第三个参数“插值”有几个不同的选项。以下是这些名字:

cv2。线性的

cv2。最*的

cv2。立方间

cv2。跨兰佐斯 4

如果不提供任何“插值”方法,OpenCV 使用的默认方法是 cv2。提供双线性插值的 INTER_LINEAR 方法。

如果有兴趣,请随时探索更多。

imutils 库让我们变得更容易。它为我们计算长宽比。它只需要高度或宽度。它自己计算宽度或高度。下面是语法:

im = imutils.resize(image, width=300)

在这里,我们提供的宽度和高度将自动计算,以保持纵横比不变。

种植

如果你曾经处理过图像,你肯定以某种方式进行过裁剪。裁剪图像的一种常见方法是简单地使用数组切片。正如我们在开始时谈到的,图像只不过是一个 NumPy 数组。

我在这里裁剪笑脸:

part = image[0:600, 0:600]
plt.imshow(part)

作者图片

所以,它取了上面的 600 个像素和左边的 600 个像素。这是另一个例子:

part = image[100:600, 100:600]
plt.imshow(part)

作者图片

这次我们从左上第 100 个像素开始。所以,这里我们从顶部的第 100 个像素到第 599 个像素,从左边的第 100 个像素到第 599 个像素。请随意尝试其他产品系列。

结论

OpenCV 中有很多可用的函数。我只想从这三个基本操作开始。在我接下来的教程中,我将会讲述更多的高级技术。希望这对你有帮助。

更多阅读

关于运筹学和机器学习协同作用的一些思考

原文:https://towardsdatascience.com/some-thoughts-on-synergies-between-operations-research-and-machine-learning-921d78ed4bd5

搭建运筹学和机器学习之间的桥梁

Unsplash 上的 Modestas Urbonas 拍摄的照片

几十年来,运筹学(or)和机器学习(ML)作为两个相对独立的研究领域取得了进展。数据科学和人工智能领域的人可能比 OR 更熟悉 ML,尽管每个 ML 从业者都应该知道至少一些优化技术,因为每个机器学习问题本质上都是一个优化问题。在本文中,我将把 OR 和 ML 视为一个整体主题,回顾它们之间的联系,并分享这两个领域之间协同作用的一些最新进展,以充分利用这两个领域的优势。

作为预览,下图从三个角度说明了 OR 和 ML 之间的联系:OR 帮助训练 ML 模型,ML 为 OR 提供输入,ML 改进 OR 的解决方法。以下段落将详细阐述这三个方面。

说明 OR 和 ML 之间相互作用的图表(图片由作者提供)

或者帮助训练 ML 模型

OR 的核心是优化。或者研究人员开发了许多技术来寻找决策变量的最优值,以最小化或最大化手头的目标函数。根据优化问题是否有约束,优化问题可以分为约束优化和无约束优化。基于目标函数和约束的公式化,优化问题可以粗略地分为线性优化和非线性优化。

也许 ML 中最常遇到的优化问题是非线性优化,因为对于监督学习中的分类(例如,交叉熵)和回归(例如,均方误差),损失函数相对于 ML 模型的参数通常是非线性形式。基于梯度下降的算法在解决这些问题时通常是有效的。如果正则项存在,我们最终会遇到一个受约束的非线性优化问题(例如,岭回归、LASSO、支持向量机)。在这种情况下,我们应用拉格朗日乘数.)并对原始约束优化问题的拉格朗日松弛进行工作,这是处理复杂约束的典型 OR 技术。例如,在岭回归中,我们试图解决下面的问题:

其中 y 是输出变量的观测值的向量, X 是输入变量的观测值的矩阵, b 是要拟合的系数的向量, t 是用于控制正则化水*的参数。直接求解这个公式并不容易,所以我们应用一个拉格朗日乘数λ,并将原始公式转换为它的拉格朗日松弛:

这进一步简化为:

现在可以应用无约束优化技术来获得 b 的最优值。

每个 ML 问题本质上都是一个以损失函数为目标函数,模型参数为决策变量的优化问题。在这个意义上,OR 增强了 ML,因为对非线性优化问题的更好的解决方法无疑提高了机器学习模型的训练过程的准确性和效率。本文开头给出的图表中的蓝色箭头说明了 OR 和 ML 之间的这种交互。

ML 向 OR 提供输入

与 ML 中 OR 技术的应用不同,现实世界应用中的主要 OR 模型是线性规划(LP)和混合整数线性规划(MILP)模型。LP 问题是具有线性目标函数和线性约束的优化问题,其中决策变量可以取连续值。虽然 MILP 问题也有线性目标函数和线性约束,但它的一些决策变量必须取整数值。LPs 和 MILPs 在各个行业都有广泛的应用。例如,在供应链管理中,MIPs 通常用于设施位置选择、生产计划和车辆路线安排等..这类问题通常具有线性成本函数作为目标函数,具有满足客户需求、确保资源最小利用率等大量约束..事实上,OR 从业者倾向于不将现实世界的问题公式化为非线性优化问题,因为它们解决起来要复杂得多,尤其是有许多约束的情况下。

在这样的应用中,ML 模型,主要是监督学习模型,通常用于提供 LP 和 MILP 模型中已知参数的估计。例如,在供应链管理领域,我们可以建立一个监督学习模型来预测客户需求,然后该模型成为 LP 和 MILP 模型的约束条件或目标函数中的一个已知参数。客户需求预测可以是点估计,也可以是概率估计,这与确定性或随机优化问题有关。由于 ML 模型会影响 OR 应用输入参数的准确性,因此 ML 模型的质量也会影响 OR 应用的成败。OR 和 ML 之间的这种相互作用由本文开头给出的图表中的绿色箭头来说明。

下面,我将通过一个设施位置选择问题的简单示例来说明 ML 模型的输出如何作为 MILP 的输入。让我们假设一家公司想要考虑在 I 候选地点中建立配送中心,将他们的成品运送给 J 的客户。每个站点 i 都有其相应的存储成品的容量,最多可存储 m_i 单位的产品建造每个地点 i 需要固定的建造费用 f_i. 将每单位产品从地点 i 运送到客户 jT21c _ ij。每个客户 j 都有一个 d_j 的需求,所有客户的需求都必须得到满足。设二进制变量 y_i 表示我们是否在站点 i 建造设施,并且 x_ij 表示从站点 i 向客户j装运的产品量。以最小化总成本为目标的优化问题可以表述如下:

设施选址问题的数学公式(图片由作者提供)

这里, y_ix_ij 是代表我们需要做出的决策的决策变量,在我们试图解决问题之前是未知的。其他变量是已知的参数,在我们试图解决问题之前必须知道这些参数。ML 在这个问题中的作用是它可以提供需求预测,对 d_j. 需求预测属于时间序列预测的范畴,因为需求数据通常以时间序列的形式出现。各种算法,从传统的时间序列模型(例如,ARIMA,指数*滑等。)到 ML 模型(例如,LightGBM、神经网络)可在此应用以获得 d_j 的合理估计。 ML 模型也可用于获得其他参数的估计,如 c_ij、f_i、等。,但我个人在预测需求方面看到的应用比其他参数多。上述优化问题可以用任何商业求解器解决,如 CPLEXGurobiXpress ,以及非商业求解器如 SCIP

ML 改进了 OR 的求解方法

正如 OR 影响 ML 的训练过程一样,ML 也可以在 OR 模型的求解过程中发挥作用。*年来,利用 ML 提高求解混合整数规划(MIPs)的分支定界算法的效率的研究兴趣越来越大。分支定界是一种广泛用于求解 MIPs 的算法,其工作方式类似于树搜索算法。假设我们正在解决一个最小化问题。在根节点,该算法求解原始问题的 LP 松弛(在 MIP 中丢弃完整性约束将原始问题转换成它的 LP 松弛)。然后从根节点发展两个分支,产生两个新节点,并且使用最接*的整数向每个分支添加附加约束。下图简要说明了在分支定界算法中开发分支的过程。

一个简单的图表来说明分支定界算法(图片由作者提供)

以上图为例,如果在根节点(即 LP0)原问题的 LP 松弛的最优解中 x_1 = 2.5,我们选择在其上分支,我们将 x_1 ≤ 2 加到第一个分支,x_1 ≥ 3 加到第二个分支。然后在每一个新节点,我们用增加的约束来解决产生的 LP 松弛问题。上面的过程被称为分支,我们在开发树的时候重复这个过程。如果带有完整性约束的决策变量都是整数,那么我们到达一个叶子节点。

请注意,在搜索树时,每当我们在一个节点遇到整数解时,我们就更新当前的最佳解。当前最佳解决方案提供了 MIP 的最佳目标值的上限。如果在特定节点处,LP 松弛具有大于当前最佳解的最优目标值,则无需进一步探索该节点,并且该节点将被修剪。这种修剪过程通过消除到达树的每个叶节点的必要性,显著地帮助减少了树搜索工作。

然而,即使有剪枝,实际问题通常是如此之大,以至于执行分支定界算法的普通版本仍然相当耗时。研究人员已经提出了几个改进品牌和边界算法的想法。一个想法是在一些节点上增加对 LP 松弛的削减。截是可以排除非整数解,但不能排除整数解的约束。通过在一些节点上添加切割,我们可以缩小 LP 松弛的可行区域,并且通过求解 LP 松弛更容易找到整数解。遵循这种思想的算法叫做分支切割算法。

添加切割是一个好主意,但有时找到好的切割本身也是一项重要的任务。在这种情况下,对一些节点应用试探法对于寻找整数解非常有用。一种常用的试探法是松弛诱导邻域搜索(RINS)。这种启发式方法查看当前节点处 LP 松弛的当前最佳整数解和分数解,固定两个解一致的决策变量的值,并将其余决策变量作为子问题求解。

注意,每个整数解提供了原始 MIP 的最优解的上界(假设解决了最小化问题),并且在活动节点(未被修剪的节点)处的 LP 松弛的每个分数解是原始 MIP 的最优解的下界。因此,添加切割有助于改善下限,应用试探法有助于改善上限,这一起有助于分支和界限算法更快地收敛。

回到 OR 和 ML 之间的交互,使用 ML 改进分支定界算法的核心思想是将 ML 应用于:

  1. 学习分支——在节点上分支哪个决策变量
  2. 学会削减——如何找到有效的削减来增加 LP 放松
  3. 学会寻找好的启发法——帮助找到更好的整数解
  4. 学习配置优化求解器的参数化-如何配置求解器(例如,终止标准、应用试探法的频率),以便更快地解决问题

通常需要一大组 MIP 来训练 ML 模型,然后 ML 模型将其所学应用于感兴趣的特定 MIP 实例。

利用最大似然法求解 MIPs 是一个新兴的研究课题,大部分工作都集中在理论研究上,而不是在商业或非商业 MIPs 求解器中的实际实现。下面是一个有用的资源列表,您可以从这个角度了解更多信息。

  1. ML4CO 是与此题目相关的竞赛,致力于鼓励使用 ML 解决组合优化(一个与整数规划大致相同的概念)问题。这项比赛向参与者提出了三项任务的挑战:原始任务、对偶任务和配置任务,每项任务都侧重于分支定界算法的不同方面。假设求解最小化 MIP,原始任务要求参与者使用 ML 在根节点找到更好的整数解,以降低最优解的上限。双重任务要求参与者关注如何用 ML 进行分支决策,以增加最优解的下限。最后,在配置任务中,参与者试图使用 ML 为非商业求解器 SCIP 找到更好的参数化来求解 MIPs。
  2. 论文“ 《组合优化的机器学习:方法论之旅 d'Horizon ”提供了利用 ML 技术解决组合优化问题的尝试的综述。作者总结了使用 ML 解决组合优化问题的两个动机:从专家给出的演示中学习,以在搜索最优解的同时做出决策(例如,分支定界算法中的分支决策);从经验中学习,这可能导致探索做出决策(例如,分支决策)的新策略,以推进技术水*。第一个概念与模仿学习一致,第二个概念与强化学习一致。本文开头给出的图中的红色箭头从这个方面说明了 OR 和 ML 之间的交互。
  3. 另一篇值得注意的论文是由 Google DeepMind 团队撰写的“ 使用神经网络 求解混合整数程序”,其中创建了 MIPs 的图形表示,并且使用神经网络来为整数变量生成部分赋值(神经驱动)并学习做出分支决策(神经分支)。在使分支定界算法更有效方面获得了有希望的结果。

结论

在本文中,我从三个角度分析了 OR 和 ML 之间的联系。虽然对于前两种观点,实际实现中已经有成熟的技术,但最后一种观点仍然需要更多的研究工作来实现实际和可扩展的实现,但它非常有前途。OR 和 ML 在本质上是紧密相连的,并将随着两个领域的推进而齐头并进。很可能在未来 OR 和 ML 之间还会有其他更有趣、更令人兴奋的协同作用。

使用通用数据库工具 DBeaver 的一些技巧

原文:https://towardsdatascience.com/some-tips-for-using-dbeaver-a-univeral-database-tool-94af18d50671

学习一个方便的工具来方便地管理您的数据库

图片由威廉创作在 Pixabay

DBeaver 是一个通用的数据库管理工具,可以用来管理各种关系数据库和 NoSQL 数据库。DBeaver 既有免费开源的社区版(CE),也有商业企业版(ee)。社区版支持 MySQL、PostgreSQL、Oracle 等各种关系数据库。另一方面,企业版也支持 NoSQL 数据库。

DBeaver 非常人性化,非常容易上手。它支持代码完成、代码格式化、语法突出显示和其他大量方便的特性,这些将在后面介绍。如果您一直在使用其他数据库管理工具,那么非常值得尝试一下 DBeaver。很有可能你会喜欢的。

为了让你更喜欢它,在这篇文章中,我们将介绍一些对初学者来说不容易发现的技巧。这些提示将解决您第一次使用 DBeaver 时可能遇到的问题。我们还将探索一些可以显著提高您工作效率的功能。

准备

请从此链接下载并为您的操作系统安装 DBeaver。出于演示目的,我们将使用不需要注册或许可证的社区版。

我们将使用 DBeaver 来管理一个 MySQL 服务器,该服务器可以通过以下 Docker 命令启动:

请注意,高端口(13306)用于避免潜在的端口冲突。

然后运行下面的命令导入一些虚拟数据,稍后可以在 DBeaver 中播放。

注意,我们直接使用 Docker 容器中的 MySQL 客户端,因此不需要单独安装。如果您确实想在您的计算机上本地安装一个 mysql 客户端,您可以安装 mysql-client ,它包括mysqlmysqldump,以及其他有助于在命令行上管理 MySQL 数据库的工具。在 Ubuntu 上,命令是:

$ sudo apt-get install mysql-client

或者,您可以使用图形界面直接在 DBeaver 中创建数据库和表,如下所示。但是,在这样做之前,您需要首先在 DBeaver 中为我们的本地 MySQL 数据库创建一个连接。

在 DBeaver 中创建连接

现在让我们为 MySQL 数据库创建一个连接。单击“新建数据库连接”按钮,然后选择 MySQL 作为数据库。如果你使用 MySQL 5,你需要选择 MySQL 5。

作者图片

在“连接设置”窗口中,输入主机、端口、用户名和密码。您可以在创建连接之前测试它:

作者图片

如果你因为 SSL 问题无法连接你的数据库,你可以查看这篇文章作为参考。基本上,您需要为您的驱动程序添加两个属性,即“useSSL”和“allowPublicKeyRetrieval”。您可以右键单击您的连接,然后选择“编辑连接”来编辑连接。

作者图片

如上所示,将打开相同的“连接设置”窗口。单击“编辑驱动程序设置”按钮打开“编辑驱动程序”窗口。然后在打开的窗口中单击“驱动程序属性”选项卡。在窗口中右键单击,为您的驱动程序添加两个新属性,即“useSSL”和“allowPublicKeyRetrieval ”,它们的值应分别设置为“false”和“true ”:

作者图片

保存对驱动程序属性的更改。

为您的连接设置名称

您还可以为您的驱动程序设置一个名称,这在您需要在 DBeaver 中管理多个数据库时非常有用。在“连接设置”窗口中,单击“常规”选项卡并设置连接的名称:

作者图片

设置 SQL 编辑器格式

DBeaver 最吸引人的特性之一是代码自动完成、自动大小写转换和语法突出显示。这非常方便,因为 SQL 查询非常灵活。您可以用任何风格编写您的查询,它们将永远有效。然而,说到可读性,那就是另一回事了。DBeaver 的自动完成和自动格式化特性可以让您非常方便地编写干净、美观、易读的 SQL 查询。

我们可以为单个连接设置 SQL 编辑器格式。但是,最好全局设置格式样式,以便它适用于所有连接。

选择“窗口”菜单,然后选择“首选项”子菜单。在打开的“首选项”窗口中,选择“编辑器”= >“SQL 编辑器”= >“格式”。对于格式,建议按如下方式设置配置:

作者图片

有了这些设置,如果您选择您的 SQL 查询并按下 CTRL + SHIFT + F 来自动格式化它们,您的 SQL 查询将被相应地格式化。

启用关键字自动大小写转换

另一个我非常喜欢的便利功能是关键字自动大小写转换,即在键入 SQL 关键字后,它会自动转换为相应的大小写,即上面步骤中设置的大写。要启用关键字自动大小写转换,请在上面打开的“首选项”窗口中选择“编辑器”= >“SQL 编辑器”= >“代码编辑器”,然后启用“转换关键字大小写”。您也可以启用您喜欢的其他代码转换:

作者图片

在 DBeaver 中编写和执行 SQL 查询

您可以右键单击左侧导航栏上的数据库连接,然后选择“SQL Editor”= >“New SQL script”。将为您打开一个新的 SQL 脚本编辑器。现在您可以开始享受在 DBeaver 中编写 SQL 查询了,尤其是关键字自动大小写转换和代码自动完成😃。

出于演示目的,将 SQL 查询从复制到这里,并粘贴到 SQL 编辑器中。然后单击“执行 SQL 脚本”按钮,逐个执行它们:

作者图片

探索高级副本

默认情况下,如果您复制多条记录,它们将被复制为多行,如果您希望将输出用作其他查询或程序的输入,这是不方便的。通过“高级复制”功能,您可以进行更灵活的设置,并以您想要的格式复制数据。例如,选择如下所示的所有名称,右键单击选择,然后选择“高级复制”= >“高级复制…”。在打开的“选项”窗口中,根据需要设置格式选项:

作者图片

使用这些选项,记录将作为一个单独的字符串被复制,每个条目都被引用,然后可以在其他地方直接使用。

作者图片

您还可以将所有行复制为 Markdown,这便于共享查询和结果。首先选择你要复制的记录,然后右键点击选中的记录,选择“高级复制”= >“复制为 Markdown”,你会得到 Markdown 格式的结果,可以方便的分享。

作者图片

以友好的方式显示 JSON 数据

如果您的表中有一个 JSON 字段,它将显示为一个不容易阅读的长字符串。您可以以非常用户友好的方式显示 JSON 字段的值,如下所示:

作者图片

以各种格式导出结果

您还可以以各种格式导出查询结果:

作者图片

作者图片

只需选择您想要的格式,并根据说明导出数据。

使用快捷方式转换为大写

最后但同样重要的是,我们可以选择一些字符并按下 CTRL + SHIFT + X 将它们转换成大写字母。这可能看起来微不足道,但如果你像我一样是一个 SQL 完美主义者,这是非常方便的。此外,该快捷方式是隐藏的,不能直接在顶部菜单或右键菜单中找到。

DBeaver 是一个非常方便的图形数据库管理工具,可以用于各种数据库。它使用起来相当直观,你可以立即上手。本文中的技巧旨在探索 DBeaver 的一些特性,这些特性对于初学者来说可能不太明显,但可以显著提高您的效率。

相关文章:

使用 NLP 将歌曲分类到播放列表

原文:https://towardsdatascience.com/songs-to-playlist-classification-using-nlp-1821c66b1a16

使用 word2vec 和逻辑回归将新歌曲分配到 Spotify 播放列表的指导方法

照片由 Unsplash 上的 israel palacio 拍摄

本文将介绍一个 NLP 项目,旨在将歌曲分配到播放列表。

已经从 Spotify 中选择了两个播放列表,并且通过 Spotify API 选择了诸如艺术家、歌曲标题、流行度等信息。已下载。歌词数据不是通过 API 获得的,而是通过网络搜集获得的。

接下来,对原始歌词执行一些数据预处理步骤,以便训练 Word2Vec 模型并将文本编码成高维向量。

使用 PCA 生成每个播放列表的 2D 表示,并且我们最终使用新歌来完成播放列表分配的任务。这个任务是通过一个逻辑回归模型解决,并给出了一个图形表示。

以下是所用方法的概述:

作者图片

目录:

1 —数据
1.1 —使用 Spotify API
下载播放列表信息 1.2 —抓取歌词
1.3 —数据预处理
1.4 —播放列表概述与世界云
2 —歌词嵌入
2.1 — Word2Vec 模型概述
2.2 3.2 —绘制播放列表质心
3.3 —用逻辑回归进行歌曲分类
4 —结论
参考文献

1.数据

这篇文章是基于这两个 Spotify 播放列表上的歌曲歌词:

  • 第一个是全球播放列表,其中包括全*台用户收听的前 50 首歌曲。播放列表每天更新,通常包括流行歌曲。
  • 第二个播放列表是金属混音,包括排名前 50 的金属歌曲

两个播放列表的音乐流派似乎相当“遥远”:让我们验证这个“距离”是否也存在于我们将使用 NLP 模型获得的向量中,并让我们确认这是否将帮助我们完成播放列表分配任务。

1.1 使用 Spotify API 下载播放列表信息

为了使用 Spotify API,首先,我们应该在 https://developer.spotify.com/的上创建一个开发者账户,并创建一个新的应用程序。

接下来,通过点击我们的应用程序,我们可以获得:

  • 客户端 ID
  • 客户机密

作者图片

有了以上信息,我们可以连接到 API:

最后,通过传递播放列表 id(我们可以从 Spotify 上的 URL 获得),我们可以开发一个函数来获得我们想要的信息,具体来说就是:艺术家、标题、专辑、流行度。

(类型是手动创建的字段,用于区分两个播放列表)。

作者图片

1.2 刮歌歌词

在撰写本文时,Spotify API 不允许提取歌词。然而,通过艺术家和标题,我们可以执行一些网络搜集来获得歌词。

为此开发的代码相当长,超出了本文的范围。我创建了一个关于网络抓取的教程,我在这里引用了。

完成这一步后,数据最终看起来像这样:

作者图片

文章中使用的数据已经上传到这个 github 库上。

1.3 数据预处理

我们的目标是使用 Word2Vec 模型将单词嵌入到向量中。

为了做到这一点,我们将继续下面的步骤,以便原始文本可以被清理并转换成模型可以使用的格式:

  • 删除无用的行(如歌词中的“重复行”、“合唱”)数字、符号
  • 降低每个单词
  • 单词词条化:我们更喜欢词条化而不是词干化。这两种技术在项目中给出了非常相似的结果,但是词干化会截断单词,词汇化会将单词转换成一种常见的“基本”形式,从而给出更具可读性的结果。例如,单词“caring”将通过词干化被截断为“car ”,但通过词汇化被转换为“care”
  • (此处保留了通常在 NLP 预处理步骤中消除的停用词。删除它们并没有改善这个项目的结果——可能是因为词汇表不是很大,Word2Vec 模型可以在上下文评估中使用它们)
  • (最后,单词标记化将在模型训练时执行)

在这些步骤之后,每首歌曲都被转换成一个列表,每个列表元素都是一行歌曲:

作者图片

1.4 世界云播放列表概述

现在,我们将通过生成两个世界云图来初步了解这些数据,每个播放列表一个。

为了产生下面的图像,所有歌曲的歌词被合并成两个变量,每个播放列表一个,然后使用遮罩生成世界云。

这些图表基于单词在每个播放列表中的出现次数,突出显示出现次数最多的单词:

金属播放列表|作者图片

全球 plyalist |作者图片

2.歌词嵌入

为了训练一个能够将新歌曲分配到播放列表的模型,我们需要将歌词嵌入到向量中。

有几种策略可以做到这一点,这些策略可以分为两类:

  1. 基于词频的模型,这些被称为“单词包”方法,如 Tf-Idf 模型或 N-grams
  2. 使用简单神经网络提取向量表示的模型,该向量表示考虑了单词之间的“距离”(或相似性)。这些模型更先进,它们的出发点是出现在同一语境中的两个词在意义上应该“接*”。因此,有了这些模型,就有可能根据单词出现的上下文来捕捉它们之间的相似之处。这些模型可以进一步分为两种方法:
  • 全局矩阵分解方法(如 LSA、LDA)
  • 本地上下文窗口方法(如使用 cbow / skip-gram 的 Word2Vec)。这将是我们的重点领域。

(注意:还有许多其他方法可以使用,例如 GloVe,它旨在使用完整的语料库从词出现的分布中提取词义— [1] Pennington et al .,2014 )。

Word2Vec 将在这个项目中使用,因为 Python Gensim 库提供的内置方法正好满足我们的目的。

2.1 Word2Vec 车型概述

Mikolov 等人(2013 年)[2]开发了这个模型,它由一个单隐层神经网络组成,在单词分类任务上进行训练。该网络学习单词与其上下文的句法和语义关系(在给定窗口中使用前面的和前面的单词)。

在该模型中有两种可能的算法可以使用:Skip-gram 和 continuous bag-of-words (CBOW)。

在 skip-gram 变体中,目标是在给定单词的情况下预测单词上下文;而 cbow 是镜面反射的:它的目标是预测给定上下文的单词。

skip-gram 模型在我们的项目中表现更好,其架构如下:

图片由 Mikolov 等人提供(2013 年)[2]

为了获得成功预测周围单词的单词的矢量表示,给定我们的当前单词,该算法最大化观察一些上下文单词的*均对数概率:

图片由 Mikolov 等人提供(2013 年)[3]

在公式中:

  • c 是“窗口”,或一个词的上下文的大小。这是一个用“单词”表示的模型超参数,例如,7 表示我们在当前单词 w_t 周围使用 7 个单词的上下文
  • t 表示训练规模(即语料库中用于训练模型的所有单词)
  • 条件概率由以下 softmax 函数定义,其中“v”表示神经网络中单词 w 的输入和输出向量:

图片由 Mikolov 等人提供(2013 年)[3]

最终输出将是期望维度(另一个模型超参数)的向量(词汇表中每个单词一个)。

这些向量应该高精度地表示单词之间的关系。比如米科洛夫等人。,2013 [3]获得了可用于执行线性运算的向量,如: - + 将给出非常接*对应于的向量的结果。

2.2 概率*似值

上述最大化需要计算对数概率的梯度。

米科洛夫等人的论文。,2013 [3]示出了该梯度的计算如何具有 O(V)的复杂度,即与词汇表的大小成比例(通常是巨大的)。这是实施方面的一个瓶颈。

然而,同一篇论文提出了以更有效的方式实现 word2vec 的几种策略,例如:

  • 通过分级 softmax 得到的 softmax 的*似值。这通过使用二叉树简化了输出图层表示,从而减少了参数的数量。
  • 负采样:这种技术允许从数据中区分“噪声”,通过训练模型从噪声分布中采样我们的目标词。

这两种技术都在项目中使用,分层 softmax *似给出了更好的结果。

3.结果讨论

3.1 Word2Vec 模型培训

为了训练 word2vec 模型,我们首先对歌词进行标记,并选择在分类任务中表现最佳的超参数,我们将在下面介绍:

  • sg = 1 以使用跳格算法
  • hs = 1,使用分级 softmax 方法来*似概率
  • vector_size = 300 意味着每个单词的输出向量将有 300 个坐标。这与在整个 Google 新闻语料库上训练的 word2vec 模型使用的向量维度相同
  • window = 7 意味着我们的上下文将基于 7 个单词。在这个项目中,使用了不同的窗口大小,5 到 10 之间的数字给出了最好的结果。
  • min_count = 3 意味着出现次数少于 3 次的单词将从语料库中排除。这将过滤掉那些很少使用的单词,从而减少我们的词汇量。

使用这些值,模型训练得非常快,这个小数据集上的词汇大小为 1223。

3.2 绘制播放列表质心

训练之后,我们在播放列表中获得每个单词一个向量。因此,我们可以计算:播放列表质心,播放列表中最具代表性的单词。

  • 首先,我们提取了对应于播放列表中每个单词的向量,并对它们进行*均以获得质心
  • 然后,我们提取与质心相似的词,这些词是每个播放列表中最有代表性的词。

每种金属和全球播放列表的“前 10 个单词”分别是:

作者图片

有趣的是,我们注意到最有代表性的单词并不一定与最常用的单词相对应(如上面的单词云图片所示)。正如所料,该算法超出了词频来计算向量坐标。

我们也可以把这些单词形象化,我们将使用主成分分析来表示 2D 的 300 坐标向量:

作者图片

类似地,我们可以绘制两个播放列表质心的 PCA:

作者图片

我们可以根据最有代表性的单词和两个质心来分离这两个播放列表。让我们利用这些距离将新歌曲分配到播放列表。

3.3 用逻辑回归对歌曲进行分类

我们现在将使用我们的向量和播放列表标签来训练逻辑回归模型。该模型将在由 6 首未被观察到的歌曲组成的测试集上进行测试。(这些歌曲以前是 Spotify 上这两个播放列表的一部分,但现在不再是它们的一部分了)。我们将确认模型是否会将歌曲分配到正确的播放列表:

作者图片

逻辑回归是广义线性回归模型,这是一种非常常见的分类技术,特别是用于二元分类(2 类。然而,该模型也适用于多类分类问题)。

该模型返回记录属于“类 1”的概率;可以设置阈值,以便“硬”将记录分配到“类别 1”,仅当概率高于阈值时。

数据预处理:

让我们首先准备数据集:

  • 我们将使用已经导入的歌词来计算每首歌的*均向量。这将是我们的火车布景。
  • 我们将上传新歌曲,并使用相同的 word2vec 模型计算每首歌曲的向量(*均单词向量)。这将是我们的测试集。

超参数调谐:

接下来,我们运行交叉验证网格搜索,以确定最佳模型超参数:

模型训练&测试:

我们现在可以在训练集上训练最佳模型,然后通过对新歌曲进行分类来测试它:

我们获得了以下预测的标签和性能:在训练集上的非常高的准确度和在测试集上的一首错误分类的歌曲。

作者图片

图形概述:

通过对 Word2Vec 模型向量使用 PCA,现在可以可视化测试集歌曲(每个点代表一首歌曲,通过对单词向量进行*均获得)。让我们检查一下新歌离两个播放列表质心有多*:

作者图片

使用这种表示,我们还可以可视化新歌曲是否非常接*播放列表的质心,从而使分类更加健壮。事实上,我们可以看到为什么一首歌被错误分类:它更接*“错误的”播放列表的质心。

4.结论

本文介绍了一种基于歌词将新歌曲分配到现有播放列表的可行策略。

使用 word2vec 进行歌词嵌入,使用 logistic 回归进行分类,取得了很好的效果。

为了推广这种策略,可以比较不同的嵌入技术和不同的回归模型,理想情况下使用大得多的数据集,这通常会改进单词嵌入任务。

感谢您的阅读!

参考

发音——当你超越了数据的视觉表现

原文:https://towardsdatascience.com/sonification-when-you-go-beyond-the-visual-representation-of-data-cf6c7229a557

以声音为指导,交流和寻找模式

这张照片看起来像别的吗?像声波一样?照片由来自 Unsplash 的 Adrien Olichon 拍摄

"没有声音,庆祝和悲伤看起来几乎是一样的."(本·马库斯

声音为我们对周围环境的感知提供了独特的东西。

我总是记得走进所有这些历史地标,看到它们陈设齐全、漂亮,但同时却毫无生气。

然而,一旦你在那些房间里演奏当代音乐(或音效),它们就会突然变得活跃起来。你会立刻被传送到那个时间点。

你可能会参加纪念路易十四的盛大舞会,或者见证《独立宣言》的撰写。

声音给原本处于休眠状态的物体、地方和时刻带来了生命。当你对数据进行声音处理时,也会发生同样的情况。

这是健康发展的下一个前沿。

什么是数据语音化?

这是传递信息的另一种方式。

当你展示数据时,你通常使用视觉提示来展示对你工作的分析。但是在这种情况下,你用让事情更容易理解。

想想盖革计数器。这是一种测量周围辐射水*的电子设备。如果你去了切尔诺贝利,你可能会带上其中的一个,避免太靠*那些会损害你健康(永久)的地方。

为了保证你的安全,这个设备会发出声音,代表你暴露在辐射中的程度。听到的咔哒声越多(间隔越少),辐射越高。

那是你的“离开那里”的信号。

这里有一个更直观的数据转换成声音的表示。在这种情况下,作曲家和计算机科学家 Brian Foo 希望用声音和视觉来表现北京空气污染在 3 年时间内的变化。

在音乐的基础层(一种重复的钢琴般的声音,节奏适中)之上,布莱恩创建了一种算法,根据该市每天的空气质量测量结果来改变这首歌的声音和光学元素。

截图 1 — 通过维基百科对北京空气质量的发音

一方面,当空气质量恶化时,你会在音乐中听到更多打击乐和噪音成分【截图 1】。这也将伴随着快节奏的笔记和图表中更高密度的点。

另一方面,当空气污染改善[截图 2]时,这些有节奏的部分会逐渐消失,音乐会变得更加悦耳,这也导致了饱和度较低的图形。

截图 2 — 通过维基百科对北京空气质量的发音

通过这个发音实验,布莱恩对一个紧迫的问题创造了一个独特的视角,并帮助提高了人们对这个问题的认识。

这只是两个例子,说明声音反馈如何帮助你以不同的方式感知世界,有时甚至更容易。声音可以是传送数据序列的有用且强大的工具。

你用什么技术把数据转换成声音?

同样的数据,你可以从中创建视觉图表,你也可以把声音和音乐。你可以使用不同的声音元素(音高、音量、速度)来让用户意识到你试图传达的信息的变化。

例如,安布罗斯·索恩是一家名为 ChirpShop 的音频品牌公司的联合创始人,他利用音调的变化来提高人们对气候变化的认识。

通过获取全球温度变化的历史数据,他将更极端的气候事件(如热浪)指定为更高的音高,将更低的音高指定为较冷的年份。

换句话说,对于 1880 年到 2016 年之间的每一年,他都会给一个温度值分配一个特定的音高(以 MIDI 格式)。

一旦他将它们绘制出来,他会将该文件导入到 DAW(数字音频工作站)中,在那里他会将一个虚拟乐器分配给一系列音高(实际上是音符)以将它们转换成音乐。

瞧啊!这是这个实验的结果声音图。

这样做的好处是什么?数据变活了!曾经只是对图表的概念性理解,现在变成了对信息的更深层次的理解。你参与到你所听到的事情中,并且能够感受到这些数据。

这不再仅仅是一连串的数字,而是与数字背后的智慧的个人联系。

还有其他获取信息的方式:

  • 可听化 :是在我们的听觉范围内(20Hz 到 20kHz)将数据值表示成声音的一种方式。例如,当你的医生将一个听诊器贴在你的皮肤上时,他们能够听到心脏、肺、肠甚至血液流经你的静脉和动脉时发出的声音。他们把风琴的声音变成他们容易听到的声音。
  • 基于模型的发音 :这是一个人与它互动时产生的声学反应系统。你建立一套指令(例如,当用户靠*时,提高音调),并告诉用户如何与之交互。例如,如果你见过一种叫做 特雷门 的乐器,你就会知道,根据你如何挥动你的手,你会演奏出不同的音高、发音和音量,从而产生一种非常特别的音色。

我打赌你以前没想过这样的声音,是吗?

体验音频表现

我们感知声音和图像是不同的。当人们说一张图胜过千言万语时,一个就会让你哑口无言。它超越了语言的范畴,很多时候你会发现自己无法描述你所听到的。

但是直到你找到一个参考框架,它会引导你找到有价值的信息。

让我们看一些例子来更好地理解这一点。

最基本的是一个时钟的滴答声。如果没有时间分割,这种声音不会传递任何有用的数据。但是一旦你明白它每秒滴答响,每小时响一次,你就能更恰当地使用这些信息。基本上,你听到的是一个听觉提示(滴答/响)来代表一个数据(秒/小时)。

然后你有不同程度的复杂。

你可以用声音来代表图像

假设你有一张照片,你想把它转换成声音。一个叫做 Photosounder 的软件可以让你把像素变成高低音高。你在这个软件上从左到右处理图像。

照片主图中包含的每个像素都将获得独特的间距。因此,如果是一个圆形,你首先会听到中间的一个音高,然后是两个方向相反的音高(一个高,一个低),最后以中间的音高结束。

这里有一个香蕉的例子,让你更容易理解。

一组更加复杂的数据是将新冠肺炎刺突蛋白的氨基酸序列翻译成声音。多亏了麻省理工学院工程学教授和音乐作曲家 Markus Buehler 的工作,研究人员现在可以通过声音的透镜听到它的结构。

这可以刺激科学家从另一个角度看待问题,增强横向思维。如果你好奇的话,这里是这种病毒的真实声音表现。

外卖

我们用来理解数据的技巧之一是将数据显示为图表、图形、信息图等等。除了用视觉术语显示数据,为什么不试着用声音来表示呢?

有一个被称为发音的发展领域,它不仅可以帮助人们与他人交流想法,还可以帮助我们感知传统视觉可能忽略的模式和趋势。

它有助于让信息鲜活起来。当你能够听到而不仅仅是看到事情时,你会有不同的感觉。你周围的空间充满了振动,可以到达你灵魂的最深处。感觉很有意义。

通过发音,你可以给科学增加一种感性的理解。

当你开始体验它时,声音和音乐会成为强有力的工具,但前提是我们要学会如何恰当地使用它们。

如果你喜欢这篇文章,试着读一下:

https://medium.com/general_knowledge/how-to-become-a-music-sommelier-2966903da766 https://medium.datadriveninvestor.com/when-fans-become-investors-the-use-of-nfts-in-the-music-industry-59bf3172d041 https://medium.com/predict/were-entering-into-a-post-screen-future-sound-is-the-way-7795db0ee022

如果你喜欢阅读这样的故事,并愿意支持 Medium 上的作家,考虑 注册成为 Medium 会员。每月只需 5 美元,你就可以无限制地阅读世界各地优秀作家的文章。

对不起女士们…这仍然是一个男人的世界!

原文:https://towardsdatascience.com/sorry-ladies-this-is-still-a-mans-world-1e5195c5fcbd

公*和偏见

对不起,女士们……这仍然是一个男人的世界!

算法如何助长性别偏见

克里斯蒂安·布纳在 Unsplash 上拍摄的照片

到现在为止,你一定听说过,也许已经忘记了亚马逊的自动招聘工具基于性别的歧视(Dastin,2018)。但是性别偏见兔子洞在人工智能中到底有多深?除了女性的简历被财富 500 强公司的可怕算法忽略之外,还有什么其他后果?本文将快速探究一系列涉及性别问题的不幸事件,旨在探索数据集内的不一致如何影响算法偏差,以及这些暗示对特定群体可能意味着什么。

重复亚马逊的故事

经过适当训练的人工智能模型是已经学会基于具有代表性和包容性的高质量数据集进行预测的模型(Buolamwini 等人,2018)。然而,显然并非在所有情况下都是如此,因为训练算法的数据可能会过度代表一个群体。亚马逊的失败证明了这一点,他们的招聘模式可以识别模式,并根据 10 年来主要来自男性的申请做出预测,从而提出对他们有利的建议。

这一事件凸显了数据收集对算法训练过程的重要性:如果在深度学习发生时,训练数据是基于偏见的,那么结果将对参与倾向产生负面影响。换句话说,如果算法一次又一次地被输入某种类型的数据,它不会奇迹般地自动包含它没有学到的东西,从而导致人工智能中的排他性和偏见放大的支持,正如上面的场景所证明的那样——多少有才华的女性因为亚马逊的性别歧视招聘工具而被剥夺了机会?

UnsplashElisa Ventur 拍摄的照片

更进一步说,自适应算法有能力调整其学习以促进新的发展,从而做出最佳决策;因此,这可能解释了为什么亚马逊的招聘模式仍然处于有偏见的循环中(拉万基 2018)。但是这对普通人来说意味着什么呢?我是说,吉尔?

算法是人类工作的结果,从数据收集一直到模型训练,这是问题的关键:组成该领域的专业人员中只有 22%是女性(Young 等人,2021 年),这强化了为什么代表性不足会严重影响结果。如果那些管理机器学习过程的人在确定需要什么和多少训练数据来提高准确性时,不考虑与性别动态相关的公*和道德特征,最终结果将是不具代表性和不可取的结果。

禁果——女人 Vs 苹果

照片由肖恩·孔Unsplash 上拍摄

现在,想象一下,亚马逊的招聘工具甚至不考虑你的申请,因为你是女性,却被苹果公司的性别歧视信用额度打了一巴掌。2019 年,有人对 Apple card 似乎厌恶女性的偏好进行了几次投诉;虽然调查清除了这家科技巨头的任何算法“违规行为”——对* 40 万名申请人的核保 数据进行了彻底检查(沙欣等人,2021)——但一些非常重要的问题仍有待回答。

谁探测了算法?是否对训练数据集进行了可能未被发现的偏差检查?教授模型的数据是如何收集和丰富的?这些肯定是最基本的步骤,以确保用于确定苹果公司信用价值的算法实际上是性别中立的,而不仅仅是筛选一些记录来迅速安抚大众:这与正在讨论的机器驱动程序无关,该程序对一种性别抱有偏见。

如果算法开发的决策过程在很大程度上是由男性主导的(D'Ignazio 等人,2020),那么机器智能的设计师在多大程度上系统地和自愿地消除他们创建的模型中存在的遗传偏见,以消除性别偏见?反对使用基于肤浅的男女二分法的主观数据标签的动力是什么?充满传统性别观念的训练数据将永远产生增加有害的意识形态不对称的统计模型,其影响表明性别不*等的永久化(Leavy,2018)。

Bowdlerizing 偏见——性别动态中的公*

照片由拍摄那是她的事Unsplash

一个人不需要非常理智就能感知“服务器机房中的大象”(D'Ignazio 等人,2020):有偏见的人工智能模型来自有偏见的历史训练数据,而这些数据又来自有偏见的人类特征。为此,如果要在算法程序中实现性别*等,那么具有代表性和标准化的历史数据应该用于模型训练目的。

这需要重新评估算法的开发方式,包括对数据收集过程的仔细审查,对数据的仔细管理,以及在深度学习过程之前、期间和之后对人类交互、干预和干扰训练数据的彻底检查。Gebru 等人(2018 年)进一步提出,数据集应附有详细说明其应用、设计、传播、基本原理、框架和延续的规范,以期增强问责制和透明度。

偏见极大地影响规范行为和社会原则;因此,必须消除这种现象,以阻止不断强化旨在征服的意识形态——否则就永远不会享有社会正义。因此,应该不断扩大关于机器学习中性别偏见的对话,以便开发最佳实践,消除支持霸权力量的人工智能特征,努力过渡到更加民主、包容和公*的数字经济。

文献学

BBC (2019)苹果公司的“性别歧视”信用卡被美国监管机构调查。可在线查阅:https://www.bbc.co.uk/news/business-50365609[2021 年 12 月 28 日查阅]。

Buolamwini,j .和 Gebru,T. (2018 年)。机器学习研究会议录 81:1–15。 2018 年公*、问责和透明性别阴影会议:商业性别分类的交叉准确性差异。纽约,2 月 23 日至 24 日,1 日至 15 日。

杰弗里·达斯廷(2018)。亚马逊废弃了显示对女性有偏见的秘密人工智能招聘工具。网上有:https://www . Reuters . com/article/us-Amazon-com-jobs-automation-insight/Amazon-scraps-secret-ai-recruiting-tool-that-show-bias-against-women-iduskcn 1 MK 08g[2021 年 12 月 28 日访问]。

D'Ignazio,c .和 Klein,L. (2020 年)。数据女权主义。剑桥:麻省理工学院出版社。

Gebru,j . Morgenstern,b . Vecchione,b . wort man Vaughan,j . Wallach,h . daum III,h .和 Crawford,K. (2018 年)。数据集的数据表。可在线查阅:https://www . Microsoft . com/en-us/research/uploads/prod/2019/01/1803.09010 . pdf[2022 年 2 月 1 日查阅]。

Lambrecht,A 和 Tucker,C E (2019 年)。算法偏差?STEM 招聘广告中明显性别歧视的实证研究。管理科学,65 卷 7 期。第 2966-2981 页。

拉万基,男(2018)。亚马逊性别歧视的招聘算法仍然可能比人类更好。期望算法完美执行可能对我们自己要求过高。可在线查阅:https://www . IMD . org/research-knowledge/articles/amazons-sex ist-hiring-algorithm-can-been-be-better-than-a-human/[2021 年 12 月 28 日查阅]。

Leavy,S. (2018)人工智能中的性别偏见:机器学习中对多样性和性别理论的需求 Susan Leavy。在线提供:https://dl.acm.org/doi/pdf/10.1145/3195570.3195580?casa _ token = 01 yph _ loxi 4 aaaaa:G1 bwwkoywh 4 ovozgbvhtggm _ w5 cxdrot 5 ujgpzjpmbo 0 liv dnxdnjpoojlzzghve-fahiehymkdeg。[访问日期:2022 年 1 月 1 日]。

Nasiripour,s .和 Farrel,G. (2021)高盛在纽约审查苹果卡时澄清了偏见。网上有:https://www . Bloomberg . com/news/articles/2021-03-23/Goldman-not-discrimina-with-apple-card-n-y-regulator-says。访问日期:2022 年 1 月 1 日。

史密斯和鲁斯塔吉(2021 年)。当好的算法变得性别歧视:为什么以及如何推进人工智能性别*等。斯坦福社会创新评论https://doi.org/10.48558/A179-B138

Young,e .,Wajcman,j .和 Sprejer,L. (2021 年)。女人在哪里?绘制人工智能中的性别工作差距。政策简报:完整报告。艾伦图灵研究所

如何对熊猫数据帧进行排序

原文:https://towardsdatascience.com/sort-pandas-df-5748eaadcd4f

使用一列或多列对熊猫数据帧进行排序

Markus SpiskeUnsplash 上拍摄的照片

当检查我们的数据时,我们有时可能想要或者甚至不得不基于一个或多个列对它进行排序。这个简单的过程可以帮助我们调查一个特定的用例,探索边缘案例等等。

在今天的教程中,我们将详细解释如何对熊猫数据帧进行升序或降序排序。此外,我们还将演示在对数据进行排序时如何使用一个或多个列。我们甚至将讨论如何按升序对列的子集进行排序,并按降序对剩余的子集进行排序。

首先,让我们创建一个示例数据帧,我们将在整个教程中引用它,以演示一些概念并展示如何有效地对 pandas 数据帧进行排序。

import pandas as pd df = pd.DataFrame(
    [ 
        (1, 'A', 140, False, 3.5),
        (2, 'B', 210, True, 4.0),
        (3, 'A', 562, True, 1.1),
        (4, 'D', 133, False, 2.3),
        (5, 'C', 109, False, 9.8),
        (6, 'C', None, True, 3.9),
        (7, 'B', 976, False, 7.8),
        (8, 'D', 356, False, 4.5),
        (9, 'C', 765, True, 2.1),
    ],
    columns=['colA', 'colB', 'colC', 'colD', 'colE']
)print(df)
 ***colA colB   colC   colD  colE*** *0     1    A  140.0  False   3.5
1     2    B  210.0   True   4.0
2     3    A  562.0   True   1.1
3     4    D  133.0  False   2.3
4     5    C  109.0  False   9.8
5     6    C    NaN   True   3.9
6     7    B  976.0  False   7.8
7     8    D  356.0  False   4.5
8     9    C  765.0   True   2.1*

[pandas.DataFrame.sort_values()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sort_values.html)是根据特定条件对数据帧进行排序时需要使用的方法。在接下来的几节中,我们将讨论一些潜在的用例,并演示如何使用sort_values来推断出想要的结果。

按一列排序

现在让我们假设我们想要根据列colC的值对我们刚刚创建的数据帧进行排序。我们需要做的就是在by参数中指定列名:

>>> df.sort_values(by='colC')

结果将包含按colC升序排列的所有记录(默认)。

 ***colA colB   colC   colD  colE*** *4     5    C* ***109.0*** *False   9.8
3     4    D* ***133.0*** *False   2.3
0     1    A* ***140.0*** *False   3.5
1     2    B* ***210.0*** *True   4.0
7     8    D* ***356.0*** *False   4.5
2     3    A* ***562.0*** *True   1.1
8     9    C* ***765.0*** *True   2.1
6     7    B* ***976.0*** *False   7.8
5     6    C    NaN   True   3.9*

或者,您可以指定ascending=False来对列colC上的数据帧进行降序排序:

>>> **df.sort_values(by='colC', ascending=False)**
 *colA colB   colC   colD  colE
6     7    B  976.0  False   7.8
8     9    C  765.0   True   2.1
2     3    A  562.0   True   1.1
7     8    D  356.0  False   4.5
1     2    B  210.0   True   4.0
0     1    A  140.0  False   3.5
3     4    D  133.0  False   2.3
4     5    C  109.0  False   9.8
5     6    C    NaN   True   3.9*

按多列排序

现在让我们假设我们想要基于两列,即colAcolC,对 DataFrame 进行排序。这次我们需要做的就是以列表的形式提供列名,并将其传递给by参数:

>>> df.sort_values(by=['colB', 'colC'])

上面的语句将根据列colBcolC的值对数据帧进行升序排序:

 ***colA colB   colC   colD  colE*** *0     1    A  140.0  False   3.5
2     3    A  562.0   True   1.1
1     2    B  210.0   True   4.0
6     7    B  976.0  False   7.8
4     5    C  109.0  False   9.8
8     9    C  765.0   True   2.1
5     6    C    NaN   True   3.9
3     4    D  133.0  False   2.3
7     8    D  356.0  False   4.5*

但是请注意,列名的指定顺序很重要——换句话说,sort_values(by=['colB', 'colC']sort_values(by=['colC', 'colB']不会产生相同的结果:

**>>> df.sort_values(by=['colC', 'colB'])
**   colA colB   colC   colD  colE4     5    C  109.0  False   9.8
3     4    D  133.0  False   2.3
0     1    A  140.0  False   3.5
1     2    B  210.0   True   4.0
7     8    D  356.0  False   4.5
2     3    A  562.0   True   1.1
8     9    C  765.0   True   2.1
6     7    B  976.0  False   7.8
5     6    C    NaN   True   3.9

对多列按升序或降序排序

接下来,我们甚至可以在对多个列进行排序时指定每个列的排序顺序。这意味着我们可以对一列进行升序排序,而对其他列可以进行降序排序。

为了实现这一点,我们需要做的就是指定一个包含布尔值的列表,这些值对应于在by参数中指定的每一列,并表示我们是否要按升序排序。

以下命令将根据colB(升序)和colC(降序)对数据帧进行排序。

>>> df.sort_values(by=['colB', 'colC'], ascending=[True, False])
 ***colA colB   colC   colD  colE*** *2     3    A  562.0   True   1.1
0     1    A  140.0  False   3.5
6     7    B  976.0  False   7.8
1     2    B  210.0   True   4.0
8     9    C  765.0   True   2.1
4     5    C  109.0  False   9.8
5     6    C    NaN   True   3.9
7     8    D  356.0  False   4.5
3     4    D  133.0  False   2.3*

处理缺失值

你可能已经注意到,空值总是被放在结果的最后,不管我们选择什么顺序。

我们可以通过简单地指定na_position='first'(默认为'last')来改变这种行为,将它们放在结果的顶部。

>>> df.sort_values(by='colC', ascending=True, na_position='first')
 ***colA colB   colC   colD  colE
5     6    C    NaN   True   3.9*** *4     5    C  109.0  False   9.8
3     4    D  133.0  False   2.3
0     1    A  140.0  False   3.5
1     2    B  210.0   True   4.0
7     8    D  356.0  False   4.5
2     3    A  562.0   True   1.1
8     9    C  765.0   True   2.1
6     7    B  976.0  False   7.8*

最后的想法

在今天的简短教程中,我们演示了熊猫如何进行排序。给定一个相当简单的数据框架,我们展示了如何基于一列甚至多列对数据进行排序。

此外,我们演示了如何按降序或升序排序,甚至决定哪些列需要按降序排序,哪些应该按升序排序。

最后,我们展示了如何选择空值是出现在结果的顶部还是底部,而不管我们选择的记录排序顺序。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章您可能也喜欢

[## 熊猫中的 loc 与 iloc

towardsdatascience.com](/loc-vs-iloc-in-pandas-92fc125ed8eb)

源代码数据集——深入探究 GitHub 特征

原文:https://towardsdatascience.com/source-code-datasets-deep-dive-into-github-characteristics-a26c622e0794

采样 GitHub 时要考虑的亚人群

照片由来自 Pexels 的 Jonathan Borba 拍摄

Github 隐性偏见——为什么它很重要

CodeAI 最*成为了 AI 界的共同话题;从 Github 的副驾驶,通过微软的 CodeBertVsCode 的 GuessLang (PS,你能找到这三者之间的共同联系吗?).机器学习比以往任何时候都更多地应用于源代码领域。推动它的是 NLP 世界最*的重大改进,代码库的高可用性(无论是其 GithubGitlab 还是甚至像 Rosetta Code 这样的开源项目),以及源代码是技术世界中几乎每个人的重要部分这一事实。让我们每个人都成为该领域的专家。但是这也是一个主要的缺点,因为获得源代码数据集的简单性可能会掩盖它所包含的隐含的复杂性。当我们对 Github 的机器学习应用程序进行采样时,我们基本上会搜索类似于推理时间的代码,类似于它将应用到的代码,最有可能是公司内部的代码。但问题是,我们可以假设这些代码与占 GitHub 较高份额的开源和虚拟个人项目有很大不同。天真地没有考虑到这一点,按原样对 Github 进行采样,将最有可能导致我们的模型过度适应手头有偏见的人群,而不是对目标人群进行足够的概括(值得一提的是一个类似的案例,其中许多当局决定禁止面部识别服务,因为训练人群太有偏见)。这就是为什么深刻理解 GitHub 的内部特性如此重要。Github 的主要子群体详述如下。

源代码语言

第一个也是最明显的地方是 Github 的源代码语言发行版。众所周知,Github 有一个超级长尾语言版本(一些语言如 Javascript 非常常见,而其他语言如 C 非常罕见)。考虑到 Github 的社交方面,这并不奇怪。由于同样的现象可以在其他基于社交的技术网站上看到(如 StackOverflow 最常见的问题标签),Github 拥有类似的特征(更常见和流行的源代码语言和技术的比率更高)是有道理的。考虑到天真的 Github 采样将以代理长尾分布结束,这会严重影响机器学习模型,这可能是有问题的。应该强制执行诸如分层抽样的规范化政策,以避免生成的模型过度拟合这种倾斜的语言分布。

账户类型

Github 有两种账户类型 —用户(个人)和组织(公司)。理论上我们会假设它反映了两种不同的使用模式;个人(用户)存储库是辅助/虚拟项目,公司(组织、公共)存储库是开源/文档(鉴于 Github 启用了仅由公司员工访问的私有模式,公共模式应该包括我们希望与世界共享的内容——开源、示例和文档)。事实是,我们可以在这里观察到一个有趣的组合:一些公司向他们的公共帐户发布看似私人的代码,一些用户向他们的个人帐户发布看似他们公司相关的代码。这有许多可能的原因;公司可能会错误地发布代码。用户,尤其是在 covid 期间在家工作的用户,可能缺乏对 Github 上私人用户(公共范围)和公司(私人范围)帐户之间的区别的理解。这种现象没有什么有趣的应用;首先,它增加了在公司的公共存储库和员工的存储库中找到内部机密的可能性。第二,当我们对 Github 进行采样时,我们可能会面对个人和(公共)公司账户的内部代码。虽然 Github 的较高份额仍然是个人账户(在随机抽样测试中,我们面临大约 9:1比率),因此大部分抽样代码很可能是私有的(与公司无关),但如果我们想要将 Github 上的内部专业级存储库作为目标,应该将它考虑在内。

组织账户范围

如前所述,公司账户可以是公共账户或私人账户。考虑到不同的共享范围,假设私有存储库包含更多的公司关键代码是有意义的,这些代码很可能会受到较低标准的影响(因为代码在公司外部是不可见的,缺乏清晰的文档和代码样式不太重要)。开源项目中高标准的一个例子可以在像 Meta 官方账户这样的地方找到..通过开源技术构建社区)。Github 中有许多开源项目类型,其中包括员工仅公司所有(如 Meta,其中“成员必须有两个因素授权”)、一般开放公司所有(如 SalesForce ,其中贡献不限于 SalesForce 员工),以及一般开放,如 VueJs (非公司所有)。这样的账户应该被抽样,以防我们寻找高代码标准或者成为数据集负总体,在那里错误不太可能出现。以防我们寻找更多的内部知识库;其他不太面向开源的公司应该成为目标。Github 启用了高级搜索标准,这对此很有用——通过考虑关注者的数量、存储库的数量甚至账户位置,我们可以有意地将目标锁定在规模更适中的账户上(这里应该出现较低的标准,因此代码应该更类似于公司内部)。

正在使用的技术

正如我们到目前为止所看到的,依赖 Github 作为主要数据源来训练我们的模型可能会产生一种隐含的偏见——虽然我们最有可能针对的是内部代码,但天真随机的抽样群体最有可能由开源和私有项目组成。这可能会影响正在使用的代码标准和技术(例如,看到环境变量或日志记录等专业代码模式的可能性)。反映这一点的一个简单直观的练习可以是,在搜索像谷歌地图这样的超级通用技术时,比较结果的数量(大约 200 万个结果,其中许多似乎是次要项目)与搜索像 Okta 这样的更小众的企业相关技术(大约 9 万个结果,其中许多似乎是内部公司相关的)。另一个相关的证词可以在 Meli at el Github 的泄露秘密分析中找到,他们发现 Google api key 的泄露频率几乎是 Twilio 的 1000 倍(与 Okta 一样,它可以被认为是更专业的代码)。考虑使用中的技术可以突出其他特征,比如我们发现的项目的代码标准(内部代码应该比虚拟项目的质量更高)。因此,依靠这种技术的存在来定位更有可能与内部公司相关的存储库是有意义的。

主题

代码项目可以满足各种需求,从服务器客户端到 Android 原生应用。Github 的存储库目标可以通过查看存储库标记的主题来查看。同时值得一提的是,根据 Izadi 等人的分析,大多数 Github 的存储库都没有主题/正确的主题。如何评估存储库主题的另一个选择是查看源代码语言和正在使用的技术——使用 Angular 的 Javascript 最有可能与客户端相关,而使用 Node.js 的 Javascript 最有可能与服务器相关。C、C++和 C#更有可能是服务器端而不是客户端相关的。正如我们在前面的分析中看到的。搭载谷歌地图的安卓,可以增加成为副业的可能性。作为一个例子,Python 比 Javascript 在数据从业者中更常见,因此它更有可能包含相关的数据科学/数据工程代码。因此,确保我们的数据集跨语言均匀采样不仅对于避免语言过度拟合很重要,而且确保它将跨不同的代码主题通用化。例如,虽然我们的目标用例与 C++的相关性可能不如 Javascript,但确保我们的数据集包含 C++项目仍然很重要,以便获得子样本揭示的相关代码模式的可见性。

用户特征分析

私人帐户可以是不同的成熟级别(虚拟项目对面向开源)和不同的目标范围(附带项目对内部公司相关)。相关的指示可以是专业代码模式的存在,如许可证文件、自述文件文件、专业技术(如 Okta)或者甚至只是测试文件。例如在 Python 上,没有一个 requirements.txt 文件可以表明回购标准较低。考虑到特定的源代码语言特征可以使目标语言具有特定的代码味道(就像 Python 数据从业者一样,使用雪花连接器并将凭证保持为明文代码)。根据 Meli 等人 Github 的秘密分析发现泄露的秘密是一个重复的问题(泄露的秘密更有可能同时出现),我们可以假设我们在一个回购中发现的特征也适用于用户的其他回购。使我们能够通过在一些用户帐户的存储库中观察特定代码模式(如代码味道)来锁定用户。

智能瞄准

Github 有很多子种群。这里介绍了最重要的几个,但是还有很多其他的。我们看到,用户帐户在保密率和代码实践方面更类似于私有(内部)公司,而公司(公共)帐户在使用的技术类型方面更类似于私有公司。在天真地对 Github 进行抽样之前,我们应该首先了解我们寻找的目标项目特征是什么(以及为了产生丰富的负面群体,什么不是)。只有这样,我们才能找到针对感兴趣人群的方法,并真正估计我们的数据集代表它的程度。但无论如何,不要天真地试用 Github,就认为它会工作得很好。在大多数情况下,更聪明的目标是必须的。

SourceCodeAI——对源代码的深度学习——为什么和如何

原文:https://towardsdatascience.com/sourcecodeai-deep-learning-for-source-code-why-and-how-50eba7ff0329

照片由来自 Pexels 的 Enric Cruz López 拍摄

又一个 NLP?

源代码 AI 最*成了热门话题;公司比以往任何时候都更多地在这个方向上花费精力,试图利用它来满足他们的需求。动机很明显;首先,随着人工智能应用从领域专业知识中受益,还有什么比让整个 R&D 组织都成为感兴趣领域的专家更好的呢!第二,鉴于最* NLP 的许多突破,这种技术似乎可以很容易地应用于存在的每个文本领域(包括源代码)。综上所述,将深度学习 NLP 技术应用于源代码似乎是一个非常容易的事情;只需从众多预先训练好的语言模型 变形金刚中选取一个,用于感兴趣的任务。什么会出错?但现实一如既往地更加复杂。距离成功将深度学习应用于源代码还有一英里。主要原因是该领域的许多独特特征,使得这种方法不太相关。让我们更深入地看看源代码领域的一些最重要的挑战。

独特的字典

虽然 Python 的鼓吹者喜欢声称阅读一个好的 Python 程序感觉就像阅读英语 “事实是 Python(和其他任何源代码语言)在构建它的标记上是不同的;有两种主要的标记类型——用户定义的(如变量名、函数名等..)和语言内置(如单词' def ',' len '或字符' = ')。这两者都不会被常见的深度学习 NLP 模型(通常在常规英语语料库上训练)正确识别,从而导致许多“词汇表之外”的场景,已知这些场景会严重影响此类模型的性能。一个解决方案可以是使用专门针对源代码领域的语言模型(例如 code2vec ),然后针对感兴趣的问题进行调整。下一个挑战将是如何使其多语言化。

每种语言的独特词典

而在一般的 NLP 世界中,使用基于英语的模型对于许多类型的应用(如情感分析、摘要等)来说已经足够了..鉴于英语的高度主导地位,在源代码上,我们通常更喜欢使用多种语言模型,来解决相同的问题(如缺陷检测),在各种语言上,一起解决(不仅是 Python,还同时支持其他语言,如 JavaScript 和 Java)。这是学术界和商界的一个主要区别;虽然发表论文的动机可能是为了证明一个新概念(因此应用于单个源代码语言就足够了),但在生产领域,我们希望为我们的客户提供一个限制最少的解决方案,使其尽可能支持更广泛的语言。微软的 CodeBERT 和 SalesForce 的 CodeT5 就是那个方向的例子,刻意训练多语言的语言模型(支持~6 种语言)。这种解决方案的第一个问题是,它们的特定于语言的子模型总是比通用的要好(试着用通用的 CodeT5 模型和 Python 优化的模型总结一个 Python 片段)。另一个更内在的问题是,这种有限的(~6 种语言)支持只是沧海一粟。看看 GitHub 的语言学家的支持语言列表就知道这还不够。即使我们乐观地假设这种模型可以无缝地应用于类似的语言(比如 C 和 C++,因为 CodeBert 支持 Go 语言,而这种语言被认为是非常类似的),那么像 Yaml、XML 和 Clojure 这样语法如此不同的语言呢,假设这种转换不成立是公*的。解决方案可以是尝试采用不太通用的语言模型,而是对感兴趣的问题更优化的语言模型。下一个挑战将是如何满足所需的预测上下文范围。

稀疏上下文

与应该从头到尾阅读的常规文本不同,代码更具动态性,更像是用乐高积木搭建的塔,应该根据具体情况进行编译和评估。例如,考虑一个简单的面向对象的程序,它具有一个基本抽象类(person)的继承%2C%20retaining%20similar%20implementation.)结构,该抽象类具有一个接口(打印 person 职位)、一组实现(对于雇员和经理的不同打印)和一个将接口(打印标题)应用于基本(person)对象的输入列表的函数。让我们假设我们想要训练一个模型来总结这样的函数。我们的模型应该如何理解我们刚刚描述的程序?所需的上下文(打印标题实现)很可能在不同的位置——远在那个类上,或者甚至可能在不同的文件或项目上。即使我们会考虑一个巨大的变压器,看到了很多的背景;由于相关的上下文可能在完全不同的地方,本地上下文可能不足以解决我们试图解决的问题(此外,考虑像封装代码重用这样的最佳实践,这将增加本地上下文稀疏的可能性)。一个解决方案可能是在应用模型之前尝试编译和合并所有相关的代码片段。下一个挑战将是如何考虑各种代码动态状态。

程序状态

不像文本总是有相同的结果,不管我们怎么读(可能不包括 DnD 任务),在代码中,结果取决于我们提供的特定输入。例如,如果我们想要识别空指针场景;由于糟糕的编码,它们可以是静态的(总是发生,不管输入如何),因此应该可以通过读取代码来识别(PS,这就是为什么静态代码分析工具非常擅长发现这些情况),但是它们也可以是动态的,由于糟糕的输入条件和缺乏相关验证,因此应该不太容易通过读取代码来识别。缺少的成分是数据流图;通过了解数据是如何在程序中传播的,模型可以识别代码部分和特定的数据条件何时会出现问题。使用像 Github 的 CodeQL 这样的分析程序数据流的工具可以实现这样的视图。问题是这不是一个简单的任务,而且当考虑多语言需求时(例如 CodeQL只支持大约 7 种源代码语言)。与此同时,它也是神奇解决方案的秘方。一个典型的权衡问题。

总而言之,源代码领域似乎很有挑战性。同时,我们应该考虑阿拉曼尼斯的“自然性假说”,该假说认为软件是人类交流的一种形式;软件语料库具有与自然语言语料库相似的统计特性;这些特性可以用来构建更好的软件工程工具。 NLP 算法应该能够处理源代码任务。我们有责任确保他们以正确的方式看待这些任务,使他们能够正确处理这些任务。如何将深度学习 NLP 技术成功应用于这些领域?

问题理解

第一个常见的解决方案(通常很重要)是在试图解决问题之前正确理解问题域;我们想达到什么目的?业务的最高要求是什么?有哪些技术限制?如果多语言不是强制性的(例如当专门针对 Android 或 Javascript 应用程序时),那么通过放松这种限制,开发将变得更加简单,使我们能够使用特定语言的预训练模型,甚至可以自己训练简单的模型。专注于特定的语言可以使用常规的 NLP 方法,有目的地过度适应目标源代码语言。通过正确理解问题领域,我们可以使用简化,这将使得能够使用通用的最佳实践来解决我们的需求。从超级复杂的跨语言问题到更一般的 NLP 任务的过渡。这本身就足以简化开发周期。

输入范围

为了确保我们选择正确的输入,正确理解我们试图解决的领域也很重要。如果我们试图总结一个独立的函数,那么函数体就足够了。在函数调用具有自我解释的命名约定的外部函数的情况下也是如此。如果不是这样,我们可以考虑提供整个类或者至少相关的函数实现作为输入。如果我们的目标更加面向流程,比如识别存在 SQL 注入风险的区域,那么不仅查看特定的 ORM 实现(比如与数据库交互的 Hibernate 代码)而且考虑查看通向该路径的代码片段是有意义的。主要的问题是,这种方法将复杂性从我们试图训练的模型转移到支持模块,支持模块负责收集所有相关的范围部分。这意味着我们和这些支持模块一样好。给我们的生态系统增加了多余的需求。

数据建模

眼尖的读者可能已经注意到,我们提出的主要问题是关于数据,而不是模型本身。算法很好。他们得到的数据是主要问题。和以前一样,通过正确理解问题,人们可以选择更适合他们需求的数据表示。源代码测试通常用于获得更高层次的功能交互可见性。数据流图通常用于跟踪数据是如何在程序中传播的(这对检测空指针或 SQL 注入等情况很重要)。查看系统调用操作码而不是普通代码,可以隐式地训练多语言模型(使用可以跨不同语言共享的通用接口)。像 code2vec 这样的代码嵌入可以实现对代码片段的高级理解。此外,这些嵌入模型中的一些已经是多语言的,同时满足了这种需求。在某些情况下,我们可以选择自己训练一个多语言模型。然后,重要的是要验证我们代表了所有相关的子群体(考虑到采样代码数据集,特别是依赖 Github 上的并不简单)。幼稚的方法很容易导致严重的隐性群体偏差)。将代码视为纯文本可以处理更简单的任务。它的主要构件之一是决定输入电*;它可以是单词、子单词甚至字符级别。从最具体的语言(单词)到最一般的语言(字符)。 Spiral 是一个开源的例子,它试图通过标准化不同的语言编码风格(像 Camel Case naming)来使基于单词的标记化更加通用。子单词是字符(当模型需要学习识别字典单词时)和单词标记化(当单词已经是输入时)之间的折衷,用于类似的动机;尝试生成一个特定的语料库,同时确保不会有太多的超出词汇表的场景(相关的示例实现有 BPE 和)。对于某些情况,我们可以选择只保留与我们的情况更相关的代码输入部分(比如只保留函数名或忽略内置于标记中的源代码)。问题是,为了满足这种需求,它需要更多的理解,这又给我们的生态系统增加了一个多余的、易受影响的成分。

前方看什么

软件世界将采取的最明显的方向是更加语言不可知的源代码模型。这是通过使相关模型更具体地针对问题领域,利用源代码独特的功能(如程序和数据流),并依靠不太通用的 transformers 实现来实现的,这些实现不将源代码领域视为另一个 NLP,而是以更专业的方式,考虑到特定领域的特征(有趣的是,这类似于将图像和 NLP 深度学习最佳实践应用于音频领域的方式,不是原样,而是通过考虑音频独特的概念,调整这些架构以更好地适应音频领域)。与此同时,对于源代码领域的应用程序来说,速度可能是至关重要的(比如为了成为像 CI-CD 这样的系统的一部分,有资源和速度限制),我们很可能会看到越来越多的轻量级实现,无论是轻量级转换器还是更通用的 NLP 架构。最后,由于源代码领域需要自己的标签(一般的 NLP 标签不太相关),并且理解到仅仅依靠 Github 采样是不够的,我们可能会面临越来越多的努力来生成带标签的源代码数据集。激动人心的日子就在前面。

SourceCodeAI 固有的训练-推理不匹配

原文:https://towardsdatascience.com/sourcecodeai-how-to-handle-train-inference-mismatch-2914be981a04

来自 PexelsAlex Dumitru 的照片

S 源代码 AI 有许多独特的特性,使其区别于更一般的 NLP 应用程序(比如在将输入馈送给模型之前大量处理输入的常见做法)。它的一个主要挑战是,虽然生成源代码训练数据集似乎很容易(“只需抓取 Github”),但现实中包括许多要避免的隐藏陷阱,其中一个事实是,这些高度可用的源(如 GithubBitbucketStackoverflow )通常不同于推断数据(生产阶段),这可能会影响预计的模型性能。更多关于这种现象以及如何避免的信息在前面。

所见并非所得

机器学习应用的第一个要素是工作数据集。对于源代码应用程序,这样的数据集可以使用公共源代码托管服务(如 Github)轻松构建。让我们假设,例如,我们想开发一个应用程序,它将找到无意中留在源代码片段上的 PII 。为了生成一个训练数据集,我们抓取 Github,主动搜索相关的术语(如“信用卡号= .”,以确保我们会有足够多的正面例子)。最后,我们使用该数据集训练了一个模型,结果发现我们的内部存储库的性能显著下降。哪里出了问题?更深入的观察会发现,我们用于训练-测试-验证的存储库与我们内部的存储库有很大的不同。例如,Github 存储库可以由一个组织或一个用户拥有,并且可以有公共或私有范围(只对帐户成员可见)。从这些人口特征来看,私人回购和公共回购之间存在着内在的紧张关系;虽然公共代码是容易获得的资源,但我们训练的人工智能应用程序的真正目标通常是内部代码,可以合理地假设这些代码看起来会有所不同;以您的组织内部代码片段为例,将其与 Github 随机代码片段进行比较。例如,考虑像文档级别和最佳实践比率这样的特性。遇到一个整体环境变量或者一个好的 API [封装](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)#:~:text=In object-oriented programming (OOP,some of an object's components.)结构的可能性有多大?。在随机的 Github 存储库中,在数据库相关的片段中面临 AWS 集成的可能性有多大?。考虑到 Github 的许多可能的子群体,事情变得更加复杂——尽管我们假设一个公共的、公司所有的库是 SDK、开源的或者一个通用的例子(否则,为什么要公开它?)和一个公共的、私有的、POC 或一般项目的存储库(否则,为什么不把它放在公司的存储库上),事实是 Github 拥有这一切,因为快速的 Github 搜索可以显示私有的存储库,这些存储库看起来是公司所有的,反之亦然。这种现象的主要问题是分布差异;在公共 Github 仓库中找到 PII(以我们的例子为例)的可能性有多大,在公司内部仓库中又有多大呢?为了更好地理解这种差异如何影响生成的模型的性能,让我们来看一个系统化的分析,该分析是为另一个源代码 AI 用例代码自动完成评估这种差异的重要性而做的。

训练推理群体不匹配如何影响自动完成应用

代码自动完成是源代码人工智能世界中的一个时髦话题。虽然传统上它几乎是每个 IDE 的重要组成部分,但最*像 Github CopilotTabnine 这样的解决方案开始提出一种基于人工智能的竞争解决方案。 Hellendoorn 等人 (2019)强调了这种开发的一个固有问题——自动完成模型通常在合成的数据集上训练,被要求填充随机移除的标记。这种方法的两个主要问题是:第一,提交的代码通常与编写代码时的样子不同(比如有部分的,甚至可能没有工作的上下文与准备提交的上下文),第二,在现实中,不是所有的令牌都有相同的可能性被应用程序用户要求自动完成。从模型的角度来看,一些重复的、短的、容易预测的记号比那些更长的、更罕见的记号更有利于学习。但实际上,我们可以假设较长且罕见的标记与自动完成更相关。不足为奇的是,该研究发现,与相关论文中提到的情况相比,现实中的性能有显著下降。有趣的是,许多推理时间完成都在项目 API 中,根据定义,对于在一般群体上训练的模型来说是不可见的(这可以使用每个给定群体的“调整”步骤来解决,类似于迁移学习的 ULMFit 实践)。这些发现让我们怀疑离线数据在现实世界中预测这些模型准确性的可靠性,这将我们带到了一个我们无法真正信任离线指标的场景。

更多相似的训练群体如何提高自动完成应用程序的推断能力

既然我们知道了训练推理不匹配可能会影响自动完成应用程序的性能,那么对于这样的用例,是否有可能获得好的推理结果呢?。根据海伦多恩的发现,脸书( Gareth at el ,2020)研究了这个问题,发现在真实的自动完成示例上训练这样的模型显著改善了它们的结果。有趣的是,他们不仅发现了性能下降(对于合成与真实示例模型),还发现了使用下降;一项内部 A-B 测试实验显示,根据真实数据训练的模型最终获得了更高的使用率。此外,值得注意的是,这种下降甚至出现在 GPT-2 等最先进的模型上(有趣的是,BPE 标记化的性能下降最小,可能是因为具有最低水*的词汇外标记,他们提到这显然符合 Karampatsis el al 的发现,即的“当开发人员活动数据集不可用于模型训练时,子标记编码可能特别有用”)。不足为奇的是,分析显示,被接受的自动完成标记*均比普通的提交标记更长(更难记住)。总结这些论文结果,我们应该找到一种方法来确保我们的训练数据集与推理数据集高度相似(或者至少足够接*推理数据通过模型视角看起来的样子)。

使训练数据集更类似于推理

虽然我们希望我们的训练数据集尽可能与推理阶段相似,但对许多人来说,这并不是一个可行的要求。以我们开始的用例为例;我们应该从哪里获得包含 PII 的真实公司库呢?(假设这些片段非常关键)。虽然大公司理论上可以利用他们的内部代码库来满足这种需求(有过度适应他们的内部代码实践和样式的风险),但中小型公司没有这样的奢侈。一个更现实的评估将揭示出大多数公司必须依赖像 Github 这样的公共资源来生成他们的数据集。但是由于许多 Github 子群体可能是不相关的(例如,私有 POC 可能看起来与大公司的内部代码不同),我们应该找到一种方法来定位其中更相关的子群体。它需要对问题域进行更好的分析——我们试图解决什么?一个模型会关注哪些数据关键因素?我们如何预测这些因素来看待内部存储库?。一旦回答了这些问题,我们就可以开始验证我们对内部存储库和一般 Github 存储库的假设,以更好地确定我们需要的群体类型。然后,我们可以以更直接的方式接* Github,积极地瞄准我们需要的子人群。

巧妙地瞄准 Github

现在我们对推断阶段的数据特征有了更好的了解,我们可以开始在 Github 中搜索相关的片段。重要的是要记住,一个现实的数据集将包括具有不同程度的标签相关性的不同子群体;一些可能具有高的假阳性率,而另一些可能完全是真阳性。对于我们的例子(寻找 PII ),将世界分成三个主要部分是有意义的

  • 高标准;包含 PII 的可能性几乎为零的回购协议。相关的例子可以是文档或大公司(如 S & P top 50)的存储库。作为例子,查看脸书(公共)存储库,我们可以假设找到 PII 的可能性极低。
  • Med 标准;可能包含 PII 的回购协议。相关的例子可以是小规模的开放源码或小规模的组织库(在那里更容易出错)。两者都可以通过查看 Github accounts 元字段来定位,比如提交者数量或一般存储库数量。
  • 标准低;很可能包含 PII 的回购协议。相关的例子可以是私人用户的(公共)存储库,其中包括相关的术语(如“信用卡=”。可以假设找到 PII 的可能性更高)。

好消息是,所有提到的例子都可以使用 Github 搜索 API作为目标,使我们能够积极地(综合地)生成一个与我们的目标(推断,私人)群体更相似的群体。在推理阶段获得更好的性能。

了解你的数据特征

既然我们设法生成了一个像人口这样的推论,那么验证它是否真正符合我们的需要就很重要了。主要的挑战将是如何决定我们合成的数据集中每个亚群的比率。通常,可能需要几次迭代来生成一个数据集,其特征与计划推理阶段的特征足够相似。同时,解决方案是持续监控每个子群体的性能(每个生成的数据集子群体和我们的内部存储库中)。确保预测的行为确实成立(就像在我们的例子中,高标准亚人群不应该包括几乎任何 PII,否则目标机制就有问题)。这就是为什么从你的用户那里收集反馈也很重要,以验证重要的子群体没有被遗漏。不断用新的假阳性例子重新训练。致力于生成一个完全优化的数据集,能够击败任何基于数据的模型。

酸面团绿色咖啡发酵

原文:https://towardsdatascience.com/sourdough-green-coffee-fermentation-6217b1b8e9f4

咖啡数据科学

烤得越来越奇怪了

几个月前,世界咖啡师锦标赛(WBC)见证了许多使用各种加工方法的不同咖啡。他们中很少有人使用某种发酵,我对一种使用曲酵母(用于制作酱油和味噌汤)的方法特别感兴趣。所有这些方法都是在水果还在的时候应用到豆子上的,我很好奇我是否能把这样的方法应用到青豆上。

所以我抓起我的酸面团发酵剂,匆忙地把它扔进一些青豆里,结果让我相当惊讶。酸涩苦涩,一去不复返!

所有图片由作者提供

酵母加工

通常,酵母可用于加工咖啡果,然后将果实从咖啡豆中剥离。在阅读了关于人们使用酵母发酵方法的文章后,我意识到我可能不会用这种方法得到四季豆。获得咖啡果几乎是不可能的,所以我不能做他们在我的厨房里做的事情。我可以找到一个卖它的地方,但是比较加工过的酵母和一些受控的样品可能是有挑战性的,通常,它是以烘烤的价格出售的。

我决定用我从烤面包中学到的知识自己动手做。我没有耐心,所以我用了酸面团发酵剂,而不是点酵母。

用酵母加工四季豆并不新鲜,但并不广为人知。一位评论家已经批评他们购买低等级的咖啡豆,并试图在从农民那里购买之后提高等级。他们推销他们的工艺是为了降低咖啡的酸度。

我的目标不是买低等级的咖啡,而是提高我所用咖啡的等级。

咖啡豆

说到咖啡豆,让我们来看看我用的咖啡豆。我从 Sweet Maria 的买的,他们有自己的分级系统,类似于 Q-grading 。一个顶级咖啡会打 92 分左右作为参考。90 以上的都很好。

我的目标是获得各种混合质量,这样我就可以看到是否有特定的咖啡更有优势。以下是这些实验中使用的四种烘焙方法及其分数和来源。除了 2/9/2022 使用 8% robusta 之外,所有混合物都是半豆 1 和半豆 2。

酸面团加工

我用酸面团发酵剂,混合一些水,让它静置一天,冲洗,在烤箱中干燥,然后烘烤。

酸面团发酵剂,混合在水中,混合在豆类中,静置一小时,静置一天,冲洗并干燥。

这是关键配方:

  1. 在咖啡豆中加入酵母和温水;混合。
  2. 盖上盖子,静置 24 小时
  3. 彻底冲洗
  4. 干燥的
  5. 烤肉

从我开始的时候变量就已经变了。所以我把它们放在一张桌子上。我最*买了一个多功能速溶壶,它已经改变了游戏规则。它允许完全控制整个过程,但这不是必要的。

豆子似乎在这种混合物中发芽了一点,这很有趣。

发芽豆关闭,发芽豆,清洗豆,清洗完成后。

我通常会把咖啡豆晒干,直到它们和原来的重量一样,但是我知道其中的一些物质被酵母消耗掉了,因为在这个过程中,一些咖啡豆的重量比原来的损失了很多。

放在烤箱托盘上晾干,用干酵母处理过的豆子

在烤的时候,我确实发现多花了几分钟才打到第一条裂纹,烤完还得修改。通常,我会在第一次破裂后一分钟结束烘焙,但在这种情况下,咖啡豆直到第一次破裂后两分钟才充分成熟。

左:烘烤后加工的酸面团,右:烘烤后的对照样品。

除了烘焙时间,酵母加工的咖啡在烘焙过程中也失去了更多的重量。然而,所有咖啡的密度都差不多。

设备/技术

浓缩咖啡机:金快车&像样的浓缩咖啡

咖啡研磨机:小生零

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

镜头准备:断奏夯实断奏内外断奏

预灌注:长,约 25 秒

输液:压力脉动

过滤篮 : 20g VST

其他设备: Atago TDS 计Acaia Pyxis 秤Kruve 筛

绩效指标

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

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

用折射仪测量总溶解固体量(TDS),这个数字结合咖啡的输出重量和输入重量用于确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

强度半径(IR) 定义为 TDS vs EY 控制图上原点的半径,所以 IR = sqrt( TDS + EY)。这一指标有助于标准化产量或酿造比的击球性能。

成对射击表演

酵母处理的照片从一开始就非常引人注目,它们变成了我迄今为止最好的照片。他们的主要优点是去除了酸味和苦味,这使得甜味真正透了出来。

下面是散点图上的 34 个镜头对,展示了它们之间的对比。TDS/EY/IR 非常相似,所以我没有找到一个很好的理由通过烘焙来区分它。并非所有的烘烤都受到同样的影响。2022 年 2 月 9 日的烘焙(8%罗布斯塔)结果喜忧参半。奇怪的是,2022 年 2 月 4 日的咖啡没有其他的好,这很有趣,因为我用了得分最高的咖啡来烘焙。

****

从时间上看,酵母在一开始就跑得很快。他们更快地覆盖了过滤器(覆盖过滤器或 TCF 的时间),但他们的总时间多于对照组。

我在我的味觉尺度中分解了甜、酸和苦。其他指标没有受到酵母加工的直接影响。

我们可以通过*均味觉得分来了解酵母加工是如何影响味觉的。对我来说,我没有注意到对糖浆或口感成分的影响。

最后,我使用双尾 t 检验查看了 N=34 个镜头对的一般统计数据。定量提取方法没有发现统计学上的显著差异,但最终得分(味道)指标有。

如果你在家烘焙,尝试一些酵母。如果你被酸度困扰,尝试一些酵母。我一直在用和不用酵母处理青豆的情况下烘烤,我对探索其他酵母方法很感兴趣。我对农民使用的酵母加工特别兴奋,我很高兴我有办法在家里探索这一点。希望有一天我能在农场工作。

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

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

在云中构建 Spark 数据管道—您需要什么来开始

原文:https://towardsdatascience.com/spark-data-pipelines-in-the-cloud-118f38ea90b7

常见的工程挑战和解决方案

由作者稳定扩散生成

介绍

在过去十年左右的时间里,创作和执行 Spark 作业已经变得相当简单,这主要归功于:

  • 高级 API——更容易表达逻辑。
  • 托管的基于云的*台— 高度可扩展的对象存储和基于 spot 实例的一键式临时集群使运行作业变得无比简单(并推迟了对作业进行优化的需求)

虽然在 Spark 中创作逻辑和执行作业已经变得非常容易,但是基于 Spark 的工程管道仍然是一种艺术形式,几乎没有被广泛接受的标准和最佳实践。

许多第一次构建管道的团队发现,即使对于最基本的工程问题,如打包、部署、可测试性、环境隔离和可观察性,也存在陡峭的学习曲线和缺乏记录的最佳实践。

具有讽刺意味的是,这与基于 SQL 的管道形成了鲜明的对比,在社区采用 DBT 作为事实上的标准来解决许多相同的问题后,传统上这是工程实践的黑洞。

编写 Spark 代码并在集群上运行,只是“三角洲湖的冰山一角”。作者图片

目标

这篇文章的目标是涵盖常见的软件工程问题以及在云中构建和运行基于 Spark 的批处理管道的方法。

这不是一个完整的指南,其目的是涵盖几乎适用于所有管道的一些最常见和最基本的领域。

不用说,这里描述的实践和建议不能被认为是“标准”——但是希望它们可以提供一个合理的起点。

这些例子基于 Databricks 上的 PySpark,但是这些原理也应该适用于运行在其他基于云的*台上的 Java 或 Scala。

超出范围

  • 大规模环境/复杂管道
  • Spark 代码或集群级优化
  • 分析测试或数据质量方法

议程

前期制作(本岗位)

批量数据流水线 101

  • 提取、转换、加载
  • 在增量运行中处理时间
  • 输入和输出划分
  • 合并结果

开发、测试和包装管道

  • 基本代码结构
  • 试验
  • 项目结构
  • 包装
  • 本地工作流程
  • 海峡群岛

环境和部署

  • 环境
  • 结构管理
  • 部署机制

生产和运营(下一个职位)

生产中的运行作业

  • 数据和管道的可观察性
  • 避免数据中毒
  • 并行或蓝/绿部署

常见运行场景

  • 重试和重新运行
  • 回填数据

我们走吧!

批处理数据管道 101

提取、转换、加载

批处理数据管道通常执行一个或多个 ETL 步骤。

每一步都遵循以下模式:

  • 提取—从某个位置(例如 S3)加载数据
  • 转换—执行聚合、过滤、应用 UDF 等。
  • Load —将输出写入某个位置(例如,S3 上的另一个路径)

有时,管道被建模为这种步骤的 DAG。

增量运行和时间的概念

大多数批处理管道设计用于增量处理数据,即:

  • 阅读“新数据”
  • 处理它
  • 将输出与之前运行的结果合并

定义“新数据”

大多数(但不是全部)批处理作业按计划运行,例如每天运行。因此,大多数团队从定义“新”数据开始:
“每天,对前一天的数据运行作业”。

换句话说,管道的每次运行负责处理一个(固定的)时间窗口的数据。

这种方法被称为“滚动窗口”:固定大小的连续时间帧,它们之间没有重叠。

墙壁时间与活动时间

一个简单的解决方案不起作用,用于处理具有流水线的时间窗:

  • 触发管道每隔 24 小时运行一次
  • 让管道代码使用“ 墙时间”—例如,现在的值()减去 24 小时作为它需要处理的时间窗口

这种方法不起作用的原因是,例如,当管道在周末发生故障时,现在您需要根据 72 小时前的数据运行它。

此外,大多数分析关注的是实践中事件发生的时间,而不是我们收到事件发生的消息或抽出时间进行推理的时间(尽管在某些情况下,这些也很重要)。

因此,更好的方法是根据“ 事件时间”“ ”来定义每次运行的时间窗口,即导致数据发生变化的真实事件发生的时间。

这意味着作业的每次运行都被分配一个固定的时间窗口,该时间窗口不直接取决于作业实际运行的时间。

(注意——在机器学习等一些用例中,这种方法可能需要改进,因为它可能会产生训练测试偏差)。

使用调度程序创建每个时间窗口的流水线运行

在时间窗口上运行流水线的常见方法是使用通用调度器(例如,Airflow、Prefect 等)。)

  • 调度器被配置成例如每天在某个时间触发流水线
  • 每个触发器在逻辑上与需要处理的不同事件时间窗口相关联(例如,前一个日历日期)。
  • 每当调度程序触发一个管道运行时,它都应该给它传递触发时间
  • 代码使用触发时间来计算它需要处理的时间窗口。
  • 如果我们的调度程序比预期的要晚醒来,比如在 0401,它仍然应该将“正确的”触发时间传递给管道
  • 如果触发器运行失败,管道应该以精确的原始触发器时间作为参数重新运行
  • 如果调度程序本身关闭了,它应该注意创建关闭时丢失的触发器

因此,为了正确管理时间窗口,调度程序和管道代码之间有相当多的微妙之处和隐含契约。

此外,每个调度程序对这些契约的解释略有不同。

这里有一些来自气流提督以及数据块的例子。

结论之一是,管道需要能够接受代表调度程序触发时间的参数,并根据该值计算出时间窗口。

后期数据和水印

不幸的是,数据总是姗姗来迟,令人恼火。

假设我们想要在整个日历日的“真实世界”数据上运行我们的批处理。
如果我们的调度程序正好在午夜触发,那么前一天的一些数据可能仍在写入,并且会被错过。

一个简单的方法是给我们的调度时间添加一个“缓冲区”——例如,设置触发器在 0400 触发,以便处理前一天的数据。
缓冲区大小可以猜测,甚至可以凭经验确定。

值得注意的几件事:

  • 固定缓冲区并不能保证避免延迟数据,它只是一种风险管理技术
  • 在我们采用的缓冲区大小(即数据完整性的置信度)和结果的新鲜度之间有一个明确的权衡
  • 如果您的管道处理另一个批处理的输出,您通常更容易受到延迟(例如,当上游批处理失败时)
  • 碰巧比缓冲区晚到达的数据很难被注意到,除非您对此采取主动措施

事实证明,管理最新数据并决定何时以最小风险安全处理数据是一个非常复杂的问题——尤其是在需要更新结果的系统中。

为了处理新鲜度和完整性之间的权衡,您需要推理 水印 的概念,即我们可以假设足够完整以便处理的*似最新时间,以及如何检测和响应较旧数据确实以比预期更大的延迟到达的情况。

总而言之——对于大多数不经常运行的批处理作业(每天/每周),在调度中设置缓冲区是处理数据延迟的一种简单的启发式方法。

对于其他情况,你需要对这些假设进行一些思考,或许还需要多读一些关于这个话题的内容,例如 这里

数据水印随着时间的推移而发展。作者图片

划分输入

当处理大量数据时,我们必须对其进行组织,以便于将它缩小到我们想要处理的部分。

Spark 支持数据分区的概念,在这种情况下,我们在文件系统上将数据组织成类似文件夹的结构,每个通过某个列的值表示数据的一个分区。

如果分区是根据某个约定命名的,并且包含列名,我们可以添加一个关于分区列值的条件,Spark 可以将这个条件下推,并用作装载数据的过滤器。

一个简单的例子是按日期对数据进行分区,然后添加一个条件,告诉 spark 我们只想提取某个日期范围内的数据。

  • 为了便于正确处理,尤其是在管道执行任何聚合时,最好根据事件时间和写入分区的时间来创建分区。
  • 在提取阶段应用这种基于时间的过滤是一个很好的实践,并使转换逻辑保持中立。
  • Spark 只自动识别基于日期的分区文件夹的固定格式。
window_start = calculate_window_start(trigger)
window_end = calculate_window_end(trigger)
df = spark.read.json("s3://my-bucket").filter(f"created_dt >={window_start} and created_dt <= {window_end}")

划分输出

类似于输入分区,我们通常也需要对输出进行分区。

当处理是简单变换时

在管道执行转换(相对于聚合)的最简单的情况下,可以根据与输入完全相同的列来划分输出。

这有助于我们理解输入中的每一行,我们期望在输出中的什么地方找到它。

当处理是聚合时

比方说,我们的工作需要总结我们网站中每页的点击量。

页面 A 有三次点击,分别发生在我们的时间窗口内的 t1、t2 和 t3。

该聚合的结果将是一行,表示 A 有 3 次点击。
这可以被视为一个“聚集的”或“复杂的”事件。
这件事是什么时候“发生”的?

一种方法是将 t3 定义为“复杂事件”的时间,因为这是一个完美系统发出值“3”的时间。

虽然在分析上是准确的,但生成每个键的事件时间通常会导致在回填和重新运行期间管理数据的问题,需要覆盖结果(这将在后面讨论)。

一种分析上不太准确但解决了许多操作问题的替代方法是对整个输出使用单一日期— 窗口结束

虽然这不是“最严格”的时间定义,但也不是不正确的,因为它指定了系统认为事件“真实”的有效时间。

输出的分区方案会影响您处理回填的方式,以及您向现有数据集添加新数据增量的方式。在本指南的第二部分会有更多的介绍。

将增量运行的结果与更大的数据集合并

当增量运行完成时,其结果需要与所有先前计算的数据合并。

流水线运行的一个重要属性,特别是输出的合并,是我们希望它是幂等的— ,也就是说,允许我们重复几次作业运行和合并,而不改变结果。

当将结果写入对象存储时,我们有一些常见的策略:

  • Append —向现有输出添加新输出,包括向现有分区添加新文件。不常见,因为相同日期范围的重新运行可能会导致数据重复
  • 覆盖 —这是一种简单的策略,适用于所有运行都处理一个固定的时间窗口,并且相应地对输出进行划分的情况。
    覆盖只对整个分区有效,对单个记录无效。
    这通常需要配置动态分区覆盖(参见spark . SQL . sources . partitionoverwritemode)
  • 合并 —一些现代文件格式如 delta lake 或 Iceberg 支持合并增量结果的概念。
    合并过程包括一个根据某种条件在新数据和旧数据之间进行的类似 join 的操作。
    可以插入或删除任何一方的不匹配记录(新值或旧值)。可以使用业务逻辑逐列合并匹配的记录。

选择正确的策略是一件微妙的事情。

Merge 提供了完全的正确性和灵活性,但是在计算方面要昂贵得多。如果 99%的运行没有更新数据,而是添加或替换数据,这是一个很高的代价。

覆盖成本低,但对边缘情况更敏感,以确保覆盖不会导致数据丢失。

读/写分区数据的 ACID 属性

想象以下场景:生产者忙于将其结果合并到数据集,与此同时,消费者开始读取整个数据集。

天真地说,读者可能会得到不一致的数据视图(有些写了,有些没写)。

这是一个非常基本的问题,但是直到最*,当在云中运行时,还需要相当多的精心设计,因为对象存储解决方案不提供原子重命名操作。

如果你对这个问题的历史感兴趣,这里的是一些背景。

在过去 2 年左右的时间里,这个问题或多或少通过使用支持 ACID 的数据格式得到了解决,比如 delta-lake 或 Iceberg。

不幸的是,并不是所有的上游/下游系统都能读写这些数据格式。

例如,如果您从 Kinesis firehose 在 S3 登陆数据,或者从一些旧的数据仓库技术读取管道输出。

在这些情况下,您仍然需要以其他方式解决问题。前面提到的博客文章相当透彻地解释了替代方案。

简单增量管道示例

  • 按日期对输入数据进行分区(如果计划在一天之内运行,则按小时)。
  • 使用配置控制时间窗口大小(例如 24 小时)
  • 让调度程序用一些“缓冲”来触发管道,以允许数据完全到达。
  • 让调度程序将“触发时间”作为运行参数传递
  • 使用 spark 可以下推的过滤器读取与时间窗口相关的数据
  • 对输出进行分区:
    —如果您的管道只进行转换—保持输出分区与输入分区相同
    —如果您的管道进行聚合—按照每次运行的结束日期(或时间)对输出进行分区
  • 添加增量结果:
    —如果您的数据很少更新记录(但主要是添加或替换记录),则使用“覆盖”模式,最好使用 ACID-preserving 数据格式。
    —如果对单个记录的更新很常见,并且您有应用它们的应用逻辑,则使用“合并”模式

按计划运行的简单聚合作业,从 S3 读取和写入分区数据。作者图片

设计、测试和包装管道

设计管道代码

提取并加载

输入数据通常是环境的一部分。
数据接收器也是如此,它是我们存储输出的地方。

因此,最好是:

  • 提取加载阶段与转换阶段隔离
  • 使这些功能高度可配置,以便相同的代码库可以在本地笔记本电脑和云上运行。

变换

转换是大多数业务逻辑所在的地方。

为了使转换模块化和可测试,通常将其分解为多个函数——例如,有一个“主”转换函数调用较小的函数,每个函数接受一个或多个 Spark 数据帧,并返回一个或多个 Spark 数据帧。

在编写转换函数时,使用函数式风格是一个好主意。

配置

即使是最简单的管道也需要某种支持来为其注入配置。

通常,您希望能够配置提取和加载的行为,以便可以在数据以不同路径和格式驻留的不同环境中运行代码。

此外,通常还有一些与逻辑本身相关的应用配置,这些配置可能依赖于环境,也可能不依赖于环境。

稍后将详细介绍配置。

星火会

任何管道都需要一个初始化的 Spark 会话来运行。

会话的初始化方式通常特定于管道运行的环境。

例如,在受监控的集群上运行时发生的一些初始化可能不适用于或不适合单元测试场景。

因此,在管道本身之外初始化会话,并将其作为参数传递给管道通常是一个好主意。

class Task:

    def __init__(self, spark: SparkSession, config: JobConfig):
        self.config = config
        self.spark = spark

    def main(self):
        extracted = self.extract()
        transformed = self.transform(**extracted)
        return self.load(transformed)

    def extract(self):
        pass

    def transform(self, dataframes):
        pass

    def load(self, transformed, uri=None):
        pass

试验

如果你按照上面的方法设计了代码,测试它应该是相当容易的。

大多数测试将覆盖转换函数,其中许多接受一个或多个数据帧并返回一个或多个数据帧。

要设置测试,我们需要:

  • 初始化测试的 spark 会话(例如作为 pytest 中的一个附件)
  • 创建内存中的数据框架和期望
  • 调用转换函数
  • 断言函数的输出与预期结果相同或相似

本帖举例。

单元测试

最低级别的单元测试通常在每种情况下使用非常少量的输入记录,这些记录通常以编程方式定义为测试代码的一部分。

举个例子(来自上面的博文):

@pytest.fixture(scope="session")
def stock_data(spark):
    schema = StructType([
        StructField("stock", StringType()),
        StructField("date", StringType()),
        StructField("price", DoubleType())
    ])
    data = [
        {"stock": "AA", "date": "20180902", "price": 200.0},
        {"stock": "AA", "date": "20181002", "price": 300.50},
        {"stock": "AA", "date": "20181102", "price": 400.50},
        {"stock": "BB", "date": "20180902", "price": None},
        {"stock": "BB", "date": "20181002", "price": 450.50},
        {"stock": "BB", "date": "20181102", "price": 200.50},
    ]
    df = spark.createDataFrame([Row(**x) for x in data], schema)
    df.cache()
    df.count()
    return df

组件或集成测试

这里触发代码的机制是相同的,但是被测试的单元更大。

因此,它通常需要更大的输入数据,例如符合数据的“真实”模式的数据。

通常将大型测试的输入数据放在文件中,与测试一起组织在一个数据文件夹中。

例如,要在一个测试输入上运行整个管道,可以如下编写测试(这里的参数通常是 fixtures )

def test_end_to_end(session, input_path, tmp_path, expected_path):
    config = {"input_path": input_path, "output_path": tmp_path}
    job = Task(session, config)
    job.main()
    output = load(tmp_path)
    expected = load(expected_path)
    assert_frame_equals(output, expected)

入口点

到目前为止,我们已经在单元测试中运行了部分逻辑。

要将 PySpark 管道作为独立作业运行(无论是本地还是远程),我们通常需要公开一个入口点。

入口点的作用是:

  • 解析命令行参数
  • 如果需要,读取任何配置文件
  • 创建会话
  • 如果需要,实例化管道
  • 调用管道的主函数
class Task:
    pass
def entrypoint(argv):
    args = parse_arguments(argv)
    config = load_configuration(args)
    session = initialize_session(config)
    task = Task(session=session, config=conf_model)
    task.main()
    print("done")

if __name__ == "__main__":
    entrypoint(sys.argv)

注意:入口点是相对通用的,可以在多个管道之间共享(例如,通过将管道的名称作为参数传递)

端到端测试和本地运行管道

在远程集群上启动管道之前,能够使用内存中的运行器在本地端到端地运行管道通常非常有用。

这里,我们使用 entrypoint 来完成会话和配置对象的所有初始化,例如控制作业的输入/输出路径。

编写端到端运行管道的测试是可能的,因为入口点允许您注入命令行参数。

通过在 IDE 中创建一个启动器,用正确的参数调用入口点的模块,可以进行调试。

最后,您可以在打包管道之前或之后,从 shell 中调用管道。

项目结构和包装

到目前为止,我们只有一个非常标准的 Python 项目。

我们需要所有的标准脚手架——

  • 需求管理
  • 项目打包工具
  • github 操作
  • 提交前挂钩
  • 等等。

你可以采用你喜欢的现代 Python 包模板比如这个。

包装

我们目前拥有的是一个纯 Python 项目,因此使用基于 setuptools 的标准打包机制将其打包为. whl 文件是有意义的。

如果您遵循一个现代的 Python 项目模板,您将已经选择了您的打包机制(setup.py、pyproject.toml、poem-style 等。)

将入口点函数公开为包的 API 通常是一个好主意,例如像这样的。

这使您能够安装打包的管道,并直接触发它:

pip install my-pipeline
my_task arg1 ...

在大多数情况下,至少在 Python 中,打包没有太多神秘之处,所以您应该能够在笔记本电脑上轻松地打包本地代码,这对本地测试也很有用。

可选—在远程集群上运行本地代码

出于测试目的,能够将本地代码(打包后)部署到开发/测试环境中的远程集群通常很有用。

这可以在查找和修复琐碎的环境相关问题方面节省大量时间(而不是等待某个 CI/CD 管道稍后出现故障)。

如果您的部署机制设计良好,这通常是非常可行的。

我们将在这篇文章的后面讨论部署。

本地工作流摘要

本地开发工作流程—图片由作者提供

海峡群岛

在这里,您可以使用 github 操作来遵循经典的 Python CI 工作流,包括:

  • 检查代码
  • 为林挺和其它实施运行提交前挂钩
  • 运行单元测试
  • 运行更大的测试(例如端到端测试)
  • 包装管道
  • 把它推到某个(私有的)工件注册处

测试、打包和发布数据管道的 CI。作者图片

我们已经谈到了部署到远程集群的主题,现在是时候更深入地探讨这个主题了。

部署和环境管理

这就是事情开始变得棘手的地方。

  • “环境”在数据管道上下文中意味着什么?
  • 环境包括什么?
  • 环境是如何相互隔离的?
  • 不同的环境如何影响我的代码库和开发。流程?
  • 我每次需要部署什么工件?

让我们开始解开这些问题。

什么是环境,我需要哪些环境?

一般来说,“环境”是一组资源,在这些资源中,您正在构建的软件可以在各种场景下部署和运行。

软件开发过程通常包括以下部分或全部环境:

  • 本地 —在本地机器或 CI 工作器上运行系统(参见上面的本地工作流)。
  • 开发/测试 —这个环境可能是个人的,分配给每个团队,或者跨多个团队共享。通常用于功能和可选配置项的早期测试
  • QA——一旦正式版本或发布候选版本被构建并发布,一些团队会将其部署到一个单独的、干净的环境中进行 QA(主要是当 QA 部分是手工的时候)
  • 试运行 —在将软件发布到生产环境之前,我们可以将其“试运行”在类似生产的环境中,并观察其在类似生产条件下的行为
  • 负载测试 —当您需要一组专用资源来生成和测试负载下的系统时
  • 生产 —系统可以运行并影响最终用户的地方

火花管道的“环境”包括什么?

至少,环境包括:

  • 火花簇(或旋转它们的能力)
  • 输入数据(或可以生成数据的工具)
  • 用于写入管道输出数据的接收器(例如 S3 桶或数据库)

此外,环境可能包括:

  • 其他管道(如上游和下游管道)
  • 数据目录(例如 hive metastore)
  • 调度器/编排器工具
  • 监控工具
  • 更大的

合理的起点(AWS 上的数据块示例)

  • 本地 —内存火花(从本地 FS 或 S3 读取)
  • 开发/测试、阶段、产品 —独立的数据块 工作区 ,共享相同的数据块和 AWS 账户。
  • 存储 —每个工作区都有单独的桶。
    所有环境都可能对彼此的桶具有只读权限
  • 监控 —使用不同指标标签的相同暂存和生产帐户,以及在控制面板中按环境过滤的能力。

为了扩展到更多的团队,您可以决定为每个团队打开单独的开发/测试工作区。

环境级别的隔离是不够的

环境通常比使用它们的管道少得多。

例如,我们可能在开发/测试中处理两条不同的管道,或者在生产中并行运行同一管道的两个版本。

多个作业运行共享同一个环境。作者图片

由此可见,管道运行之间的大部分隔离需要来自我们将管道部署到环境中的方式。

部署

部署包括什么

管道到环境的部署通常包括:

  • 已部署管道的名称
  • 指向我们需要运行的二进制文件的指针(例如私有 PyPi repo 中的 Python 包)
  • 运行二进制的命令
  • 适用配置(例如命令行参数)
  • 集群配置 —包括机器、角色、spark 配置、初始化脚本、环境。变量等。
  • 调度程序配置
  • 相关时的监控和报警配置

按配置部署

大多数部署选项都是高度结构化的。

大多数 cloud Spark *台都有用于部署的结构化 API,这些 API 接受上述信息的一些变体作为 JSON 有效负载。

分层配置管理

为了避免在每次部署管道时重复配置,我们可能希望分层管理配置。

配置的层次。作者图片

较低级别的配置是帐户或环境的一部分,在首次设置时就已经存在(很少更改)。

特定于管道的其余配置选项需要与管道的代码库一起管理,因为它们可能会在开发逻辑的过程中发生变化。

应用配置

事实上,每个管道都需要公开某种方式来从外部配置其行为。

例如:

  • 控制 S3 上的输入和输出路径
  • 在测试环境中设置查询限制
  • 设置执行聚合的时间窗口的大小
  • 等等。

如果您想要控制的参数数量很大,将它们组织到配置文件中是有意义的。

根据分层配置概念,我们可能希望有一种配置机制,它允许:

  • 文件中管理的配置的专业化(例如,每个环境)
  • 通过命令行参数覆盖值

实现所有这些以及更多的一个很好的工具是 hydra.cc

my_pipeline -c /path/to/hydra/configuration/root +env=test ++job_config.some_key="x"

九头蛇的指南超出了这篇博客的范围。

部署机制

部署过程需要执行以下操作:

  • 构建包(可选)
  • 将包部署到集群的可访问位置,例如 PyPi repo(可选)
  • 将适用的配置文件部署到集群的可访问位置(例如 Spark 驱动程序可用的共享文件系统)
  • “编译”该管道特定部署的运行时配置——确定集群、管道名称、调用管道的正确命令、调度程序配置、监控配置等。
  • 调用 API 创建集群,并使用上述所有信息在环境中正确注册管道

在数据块上做上述所有事情的一个很好的工具是 dbx

  • 从命令行调用部署。
dbx deploy --environment test --workflow my_pipeline --config ....
  • 它附带了一个. yaml 文件,该文件控制构建命令以及运行时配置的大多数其他方面。
  • 通常,它按照环境和管道来组织部署。
  • 每个部署的许多应用性配置都可以通过 deploy 命令来覆盖。
  • 您可以添加一些更动态的行为,例如使用 Jinja 模板和环境变量覆盖所创建的数据块作业的名称。

基于 dbx 和 hydra 配置的项目结构

.
├── .github
│   └── workflows
│       ├──ondeploy.yaml
|       └──onpush.yaml
├── .dbx
│   └──project.json
├── conf
│   ├── deployment.yaml      #dbx
│   └── tasks
│       └── my_task          #hydra
│           ├── my_task.yaml  
|           ├── test
│           ├── stage
|           └── prod
├── my_pipeline
│   ├── entrypoint.py
│   └── tasks
│       └── my_task.py
├── tests
│   ├── data
│   ├── unit
│   │   └── tasks
│   │       └── my_task_test.py
│   └── system
│       └── system_test.py
├── .pre-commit-config.yaml
├── pyproject.toml
├── setup.cfg
├── setup.py
└── README.md

摘要

在这篇文章中,我们讨论了云中基于 Spark 的数据管道的基本生产前问题。

上述信息本身并不新颖,但我希望它能为读者在识别和/或设计这些常见问题的解决方案时节省一些精力。

下一期文章将关注生产中管道的运行和操作。

如何修复无法分配请求的地址:服务“sparkDriver”在 16 次重试后失败

原文:https://towardsdatascience.com/spark-fix-cant-assign-driver-32406580375

了解如何解决最常见的 PySpark 问题之一

艾蒂安·吉拉尔代在 Unsplash 上拍摄的照片

介绍

当涉及到配置时,Spark networking 有时会变得相当具有挑战性,有时可能会出现一些奇怪的错误。我个人看到的最常见的错误之一是Service 'sparkDriver' failed after 16 retries错误。错误的完整追溯分享如下。

Exception in thread "main" java.net.BindException: Can't assign requested address: Service 'sparkDriver' failed after 16 retries (on a random free port)! Consider explicitly setting the appropriate binding address for the service 'sparkDriver' (for example spark.driver.bindAddress for SparkDriver) to the correct binding address.
    at sun.nio.ch.Net.bind0(Native Method)
    at sun.nio.ch.Net.bind(Net.java:433)
    at sun.nio.ch.Net.bind(Net.java:425)
    at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
    at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:127)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:501)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1218)
    at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:496)
    at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:481)
    at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:965)
    at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:210)
    at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:353)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:399)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:131)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
    at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 1

在今天的简短教程中,我们将探索一些潜在的解决方法,最终可以帮助您处理这个错误。

设置 SPARK_LOCAL_IP 环境变量

Spark 使用驱动程序中指定的配置参数或作为位于安装目录中的conf/spark-env.sh(或conf/spark-env.cmd)脚本的一部分加载的环境变量,确定 JVM 如何在工作节点上初始化。

在运行本地 Spark 应用程序或提交脚本时,也会用到这个脚本文件。注意安装 Spark 时conf/spark-env.sh默认不存在。然而,GitHub 上有一个名为[conf/spark-env.sh.template](https://github.com/apache/spark/blob/master/conf/spark-env.sh.template)的模板文件,你可以用它作为起点。您还必须确保该文件是可执行的。

SPARK_LOCAL_IP环境变量对应于要绑定到的机器的 IP 地址。因此,您可以通过向conf/spark-env.sh脚本添加以下命令来指定 Spark 在该节点上绑定的 IP 地址

export SPARK_LOCAL_IP="127.0.0.1"

或者,由于conf/spark-env.sh是一个 shell 脚本,您可以通过查找特定网络接口的 IP 来编程计算SPARK_LOCAL_IP,而不是将其硬编码为一个特定的值。

配置 spark.driver.bindAddress

如前一节所述,Spark 还可以使用驱动程序中指定的配置来确定 JVM 如何在工作节点上初始化。

换句话说,通过为spark.driver.bindAddress提供一个配置,您可以在初始化SparkSession时以编程方式设置绑定监听套接字的主机名或 IP 地址。

但是请注意,这种方法将覆盖SPARK_LOCAL_IP环境变量的值,如果它已经在我们在上一节描述的conf/spark-env.sh脚本中导出的话。

*spark.driver.bindAddress*:绑定监听套接字的主机名或 IP 地址。这个配置覆盖了*SPARK_LOCAL_IP*环境变量。

它还允许将与本地地址不同的地址公布给执行器或外部系统。例如,当运行具有桥接网络的容器时,这是有用的。为了正常工作,驱动程序使用的不同端口(RPC、块管理器和 UI)需要从容器的主机转发。

—来源:火花文件

在 Scala 中,您可以配置 IP 地址来绑定监听套接字,如下所示。

import org.apache.spark.sql.SparkSessionval spark: SparkSession = SparkSession.builder() \
    .appName("Test application") \
    .master("local[*]") \
    .config("spark.driver.bindAddress", "127.0.0.1") \
    .getOrCreate()

PySpark 中的等效表达式是

from pyspark.sql import SparkSessionspark = SparkSession.builder \
    .appName("Test application") \    
    .master("local[1]") \
    .config("spark.driver.bindAddress", "127.0.0.1") \
    .getOrCreate()

在本地机器上设置主机名

第三个选项是通过命令行在本地机器上手动指定hostname。主机名是作为计算机或服务器名称的唯一标识符。

可以使用以下命令调整该名称:

sudo hostname -s 127.0.0.1

检查您的 VPN 连接

最后,如果您在本地机器上运行 Spark,另一种可能性是您的虚拟专用网络可能会影响绑定监听套接字的主机名或 IP 地址。

如果是这种情况,您可以通过简单地断开 VPN 或禁用任何其他可能影响网络的工具来解决这个问题。

最后的想法

在今天的简短教程中,我们讨论了 Spark 中一个常见的报告错误,即与 Spark 网络相关的java.net.BindException: Can't assign requested address: Service 'sparkDriver' failed after 16 retries

我们展示了可以采取哪些措施来解决这个问题。总而言之,这可以通过以下方式实现

  • 导出在工作节点上初始化 JVM 时加载的相应的SPARK_LOCAL_IP环境变量
  • SparkSession中设置相应的spark.driver.bindAddress配置(注意这种方法将覆盖SPARK_LOCAL_IP环境变量)
  • 更新本地机器上的hostname
  • 检查您是否启用了虚拟专用网络(VPN)或任何其他可能影响本地计算机联网的工具,因为这有时可能会影响绑定地址

一旦应用了本文中描述的任何解决方案,您最终应该能够顺利运行 Spark 应用程序。

成为会员 阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

Spark Graphx Pregel API:它没有听起来那么复杂

原文:https://towardsdatascience.com/spark-graphx-pregel-its-not-so-complex-as-it-sounds-d196da246c73

了解 Pregel 的基础知识,以及它如何解决现实世界中的问题,即使这些问题与图形没有严格的关系。

https://www . pexels . com/photo/person-weaking-米色-毛衣-手持-地图-车内-1252500/

普雷格尔什么?

Spark 附带了一个图形处理库,其中有几个有用的 API,用户可以开箱即用,例如查找连接的组件、计算三角形和排列图形中的节点。这些 API 只是将用户从重新发明轮子中解放出来。这不仅是关于图的问题或算法,也是关于可伸缩性的问题,这样问题就可以在大量数据的情况下得到解决。

我以前有机会使用这些 API,但是有一个 API 看起来有点复杂。它实际上是一个框架,需要开发人员插入一些逻辑来解决定制问题。该 API 被称为“ Pregel ”。

来自斯坦福的课堂素材:

pregel(Parallel、Graph 和 Google 三个词的组合)是一个数据流范式和系统,用于 Google 创建的大规模图形处理,以解决仅使用 MapReduce 框架难以解决或成本高昂的问题。虽然该系统仍然是 Google 的专利,但计算范式已被许多图形处理系统采用,许多流行的图形算法已被转换为 Pregel 框架。Pregel 本质上是一个限制在图的边上的消息传递接口。

当图很大时,像 Dijkstra 的最短路径这样的算法可能不是很有效。这并不是因为算法本身就很糟糕,而是因为图像无法容纳在一台机器的内存中。

Spark 对 Pregel 的实现是在的原始论文中提出的一个轻微的变体,以允许某些内部优化。为了继续这篇文章的其余部分,建议对 Spark Graphx 有一些基本的了解。

简而言之 Pregel API

Pregel 作为一个 API 有以下冗长的签名,长时间的 currying 和高阶函数。

**def** pregel[A]
      (initialMsg**:** A,
       maxIter**:** Int = Int.MaxValue,
       activeDir**:** EdgeDirection = EdgeDirection.Out)
      (vprog**:** (VertexId, VD, A) **=>** **VD**,
       sendMsg**:** EdgeTriplet[VD, ED] **=>** **Iterator**[(VertexId, A)],
       mergeMsg**:** (A, A) **=>** A)
    **:** Graph[VD, ED]

从上到下:

  1. 该函数在类型 A 上是通用的,类型 A 是从一个节点发送到另一个节点的消息的类型。Pregel 基本上是一个消息发送和聚合算法。该类型是从下一个参数中推断出来的。
  2. initialMsg 参数保存调用 API 时发送给图中所有节点的消息。
  3. maxIter 是一个数字,用于设定运行迭代次数的上限,默认为 Int 类型的最大值。
  4. activeDirection 控制消息是通过边缘的出站方向还是入站方向发送,默认为出站。这里讨论的所有例子都假定默认值为出站方向。
  5. vprog 是一个函数,它获取顶点 Id、顶点数据和发送的消息,并基于某种逻辑更新顶点数据。这个函数通常被称为顶点程序。
  6. sendMsg 是另一个函数,它接受边三元组并返回由顶点 Id 和消息组成的元组数组。这是用于生成消息的函数,这些消息将在迭代的下一步中使用。
  7. mergeMsg 是从发送到同一节点(顶点)的一组消息中挑选出一条消息的功能。
  8. 调用这个 Pregel API 的返回类型是另一个具有相同类型的顶点和边数据的图形,但是它的顶点数据可能是输入图形的变异版本。

在接下来的几节中,上述内容的含义和用法将会更加清楚。

让我们使用 Spark 文档单源最短路径示例来实现和解释 Pregel,然后我将跳转到一个可以使用 Pregel 解决的实际业务问题。

你好 Pregel

要解决的问题是单源最短路径;简称 SSSP。如果你使用谷歌地图或苹果地图,它们解决了几乎相同的问题,只是有些不同,但概念仍然是一样的。Graphx 的 Spark 文档提供了一个片段来解决这个问题,但是是针对随机生成的图形。

让我们从头开始,从下图开始。节点 1 是起始节点,我们希望找到图中起始节点 1 到其他节点的最短距离。目测问题,节点 2 和 3 的距离很明显,分别为 5 和 7。节点 4 到 1 的最短距离应该是(5+12)和(7+8)的最小值,即 15。

作者图片

在 Spark 中创建图形

Spark Graphx 中的图必须有一个顶点 RDD 和一个边 RDD。顶点 RDD 是一个元组数组,其中第一个元素是必须很长的节点 Id(这就是为什么我没有将节点命名为 A、B、C、D)。元组的第二部分是顶点数据,在这种情况下,它被初始化为距节点 1 的最短距离。节点 1 的值为 0,每隔一个节点的距离为无穷大,这将在该过程完成后改变。

附言:顶点 RDD 是这样创建的,以便于直接运行 Pregel。在这篇文章的第二部分,我们将看到一种不同的初始化技术,以防初始化图形所需的逻辑有点复杂。

边缘 RDD 是一个容易的;它有源节点 Id、目标节点 Id 和边数据,在我们的例子中是两个节点之间的距离。

为了验证图形是否构建成功,可以将顶点和边部分打印为数据框。

作者图片

运行预凝胶

现在是时候使用 Pregel 获得最短路径了。

函数签名在前面已经总结过了,所以现在是时候看看实际的实现了:

  1. 第 1 行上的正无穷大比特是要发送给所有节点的初始消息。
  2. 第 2 行具有顶点程序,该程序简单地检查接收的消息(newDist)是否小于当前分配的最短距离(Dist)。如果计算结果为真,则用消息值更新顶点数据。请记住,除了节点 1 之外的所有节点都以+Inf 的顶点数据开始。
  3. 第 3 行到第 9 行具有沿边缘发送消息的逻辑。该算法在一个循环中迭代,其中每次迭代被称为超级步骤。在每个超级步骤中,生成一组消息并发送给一些节点。节点处理消息以改变顶点数据,并且循环继续。当由于所有节点都暂停而没有生成新消息时,循环结束。所有节点都以主动方式启动算法,因为所有节点都接收到了初始消息。如果生成的消息中没有一条边接触到一个节点,则该节点可以被暂停。这并不意味着节点将永远停止,因为如果有消息触及它的一条边,它可以在后面的步骤中被重新激活。在我们的例子中,消息发送函数只是检查当前节点距离(triplet.srcAttr)和边距离(triplet.attr)之和是否小于目标节点上设置的距离(triplet.dstAtrr)。如果评估为真,则在边缘上生成新消息。该消息是要向其发送消息的目标节点的元组加上新的距离值,该新的距离值应该小于目标节点上的当前距离。这个新的距离可以作为顶点程序的节点数据。如果条件评估为 false,则不会从当前节点生成新消息。
  4. 第 10 行只是控制一个节点在收到多个消息时处理哪个消息。在我们的例子中,如果一个节点有多条输入边,它将从这些边上发送的消息中选择最短的距离。无论节点收到一条还是多条消息,都将只选取一条消息,其值将用于变异目标节点顶点数据。

请参见 Spark Docs 上的伪代码来形象化上述描述。第二次调用mapReduceTriplets有一个最终参数(称为activeSetOpt)来控制哪些节点将生成消息。

上面可能看起来过于复杂或不可读(也许这是我的写作风格),但一旦我们描述了手头问题的迭代,这可能会改变。我将把它推迟几秒钟,现在显示输出。该算法的结果是几乎相同的图,其中每个顶点数据被更新以反映从节点 1 的最短路径距离。这是因为我们将图初始化为节点 1 的值为零,所有其他节点的值为正无穷大

作者图片

如您所见,距离计算正确,节点 4 在从节点 1 到节点 3 再到节点 4 的最短路径上有一个距离。

可视化超级步骤行为

如果算法的文本描述很复杂,那么展示某种基于调试的运行会使画面更清晰。这是再次作为复习的图表。

作者图片

超 0 步

该算法从所有活动节点开始,并且所有节点都接收到+Inf 的初始消息。

每个节点运行顶点程序,该程序不会对任何节点的顶点数据造成任何改变,因为+Inf 的初始消息不小于任何节点的顶点数据。

下一个 sendMsg 功能在所有(活动)节点的所有出站边缘被触发,这意味着:

  • 节点 1 向节点 2 和 3 发送两个消息,每个消息的值分别为零加上 1 & 2 和 1 & 3 之间的边距离,因为该值(5 或 7)小于目标节点上的顶点数据值。
  • 节点 2 和 3 不向节点 4 生成任何消息,因为它们的顶点数据仍然是+Inf。刷新一下你的记忆,是 Iterator.empty 行。
  • 节点 4 也不生成任何消息,因为它没有出站边。

超级第一步

顶点程序根据上一步的消息运行。

  • 节点 2 从节点 1 接收值为 5 的消息。节点 2 的顶点数据被更新为 5,因为它小于+Inf 的当前值。
  • 节点 3 从节点 1 接收值为 7 的消息。节点 3 的顶点数据被更新为 7,因为它小于+Inf 的当前值。

在节点 1、2 & 3 的出站边缘上调用 sendMsg (因为它们是仅有的具有当前消息边缘的节点),这产生了以下结果:

  • 节点 1 不向节点 2 和 3 发送任何消息,因为上面代码片段中第 4 行的条件不满足。
  • 节点 2 向节点 4 发送值为 17 的消息,因为 5+ 12 小于+Inf。5 是节点 2 上的值,12 是从节点 2 到节点 4 的边距离。
  • 节点 3 向节点 4 发送值为 15 的消息,因为 7+ 8 小于+Inf。7 是节点 3 上的值,8 是从节点 3 到节点 4 的边距离。

超级第二步

顶点程序根据上一步的消息运行。

  • 因为节点 4 接收到不止一条消息,所以调用函数 mergeMsg 从 17 和 15 中挑选出一条消息。根据 mergeMsg 功能的逻辑选取值为 15 的消息。
  • 在节点 4 上调用顶点程序,节点 4 将其顶点数据从+Inf 更改为 15。

在节点 2、3、4 的出站边缘上调用 sendMsg (因为它们是仅有的具有带有当前消息的边缘的节点),这产生了以下结果:

  • 节点 2 和 3 不发送任何新消息,因为上面代码片段中第 4 行的条件不满足。
  • 节点 4 没有出站边缘,因此它不产生任何消息。

当没有新的消息生成时,算法结束,并返回最终的图,其中每个顶点与节点 1 具有正确的最短距离。

https://tenor . com/view/movies-Matilda-piece-cake-piece-gif-16988524

希望这解释了 Spark Pregel API 是如何工作的,以及它如何用于解决大型图形上的几种类型的问题。

等等!这里不是包装。再来讨论一下我最*遇到的另一个实际用例。

有待解决的新的重大问题

你可能会想到使用一些社交媒体或维基百科或引用数据集的复杂大数据规模问题。这实际上是一个经典问题,可以使用递归公共表表达式来解决(Spark OOTB 目前不支持这一功能)。数据大小可大可小,但这并不十分相关,因为 Pregel 旨在处理大数据。

假设有一个传统的雇员表,包含 Id、经理 Id 和所有其他常见属性,如姓名、部门、加入日期等。要求是检查这个 employee 表,找出层次结构中向某个雇员报告的所有雇员(直接/间接)。

作者图片

这可以用于应用一些安全措施,使得一个雇员可以查看她自己以及直接或间接向她报告的任何其他雇员所拥有的数据或人工制品。这听起来像动作/间谍电影中的通关等级概念。

如果我们可以将每个雇员的层次结构路径编码为雇员记录的一部分,那么就可以应用一个简单的表过滤器来查找一个雇员记录以及向他报告的所有雇员。

例如,上面屏幕截图中的雇员层次结构路径可以如下保存:

  • 苔莎
  • 苔莎|爱德华|
  • 苔莎|埃文|
  • |泰莎|埃文|约翰|

为了获得 Evan 的根层次结构,我们可以过滤表中任何路径包含 |evan|的人。

附注:路径将由员工的唯一 ID 组成,这些 id 可能看起来像 GUID 之类的,并且 id 中不允许使用分隔符。

但是为什么要把 Pregel 牵扯进来呢?这可以在 SQL Server 或 Oracle 上使用递归 cte 来实现。使用 Pregel 预计算层次路径可能有两个原因:

  1. 员工数据是作为 ETL 管道的一部分准备的,并存储在一些可能不支持递归 cte 的数据存储中。管道使用 Spark 来准备接收和转换数据。Spark 还没有递归 cte,所以 Pregel 恰好是一个很好的选择。
  2. 预先计算路径有利于提高性能,这样消费者就不必总是运行这个逻辑(即使他们支持针对该数据存储的递归 cte)。许多 APIs 中间件系统都有积极的性能 SLA,让这样的数据提前持久化是一个明显的优势。

在 Graphx 中准备人力资源图表

我就不再赘述了,因为现在事情应该相当简单了,下面是上一节中显示的 HR 图如何在 Graphx 中表示。

因为边属性值在我们的例子中是不相关的,所以它被设置为默认值 0L。

作者图片

为预凝胶计算准备图形

在 SSSP 的例子中,已经创建了图,使得开始节点的值为零,并且每个其他节点的值为+Inf。这足以让我们使用 Pregel 开始计算。在其他情况下,可能有必要根据问题类型,用一些初始状态来改变输入图形数据。

在我们的 HR 案例中,如果节点有父节点,我们可以将每个节点数据更改为空字符串,否则节点的数据将由两个|字符组成的节点 Id 包装。

稍后您将看到如何将所有这些图连接在一起,以得到一个包含 Id、雇员姓名和路径的图或表。

作者图片

该图具有相同的节点和边结构,但是每个节点值都从雇员姓名更改为一些不同的初始值。但是只要原始的数字 id 还在,就不会丢失任何东西。顺便说一下,这里一个明显的假设是,除了高层员工,每个员工都有一个经理。这也意味着每个节点只能接收一条或零条消息。

编码层次路径的预凝胶逻辑

以上真的很简单:

  • 初始消息被设置为空字符串,表示没有路径信息。
  • 如果发送的消息具有非空路径,顶点程序改变当前节点上的路径。
  • Send message 函数检查源节点是否有路径,而目标节点是否有,如果有,则向目标节点发送一条消息,连接源节点路径和目标节点 Id。
  • 甚至不需要合并消息函数,因为它永远不会被调用,所以任何虚拟逻辑都足够了。

这是预期的输出。

作者图片

要重复查找 Evan(节点 3)层次的逻辑,应该是这样的:

SELECT * FROM EMPLOYEE WHERE PATH LIKE '%|3|%'

连接输出图和输入图

最后一个逻辑是将上面的图与原始图连接起来,这样我们就可以得到一个包含层次结构路径的雇员数据的单一视图。

作者图片

2022 年 5 月更新—多母公司人力资源层次结构

Hareesh Alamala 留下了一条评论,询问如果一个节点有多个父节点,如何调整算法。让我们在 DAG 上尝试相同的 HR 层次路径,其中一些节点有多个父节点。预期的结果将是相似的,除了每个节点有一个路径数组而不是一个路径,因为在这种情况下可能有许多父节点。

https://upload . wikimedia . org/Wikipedia/commons/thumb/f/Fe/Tred-g . SVG/1920 px-Tred-g . SVG . png

上面的输出显示了一些节点(1,2,3)有一条路径,其余的有多条路径。

作者图片

我还更新了算法解释,以反映节点暂停的原因,不是因为它们没有生成/接收消息,而是因为没有消息的边接触到这些节点。文件通过下面的评论清楚地解释了这一点。

发送新消息,跳过两边都没有收到消息的边。

包裹

这需要一点努力,但希望你理解 Pregel 是如何工作的,以及如何用它来解决现实世界的问题,即使它们看起来不像典型的图形问题。

代码是用 Scala 编写的,只需稍加修改就可以移植到 PySpark。

这篇文章中使用的图表的性质也有一些隐含的假设。如果图中有负值的圈或边(在 SSSP 的情况下),这可能会导致无限循环。所以先试着理解你的图,验证你的 Pregel 逻辑会收敛到比 Int.MaxValue 少的步数。😂

借助 JupyterLab SQL Magic 简化 Spark 流

原文:https://towardsdatascience.com/spark-streaming-made-easy-with-jupyterlab-sql-magic-53febf21d9fa

jupyterlab-sql-editor 是一个 jupyterlab 扩展,它使执行、显示和管理 Spark 流查询变得轻而易举

法迪拉·努尔哈基姆在 Unsplash 上拍摄的照片

加拿大网络安全中心(CCCS)的角色之一是计算机应急小组。在这个角色中,CCCS 检测异常并尽快发布缓解措施。

在响应时间至关重要的环境中,CCCS 利用 Spark 结构化流Kafka 事件流*台。

在本文中,我们将演示我们对 JupyterLab 扩展的最新添加,JupyterLab-SQL-editor,对 Spark 流的支持。参见我们之前的文章, 在 JupyterLab 中编写可组合的 Spark SQL analytics,获得完整的特性列表以及如何安装 jupyterlab-sql-editor。

典型火花流 API 用法

这里有一个在笔记本中使用 Spark 流 API 的典型例子。注意.format(“memory”)的用法。console 水槽在笔记本环境中不工作,因此我们使用memory 水槽,这是笔记本中一个众所周知的替代品。

为了显示流式查询的结果,我们从流式查询创建的mem_results 表中检索数据。

包装样板代码

jupyterlab-sql-editordisplay_df 函数现在能够检测到所提供的数据帧是一个流数据帧。在这种情况下,它将执行上面的所有锅炉板代码,并显示mem_results 表的当前结果。

你所要做的就是创建一个流数据帧,并将其传递给display_dfdisplay_df 可以以多种格式显示结果,并显示结果的模式(形状)。

除了这些特性之外,display_df 函数现在显示了一个与流查询相关的 UI。它显示流式查询、指标和停止按钮的状态。

使用 SQL

流式数据帧可以作为临时视图。在本例中,我们创建了一个流数据帧,然后将它命名为视图uuid_events

使用show tables我们可以看到这个视图的存在,这意味着我们可以使用%%sparksql魔法来查询它。事实上,我们还可以调用%%sparksql --refresh local%%sparksql将这个视图信息存储到它的自动完成缓存中。

注册了这个视图并缓存到%%sparksql中,我们现在可以利用%%sparksql对常规表的所有支持。这些特性包括输出模式、jinja 模板、截断、限制、自动完成、格式化和语法高亮。

记住,我们实际上是在编写一个流 SQL 查询。因此,任何聚合都需要绑定到一个时间窗口。有关更多详细信息,请参见 Spark 结构化流指南。下面是一个流查询的例子,它计算每个字符在 5 秒钟的窗口中出现的次数。

以下是现场结果。注意charactercount 与时间窗口win相关联。

结论

在本文中,我们展示了如何利用**%%sparksql**在 Spark SQL 中轻松构建流分析原型。魔术照顾了许多锅炉板代码,让您专注于您的流分析的阐述。您还可以使用由%%sparksql magic 支持的许多输出(文本、网格、html、json)来验证您的结果。欢迎新的功能想法和贡献!这是我们的 git repocyber centre Canada/jupyterlab-SQL-editor

简化的 Spark 结构化流

原文:https://towardsdatascience.com/spark-structured-streaming-simplified-9455947674b9

用 Spark 处理流数据的简单概述

卢克·切瑟在 Unsplash 上的照片

嘿伙计们!

如果您是使用 spark 进行流处理的初学者,或者即使您已经多次使用过它,但是想要更好地理解 Spark 结构化流,那么本文就是为您准备的!

在讨论在 Spark 中处理流数据之前,我们先来了解一下什么是流数据处理,它与批量数据处理有何不同?如果您已经熟悉了这些概念,请直接跳过。

流数据处理

在做出接*实时的决策时处理数据。

例如,当欺诈发生时的欺诈检测、通过分析错误率来检测错误的服务器等。

流处理和批处理有什么不同?

批量数据处理是对一段时间内积累的数据的处理。

这些是您每天、每周或每天两次运行的正常流程/作业。无论数据何时到来,它总是以固定的定义间隔被处理。

我想在这里强调的另一个区别是,就可靠性而言,批处理总是比通过流处理生成的聚合更可靠,尤其是在流处理没有正确配置的情况下,但是与批处理相比,流处理允许更快地解释数据。

另一个普遍的区别是,与批处理相比,流处理通常占用更少的内存,因为每次处理的数据更少。

批处理与流中的数据处理(图片由作者提供)

定义了流数据处理之后,让我们深入了解如何使用 Spark(即 Spark 结构化流)来做同样的事情。

火花结构化流

Spark 结构化流允许通过 Spark SQL 引擎对流数据进行*实时计算,以根据定义的逻辑生成集合或输出。

这种流数据可以从文件、套接字或 Kafka 之类的源中读取。

最酷的是,处理实现的核心逻辑与如何以批处理方式处理数据密切相关。基本上,您可以定义数据框,并像编写批处理作业时一样使用它们,但是数据的处理有所不同。

这里需要注意的一点是,结构化流不是实时处理数据,而是*乎实时处理。事实上,你很少会找到一个管道或系统,可以“真正”实时地处理数据而没有任何延迟,但这是一个单独的讨论。

微量配料

结构化流有一个微批处理的概念来处理数据,这意味着不是每条记录都被处理。相反,它们是以小批量的方式累积的,并且这些微小的(微小的批量)被一起处理,即接*实时。您可以配置您的微批处理,并且可以低至几毫秒,例如:

微量批次(图片由作者提供)

无界表

如上所述,您在结构化流作业中处理数据帧,就像批处理作业一样。但是如果你认为在这里?如何在流数据上创建数据帧?我的意思是,如果有 100 条记录,这对于批量数据来说是非常简单的,这是一个包含 100 条记录的数据帧,但是对于连续不断的流数据来说呢?在这里花点时间思考一下。

这里出现了无界表的概念:随着数据的到来,在处理微批处理时,行被追加到表中。当新数据到来时,计算在应用于表时完成,直到在数据帧上定义了逻辑。所以基本上,数据帧是在这个无界的表上创建的。

这里要注意的一点是,这个无界表更多的是一个“概念性的东西”。Spark 不会将整个无界表保存在内存中,但是它会继续写入结果,并且只在内存中维护一个最小的必需中间状态,

无界表的概念(图片由作者提供)

扳机

现在,Spark 如何知道何时生成这些微批处理并将它们追加到无界表中呢?

这种机制被称为触发。如前所述,并不是每条记录都在某个时间间隔(称为“触发”时间间隔)被处理,一个小批量的行被追加到表中并被处理。这个时间间隔是可配置的,并有不同的模式,默认模式是开始下一个过程作为前一个完成。

触发的概念(作者图片)

触发示例(图片由作者提供)

输出模式

现在我们知道,在触发时间间隔,数据处理开始,输出按照定义的逻辑生成。假设您希望在触发时间间隔内处理成功、失败或未决事务的总数,这样就会生成这个结果。

对于写入此结果或“输出”,有不同的模式可以完成:

  • Complete:整个结果作为输出存储或写入
  • Append:只有结果中的新记录作为输出写入
  • Update:只有结果中更新的记录才作为输出写入

这种输出模式是可配置的,可以根据使用情况进行设置。为了产生这些结果,

容错

现在,在一个完美的世界中,一旦您开始流处理,一切都运行顺畅,流永远不会关闭或抛出任何错误(听起来像一场梦,不是吗?)。

但可悲的是,在现实世界中,管道确实会失败,或者不得不关闭,甚至重新启动,以应用逻辑中的新变化。在这种情况下,“流”数据处理会发生什么?

结构化流通过检查点和预写日志来处理这个问题。简而言之,在 HDFS(或 S3,无论底层存储层是什么)的某个位置记住并存储数据,直到数据被处理,从而确保容错。以便数据只被处理一次,并确保幂等性。

这基本上就是结构化流的全部总结。我就讲到这里,让你吸收这些信息。

流处理还有很多有趣的东西,特别是这里的结构化流,如流流连接、小文件问题、后期数据处理、基于窗口的处理。但不是要在这里压倒你,我会做一些后续文章。

在那之前,如果你有任何问题或反馈,请留下评论或在 LinkedIn 上与我联系,说声“嗨”!

快乐编码,
JD

稀疏自动编码器神经网络——如何利用稀疏性进行鲁棒信息编码

原文:https://towardsdatascience.com/sparse-autoencoder-neural-networks-how-to-utilise-sparsity-for-robust-information-encoding-6aa9ff542bc9

神经网络

用一个详细的 Python 例子比较欠完整和稀疏 AE

作者创建的稀疏自动编码器(SAE)特色图像。

介绍

自动编码器使我们能够利用由编码器解码器组成的神经网络架构来提取信息。自动编码器有多种类型,根据它们的结构或它们要解决的问题而有所不同。四个最常见的是:

本文将关注稀疏自动编码器(SAE) ,并将它们与欠完整自动编码器(AE)进行比较。

内容

  • 机器学习算法领域中的 SAE
  • SAE 的结构以及它与不完整 AE 的不同之处
  • 如何使用 Keras/Tensorflow 在 Python 中构建 SAE

机器学习算法领域中的稀疏自动编码器(SAE)

我试图对最常见的机器学习算法进行分类,如下所示。

虽然我们经常以监督的方式使用带有标签的训练数据的神经网络,但我们也可以以非监督或自我监督的方式使用它们,例如通过使用自动编码器。因此,我觉得神经网络的普遍性和它们对机器学习的独特方法值得一个单独的类别。

点击下面互动图中的不同类别,找到 SAE 和其他类型的自动编码器👇。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的邮件。如果你不是中等会员,可以在这里 加入

SAE 的结构以及它与不完整 AE 的不同之处

自动编码器的目的是有效地编码重要的信息。实现这一点的一个常见方法是创建一个瓶颈,迫使模型保留必要的东西,丢弃不重要的部分。

Autoencoder 可以通过同时训练编码器和解码器来区分什么是重要的,解码器的目标是从编码表示中重建原始数据。

下图提供了一个瓶颈位于中间的欠完整自动编码器神经网络的示例。

欠完整自动编码器架构。图片由作者、使用 AlexNail 的 NN-SVG 工具创建。

同时,SAE 的目标与不完全 AE 的目标相同,但是它实现的方式不同。代替(或除此之外)依赖更少的神经元,SAE 使用正则化来加强稀疏性。

所谓稀疏,我们的意思是同时激活的神经元更少,产生了类似于不完全 AE 的信息瓶颈。请参见下图。

稀疏自动编码器(SAE)架构。图片由作者、使用 AlexNail 的 NN-SVG 工具创建。

请注意,上图仅代表许多潜在设置中的一种。例如,可以有多个隐藏层,或者限制中间(瓶颈)层的神经元数量。

严重不良事件和不良事件之间的主要区别是规范化。在接下来的 Python 小节中,我们将看到它在实践中是如何工作的。

如何使用 Keras / Tensorflow 在 Python 中构建稀疏 AE

设置

我们需要以下数据和库:

让我们导入所有的库:

上面的代码打印了本例中使用的包版本:

Tensorflow/Keras: 2.7.0
numpy: 1.21.4
matplotlib: 3.5.1
seaborn: 0.11.2

接下来,我们加载 MNIST 手写数字数据并显示前十位数字。我们将在可视化中使用数字标签(y_train,y_test ),但不用于模型训练

MNIST 数据集的前十位数字。图片由作者提供。

如您所见,我们在训练集中有 60,000 张图像,在测试集中有 10,000 张图像。请注意,它们的尺寸是 28 x 28 像素。为了使这个例子简单,我们将不使用卷积层,而是将图像展*,这样我们就可以通过密集的全连接层。

New shape of X_train:  (60000, 784)
New shape of X_test:  (10000, 784)

构建自动编码器模型

我们将建立两个自动编码器模型,以便更容易地看到欠完整 AE 和稀疏 AE 中神经元激活之间的差异。

我们从不完全 AE 开始。以下是需要注意的关键事项:

  • 该模型由 5 层组成:一个输入层、三个隐层和一个输出层。
  • 输入和输出层各包含 784 个神经元(我们数据的形状),隐藏层的大小减少到 16 个神经元。
  • 我们将在 50 个时期内训练模型,并绘制损失图(见下文)。
  • 我们将分离模型的编码器部分,并将其保存到我们的项目目录中。请注意,如果您不打算在以后重用同一个模型,您不需要保留它的副本。

在组装模型之后,上面的代码输出模型摘要,然后是损失图表。

不完全 AE 模型总结。图片来自作者

不完全 AE 模型损失图。图片由作者提供。

现在让我们建立一个稀疏的自动编码器模型。为了便于两个模型之间的比较,我们保持参数不变。唯一的区别是瓶颈层增加了 L1 规则。

另外,请注意,我们对神经元激活(activity _ regulator)应用正则化,而不是权重(kernel _ regularizer)。

以下是我们 SAE 模型的总结和模型损失图表:

稀疏 AE 模型总结。图片由作者提供。

稀疏声发射模型损失图。图片由作者提供。

最后,是时候对比这两款车型了!

旁注,如果你想看看模型如何再现原始图像,你可以参考我的 GitHub 上的完整 Jupyter 笔记本代码(文章末尾的链接)。

回想一下之前的内容,不同的输入会导致不同神经元的激活。因此,让我们从 MNIST 测试集中选择一个数字来代表大范围的输入。请注意,我们需要将数组从 784 重新调整为(28 x 28)以显示图像。

所选的十位数用于模型比较。图片由作者提供。

现在让我们使用每个型号(AE 和 SAE)的编码器部分对上述图像进行编码。

编码后,我们绘制一个热图,显示瓶颈中的哪些神经元被激活(≠ 0)。行是神经元,而列是数字(0-9)。

两种声发射模型中的神经元激活。图片由作者提供。

我们可以从上面的图表中看到,我们的 SAE 模型更加稀疏,不活跃神经元的数量是不完全 AE 的三倍。

结束语

我希望我的文章为您提供了 SAE 和 AE 之间的有益比较。但是,当然,最好的学习方法是通过实践,所以请随意参考我的 Python 代码,可以在我的 GitHub 库上找到。

如果你想在我发表新文章时得到通知,你可以订阅接收电子邮件或在 LinkedIn 上与我联系。

如果你不是中级会员并且想继续阅读成千上万伟大作家的文章,你可以使用我下面的个性化链接加入:

https://bit.ly/3J6StZI

如果您有任何问题或建议,请随时联系我们!

干杯!🤓
索尔·多比拉斯

稀疏矩阵:为什么它们对机器学习和数据科学很重要

原文:https://towardsdatascience.com/sparse-matrices-why-they-matter-for-machine-learning-and-data-science-55d704d936b2

以及为什么你应该关心

来源: Unsplash

介绍

什么是稀疏数据?

当使用矩阵表示数据时,我们可以量化它包含的空值的数量。这被称为稀疏性。主要包含零的矩阵(或数据集)称为稀疏矩阵。

简单的例子

假设你让你的 4 个朋友给你 4 部不同的电影打分,从 1 分到 5 分(如果他们没看过,给 0 分)。现在,假设你得到了以下评价:

矩阵示例(图片由作者提供)

这意味着约翰没有看过电影 1、2 和 4,但给了第三部电影 2 的评级。

这个矩阵的稀疏度很低——准确地说是 38 %( 16 个值中的 6 个零= 3/8 稀疏度),我们实际上称它为“密集”矩阵。现在,想象你有更多的电影。假设你有 15000 部电影(相当于网飞目录的大小)。

你可以猜到,大多数人并没有看过网飞的全部 15,000 部电影。因此,假设每部未看过的电影的评分为零,你可以想象这个矩阵会有多稀疏:它大部分由零组成。

我为什么要在乎?

两个原因:计算复杂度和存储。

稀疏矩阵的存储

理想情况下,我们希望只存储非零值,而不是整个数据集——为什么要存储大量总是具有相同值的元素呢?相反,我们只能存储非零元素的值。在下面的例子中,我们将只存储[1,2,4,2,1,5,1,2,3,1]的值以及它们在矩阵中各自的位置。然后,我们知道矩阵中的所有其他值都是零,我们不需要存储它们。

稀疏矩阵的示例(图片由作者提供)

但是我们在实践中如何做到这一点呢?

首席运营官格式

简单:存储 3 个数组,一个存储值,一个存储每个值的行位置,一个存储每个值的列位置。这被称为坐标列表(首席运营官)格式。这是前一个示例中 3 个阵列的样子:

之前的首席运营官格式矩阵(图片由作者提供)

等等,但是要知道,我们存储的是 3 个 11 个值的数组,总共 33 个,而矩阵包含 30 个值。那有什么意义呢?

假设您的数组包含 50 列和 10,000 行,因此有 500,000 个值。此外,它只包含 10,000 个非零值(即 98%稀疏)。您是愿意存储 3 个包含 10,000 个值的数组,从而存储 30,000 个值还是 500,000 个值?

这几乎减少了 20 倍的存储量。

因此,在处理具有高度稀疏性的大型数据集时,使用稀疏矩阵是很有趣的。

计算的复杂性

稀疏矩阵的第二点是在执行计算时。让我们以一个向量乘以一个矩阵为例:

带有稀疏矩阵的矩阵向量乘积(图片由作者提供)

在这里,您可以看到,对最终结果有影响的唯一操作是将两个非零值相乘。然而,我们仍然在执行 9 个浮点运算,而只有 3 个是重要的。

理想情况下,我们希望跳过有零的计算,得到一个矩阵向量乘积,其计算复杂度只取决于矩阵非零元素的数量,而不取决于值的总数。这在实践中会有更好的伸缩性。幸运的是,大多数 ML 库都有稀疏矩阵的实现。

网飞奖

现在让我们看一个例子,看看稀疏矩阵在实践中是如何有用的。

从 2006 年到 2009 年,网飞赞助了一个竞赛来改进它的电影推荐系统。他们提供了一个 100,480,507 个评级的训练数据集,480,189 个用户给了 17,770 部电影。

对于每一行,您都有一个用户 id、他们看过的电影以及他们对该电影的评价。我们的目标是预测用户对某部电影的评价(使用 1-5 星系统)。

在这个例子中,我运行了下面的代码。它指定了我感兴趣加载的列及其数据类型,以便以最有效的方式存储它们。

然后,它加载数据集,重命名它的列,并打印出前 5 行以及内存使用情况。

加载 Netflix 奖品数据集(图片由作者提供)

这里的问题是我们想知道用户看过什么电影来预测他们是否会喜欢一部电影。下面是我们希望如何格式化我们的数据(类似于我们看到的第一个例子):

一键编码我们的数据矩阵(图片由作者提供)

这样,我们将能够知道用户看过的所有电影,并使用它来预测他们将给予他们没有看过的新电影什么评级。

然而,正如本文开头所讨论的,你可以想象大多数人并没有看完网飞的全部 17,770 部电影。假设人们在网飞上*均观看了 150 部电影(网飞用户*均每周观看 3.4 部电影),在一次热编码后,数据集将稀疏约 99%。

参考前面的示例,想象一下,您向数据集添加了大约 17,000 列,乘以大约 100 万行就可以得到您添加的值的总数。你真的想存储所有这些值,而知道它们 99%都是零吗?

相反,当调用独热编码函数(这里是 pd.get_dummies)时,我们为参数 sparse 传递一个 true 值。这告诉 pandas 将我们的新数据存储为稀疏数组。

熊猫代码一键编码你的数据矩阵(图片由作者提供)

现在的内存使用量约为 990MB,而在一次性编码之前为 700MB。考虑到我们添加到数据集中的值的数量,这是相对较小的。

作为参考,我用 sparse=False 运行了同一行代码,然后……我的电脑崩溃了。

用稀疏数据改进 scikit-learn 代码

正如我之前提到的,在执行计算时,稀疏矩阵对于降低计算复杂度也很有用。Sklearn 的大多数算法都有稀疏实现。以下是随机森林分类器文档中的一个示例:

随机森林分类器的 Sklearn 文档(https://sci kit-learn . org/stable/modules/generated/sk learn . ensemble . randomforestclassifier . html)

这里使用的 CSR 格式是实践中最常用的,接*首席运营官格式。我解释了首席运营官的,因为它对初学者更友好。

确保在运行机器学习模型时使用稀疏矩阵可以大大加快运行时间。事实证明,这在运行网格或随机搜索时更加有用,因为这可能非常昂贵。

结论

在本教程中,我们看到了为什么稀疏矩阵与机器学习相关,以及它们如何帮助减少数据集的存储以及运行 ML 算法的计算成本。我们还研究了这如何应用于网飞奖数据集。

我希望你喜欢这个教程!让我知道你对它的看法。

随时连接上LinkedInGitHub多聊聊数据科学和机器学习!

空间数据科学:数据结构

原文:https://towardsdatascience.com/spatial-data-science-data-structures-a00c484b8a00

里尼和作者(2022)

表达空间数据的指南

介绍

我们如何定义空间数据?一个正式的标准遵循 OGC 标准的简单功能与众所周知的文本(WKT)。很有可能,这也是大多数人所需要的。我认为这并不新奇,但是下面的官方文件乍一看可能很吓人。

https://www.ogc.org/standards/sfa

欣赏文档让我们有了在数据库中存储空间数据的想法。本文简要介绍了如何通过理解空间数据结构来存储空间数据。

在本文中,我将解释我们如何用 WKT 格式定义空间数据,以及如何用 JSON (Javascript 对象表示法)构造空间数据。为什么是 JSON?所以我可以在 Ms Excel 中把它可视化,让每个人都能理解。这不是我们在数据库服务器中存储数据的方式,但它给出了数据的结构。

著名的文本

众所周知的文字(WKT)是定义几何学的标准。我认为维基百科的文章很好地解释了它是如何工作的。

https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry

空间数据(在这种情况下,让我们坚持简单的矢量要素)由坐标和类型组成。有 3 种基本类型:点、线串和多边形。

  • 最简单。它是一个点;我认为这是不言自明的。它由 1 个坐标组成。

By Mwtoews —自己的作品,CC BY-SA 3.0,【https://commons.wikimedia.org/w/index.php?curid=14591527

  • LineString (线),是一条线。一串组成一条线。你至少需要两个点来组成一条线。

由 Mwtoews —自己的作品,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=14594518

  • 多边形,是封闭的线串。任何线都可以转换成多边形,它是一个面。

由 Mwtoews —自己的作品,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=14592092

在 WKT 格式中,我们首先定义类型,然后是组成空间数据的坐标;举个例子,

"POLYON (0 0, 1 0, 1 1, 0 1)"

这张基本上是 1x1 的正方形。请注意,我将 WKT 写成了文本文件,因此我们可以在 Ms Word 中清晰地表达空间数据!

杰奥森

还有另一种格式,一种更加通用的格式,那就是 JSON 格式。GeoJSON 是 JSON 格式的扩展,但它是 JSON。JSON 格式是什么?JSON 是一种定义在花括号内的键值对格式。例如,定义伦敦城,

{"city": "london", "country": "UK"}

或者巴黎,

{"city": "paris", "country": "france"}

我们可以把任意的属性放在这个括号里,

{"city": "berlin", "country": "germany", 
**"**coordinates**": [52.507650, 13.410298]**}

稍等一下;我只是把位置数据放在这里!我们知道它是一个点坐标。但是,我们需要在 JSON 数据中指定它。因此,结论是,

{"city": "berlin", "country": "germany",
"location" : {
    "type" : "**Point**",
    "coordinates": [**52.507650, 13.410298**]
}}

在 Excel 中可视化空间数据结构

在我的另一篇文章中,我解释了我们如何将花括号看作表格行。我们将使用 JSON 格式在 excel 中可视化数据结构。

https://perkotaan.medium.com/nested-table-in-excel-9977637a74aa

我们之前的 JSON 城市表如下图所示。

来源:作者(2022)

这是在没有空间数据的情况下,现在空间数据(一堆坐标和类型的定义)作为 JSON 存储在单元中。

在 excel 中存储数据结构,来源:作者(2022)

或者,我们可以为伦敦添加另一个多边形数据示例

来源:作者(2022)

当我们可视化伦敦多边形的坐标时,它看起来像下面的图像:

来源:作者(2022)

或者我们也可以用 WKT 格式来声明它,这对于这个上下文来说更简单。

来源:作者(2022)

它们都转化为相同的数据:城市。然而,我们可以用各种格式存储空间数据,它们都仍然有效。

结论

空间数据是一串具有定义类型的坐标(点?多边形?线?)如果我们能以某种方式:

  • 定义类型和
  • 定义坐标组成、

然后我们有了空间数据。这就是 WKT 和乔森的用武之地。首先,我们定义几何图形的类型,然后我们定义组成几何图形的坐标。使用其中一种格式,我们可以声明我们拥有的每个特征/数据行的空间属性。它们是可以用电脑分析的,所以我们可以用电脑软件来分析它们!

空间数据科学:在 Windows 和 MacOS 上安装 GDAL

原文:https://towardsdatascience.com/spatial-data-science-installing-gdal-on-windows-and-macos-6fb5c958dc26

作者和里尼(2022)

GDAL 是空间数据科学的基础。在 windows 和 MacOS 上安装比较棘手。这是实用指南。

介绍

在 MacOS 或 Windows 上调用$ pip install fiona,你将很难找出哪里出错了。您很可能会遇到以下错误:

c:/> pip install fiona
Collecting fiona                                                                                                          Downloading Fiona-1.8.21.tar.gz (1.0 MB)                                                                                   ---------------------------------------- 1.0/1.0 MB 8.2 MB/s eta 0:00:00                                             Preparing metadata (setup.py) ... error                                                                                 error: subprocess-exited-with-error...**A GDAL API version must be specified. Provide a path to gdal-config using a GDAL_CONFIG environment variable or use a GDAL_VERSION environment variable.**

fiona是 GDAL 的 python 链接;它是允许 python 开发者读写空间数据的模块。它有一个基本的依赖:GDAL是空间数据输入输出背后的主要软件。另一个是GEOS,它是GDAL和处理背后的几何引擎,但在本文中它不是一个非常突出的问题。突出的问题是GDAL在 windows 和 macos 上的安装很棘手。

在这篇文章中,我将提供一个实用的指南,在你的 windows 和 MacOS 机器上安装GDAL。注意是实用的;这很可能与你有关,但你的实际需求取决于你要做的事情的背景。

本文回答的一些问题:

  • 如何在 windows 上安装 gdal?
  • 如何在 macos 上安装 gdal?
  • 如何使用 GDAL_CONFIG 环境变量或在 windows 或 mac 上使用 GDAL_VERSION 环境变量提供到 gdal-config 的路径

总之,这是需要做的事情:

  • 安装 anaconda

  • 使用 Anaconda 提示符,而不是通常的命令提示符(cmd.exe)。在 Mac 中,这相当于在环境变量中设置 anaconda 提示符。对于 mac 用户,请遵循

  • 熟悉管理 python 包和环境的conda命令行工具

替代安装;如果我们不想使用蟒蛇

  • 安装 QGIS
  • 从 OSGeo4W shell 或从 QGIS 控制台调用 GDAL。

让我们开始吧。

选择 1:蟒蛇

第一步是不要在 python.org 的官方 python 网站上使用 python。如果你已经从官方的 python 网站安装了 python,那就没什么好担心的,也不需要卸载。我们只需要安装来自另一个发行商的另一个 python:Anaconda。

Anaconda 是一个 python 发行商,但是它附带了conda。我们可以把conda看作是pipvenv的替代品,虽然不是真的。conda是一个在anaconda环境中管理 python 库和环境的工具。如果您不理解这一点,特别是关于环境的部分,请不要担心,但是我们可以从安装 anaconda 开始:

https://www.anaconda.com/

安装向导(来源:作者(2022))

从开始按钮调用 anaconda 提示符:

来源:作者(2022)

然后我们看到 anaconda 提示:

蟒蛇提示(来源:作者(2022))

我们可以把这看作另一个命令提示符;但是这个环境比较特殊,因为它是一个 anaconda 环境。我们可以使用conda来做事情。尝试调用conda -V查看conda版本。

来源:作者(2022)

忘记皮普,与康达和解

我们仍然可以使用pip来安装包。但是对于 gdal,让我们忘记pip install gdal或者pip install fiona,转而使用conda.

现在我们可以通过键入conda install gdal在 python 中安装 gdal。准确的说,conda install **-c conda-forge** gdal。我们加上-c表示我们正在使用一个通道,那个通道就是conda-forge。Conda 是 python 的包管理器;它像 pip 一样工作,但是在 anaconda 环境中。我们可以看到康达像皮普,但它可以做得更多。让我们在其他时间探索 conda 可以做什么,但现在让我们专注于安装 Gdal。

安装 gdal 后,应该可以通过 anaconda 提示符使用 GDAL。在你的开始菜单中搜索 Anaconda 提示,并点击它。它将打开一个命令提示符窗口,但与 anaconda 环境。将其视为普通的命令提示符。尝试调用gdalinfo --version。如果它打印出类似下图的内容,那么您已经成功安装了 GDAL!

来源:作者(2022)

是的,你已经安装了 GDAL!

现在,要使用 gdal,只需打开 anaconda 提示符,像普通的终端/命令提示符一样使用它,然后就可以开始了!

一种更简单的安装方法是使用 OSGEO Shell。

总结和安装菲奥娜

Fiona 是空间数据 IO 的 python 接口。它使用 GDAL,在空间数据科学中至关重要。下面是 Anaconda 提示符下安装 Fiona 和 GDAL 的命令行:

C:\> conda install -c conda-forge gdal
...
C:\> conda install -c conda-forge fiona
...
C:\> echo gdal and fiona are installed. let's check it. we can use python and import osgeo module to import gdal, or just straight type gdalinfo --version
C:\> python -c "import fiona; print(fiona.__version__)"
C:\> python -c "from osgeo import gdal; print(gdal.__version__)"
C:\> gdalinfo --version

备选方案#2: Osgeo4W shell,或来自 QGIS

这就简单多了。装 QGIS 就行了。QGIS 是地理信息系统软件;这是 GIS 首先要学的第一件事。它是免费的,开源的,面向所有人。

您可以从终端内部的 python shell 或从 QGIS Python 控制台访问 GDAL。如果您正在使用 python 控制台,那么我希望您对简单的 Python 语法有基本的了解。我不认为学习 python 需要那么长时间,这很简单。话又说回来,反正你用的是 GDAL,Python 应该不难;很难想象使用 GDAL 的人能不学习 Python(仅是我的看法)。

从 QGIS python 控制台使用 python 导入 gdal。(来源:作者(2022))

顶生花柱

如果您想将它用作普通的终端风格 CLI,那么搜索osgeo4w shell并执行它。它将打开一个命令提示符/终端,并设置 GDAL 环境。再次调用gdalinfo --version来验证它是否已经安装。

结论

耶!我们已经用 conda 或 QGIS 安装了 GDAL。最简单的也许就是安装 QGIS,没有比点击安装并按照向导操作更简单的方法了。长期措施是安装 anaconda 并学习基本的 conda 语法。我建议将它安装在 anaconda 中,因为它可以比 QGIS 控制台更好地预测未来的需求。

空间数据科学:Javascript 和 Python

原文:https://towardsdatascience.com/spatial-data-science-javascript-python-60464cbe908d

作者和里尼(2022)

从数据工程到全栈应用,你需要的语言。入门和精选学习资源!

介绍

进入高级 GIS 意味着做编程工作。这是必然的。当你知道如何编写一些代码时,事情就简单多了!例如,为您的 QGIS 创建自定义插件,或修改现有插件,在 ArcGIS Pro 中创建功能区工具等等。通常新程序员会问自己:“我应该使用哪种编程语言?”答案是,这取决于你想建立什么。在 GIS 的例子中,我可以说每个人最可能使用的流行工具是 Javascript 和 Python。

Python 已经是我多年来最好的朋友了。它加速了我作为交通/城市规划顾问的职业生涯,并为主要的交通基础设施发展项目做出了贡献。事实上,它的免费是非常令人印象深刻的。在这篇文章中,我将分享为什么它是我最好的朋友。

另一种语言是 Javascript。由于语法的原因,Javascript 很容易被嘲笑,这是 r/programminghumors 中的一个笑话。玩笑归玩笑,这是一种让我们的浏览器与我们互动的神奇语言。语法方面,我认为这很奇怪;尽管如此,它是如此强大。在本文中,让我们探讨 Javascript 如何在空间数据科学实践中增加价值。

最后,尽管标题中没有提到,HTML 在将事物联系在一起时起着至关重要的作用。你需要展示东西,这就是你展示的方式。它是你网络浏览器中的文件;这是你现在正在看的网页。

在开始之前,请注意,本文是空间数据科学系列的一部分。更多类似的文章,请访问下面的元文章

https://medium.com/rekayasa-data-spasial/spatial-data-science-the-series-81344a3ead29

让我们探索每一点,好吗?

解释器和编译器简介

在我们学习语言之前,我认为了解语言的类型是很重要的。我们将编程语言分为两类:编译语言和解释语言。这个帖子值得一看。

https://stackoverflow.com/questions/3265357/compiled-vs-interpreted-languages

简而言之:

  • 编译语言:一种由软件将代码(我们在代码编辑器中输入的文本)转换成可执行文件(通常以。windows 中的 exe)。这就是我们所说的通过编译我们的代码。我们不能使用代码本身,我们使用的是可执行文件。
  • 解释语言:一种由另一个可执行文件逐行执行代码的语言。这个可执行文件被称为解释器。 Python 和 Javascript 就是这一类

所以,请记住 Python 和 Javascript 都是解释语言。如果我们想两者都利用,在我们编码之前,我们需要安装解释器;哪个是用于 Javascript 的 Python 和 NodeJS(或者您用来阅读本文的浏览器)。

代码只是一堆文本文件,我们可以用 ms. word 编码,不用担心。重要的是它能够被解释者解释。

计算机编程语言

Python 是一种通用编程语言,可以做任何事情。它经常被拿来和 R 比较,但是,我认为如果你打算研究空间数据科学,我推荐使用 Python。这是因为它的性质;它可以连接到 Python 模块,为您提供可能需要的灵活性。另一方面,我认为 R 在学术环境和统计目的中更为优化。但是嘿,如果你正在制作一个 ArcGIS/QGIS 插件,我不认为你会用 r 来写。

Python 的主要优势是:

  • 对于任何目的来说都很棒,尽管速度很慢,但是对于你可能会发现的许多情况来说已经足够快了
  • 连接任何计算机科学任务
  • 容易学习,语法哲学表达了它必须尽可能的易读

空间数据分析和建模

虽然 Python 有很多用途,但在我们的例子中,Python 用于数据分析和空间数据建模。这是因为朱庇特的笔记本;包含单元格的文本文件。每个单元格包含一个我们可以执行并直接看到输出的代码。这是数据科学 101;学习如何用 Python 使用 Jupyter 笔记本。我认为科里·斯查费的这个视频很好地介绍了 python 在数据科学方面的应用。

(注意:科里·斯查费的视频假设你有一个基本的 python 基础。我把视频放在这里是为了演示 Jupyter Notebook 和 Python 中的数据分析是什么)

当涉及到空间数据建模和分析时,我们将使用 pandas 和 geo pandas(R 等价于模块 sf)。在 Jupyter 笔记本中使用这两个模块要方便得多;并将结果导出为 pdf 或任何空间数据格式,如 shapefiles 或 geojson。

数据工程

当数据存在时,分析数据是容易的,然而,通常情况下,数据分散在整个星系和服务器中。数据工程将这些分散的数据变成可供数据分析师使用的数据。最基本的角色是执行 ETL(提取、转换、加载)。medium 里面有很多关于 ETL 的文章;例如,使用 Apache Airflow,或者通常使用请求模块;但是大部分都在用 Python 或者要求 Python 是主要语言。

https://airflow.apache.org/

全栈应用

最受欢迎的学习库是 Django 和 Flask。两者都有能力生产全栈应用程序或只是一个 web 框架。我个人使用 Django,因为我对它更熟悉,但如果你需要构建一个快速简单的 API,我认为 Flask 更合适。我认为它在 Flask 中非常简单,但在 Django 中要灵活得多。

Python 空间数据科学入门

不要从官方 Python 网站(python.org)安装。而是安装 anaconda!Anaconda 是一个 python 发行商,它将 conda 作为包管理器。Anaconda 在空间数据科学中是非常必要的,尤其是对于新手来说,因为我们将使用 GDAL(地理空间抽象库)。如果能在 Windows 中安装 GDAL,基本上就是安装 Python 了。请看我关于如何入门 Anaconda 和 GDAL 的文章。

这个建议是基于多年的实践。我的判断是,这可能是人们所需要的。

学习资源

这里是一些用来学习和强化 python 基础的精选资源。一些建议:

  • 首先,学习如何使用终端。了解如何调用 Python 控制台。了解如何浏览文件夹。
  • 学习基本语法,如 if 语句、for 循环等。
  • 学习数据结构,如数组和字典
  • 你会发现很多教程会建议你使用 Pycharm。如果这是你第一次编程,我建议不要使用 Pycharm。这是因为你需要了解如何使用终端;Pycharm 实现了自动化。不是怀疑 Pycharm,可能你会发现在那里更容易,我只是认为新的程序员应该使用 Pycharm,如果他们有使用终端的基础。

我认为科里斯查费的提供了一个伟大的视频。

我个人从查克博士的 Python 为大家学习。除了 Python 之外,他还做了一个关于计算机科学基础的简短演讲。

超文本标记语言

超文本标记语言(HTML)不是一种编程语言。这是一种指示哪个部分去哪里的语言。它是一种用于在浏览器中进行布局的语言。

Python 数据分析的结果可以以 HTML 格式呈现。例如,Python 中的 Folium 模块将地理空间信息转换成 html 地图,使我们的地图具有交互性。它利用了 Javascript 传单库,我们稍后将讨论这一点。

https://python-visualization.github.io/folium/

入门指南

HTML 和网络互联网一样古老。创建一个文本文件,并以。名称中的 html。在这个文本文件中,输入<html> hello world, this is an html file</html>,把它拖到你的浏览器中,从技术上来说,你就有了一个网络文档!

现在,在 html 文件中,尝试插入这段文本。它将根据我们的指示放置文本。

Inside your html file, insert shis (between the 3 back ticks):
```<html><div style="position:absolute;top:0;right:0;">
**this is in the top right.** </div><div style="position:absolute;top:0;left:0;">
**this is in the top left.** </div></html>```
You should have the text in the top right and left corner of the browsers.

div是容器,我们将对这些容器进行布局。最终结果看起来像下图。

布局结果(来源:作者,2022)

在空间数据科学方面,这是我们如何布局交互式地图。

学习资源

w3school 是一个非常好的资源。他们提供简单的指导。

https://www.w3schools.com/html/

java 描述语言

Javascript 是让我们的浏览器具有交互性的语言。HTML 是静态的,只提供图片和文本文件。但是,按下按钮呢?在页面加载时制造纺车?换位还是爆位?收到通知时自动更新?所有这些都由 javascript 代码来完成和指示。

Javascript 已经发展成为一种前端工程语言;它可以使你的浏览器具有交互性。然后,NodeJS 被开发出来(Ryan Dahl)允许 Javascript 在机器环境中运行,而不是在浏览器环境中运行。这意味着我们可以像编写 python 程序一样编写 javascript 程序。这对于服务器端编程处理来自前端的请求非常有用。这也被称为后端工程。

所以在 Javascript 中你有两个选择:后端工程和前端工程。在我们这里,把它作为前端工程语言使用是最直接的。Javascript 是我们设计网络地图的方式;与用户互动的地图。受欢迎的图书馆包括:

  • 传单. js
  • 开放层

他们遵循 OGC 标准,因此可以轻松连接到任何地理空间服务器!

入门指南

你现在正在使用 javascript。打开浏览器,然后打开“开发工具”或“检查元素”。有一个控制台选项卡。试着输入类似console.log("hello world")的东西。

Javascript 控制台(来源:作者,2022)

恭喜你,你刚刚做了 javascript。下一步是在你的.js文件中写一串可以被这个浏览器解释的行。就像 Python 一样,但是在浏览器中。

学习资源

我建议学习 NodeJs 。这就给了学习 Javascript 就像学习 Python 一样的体验。感觉就像使用 Python,但是使用了 Javascript 语法。NodeJS 给你做后端工程师或者前端的灵活性。

普通的 javascript(唯一的 Javascript,比如,基础的)也可以从 w3schools 学到。

https://www.w3schools.com/js/default.asp

如果你只关心简单的 web,那么香草 javascript 是你需要的。学会操作 DOM(文档对象模型)和显示数据。

一个伟大的介绍都是由火船简介

一个把所有东西放在一起的普通例子

我们知道如何进行空间数据分析,也就是使用 Python。创建一些为你做分析的脚本;指示做什么,替代鼠标和点击按钮的工作流程。我们将把原始数据转换成有用的见解,这些见解只需要被可视化,以便每个人都能理解。

可视化可以使用 Javascript 库来完成。地图是使用像 OpenLayers 或传单 JS 这样的库制作的。如果您在 Esri 环境中工作,ArcGIS online 也是另一个选择。现在你已经有了地图,我们只需要布局这个交互式地图。

这就是 HTML 的用武之地。使用 CSS(层叠样式表)来指定使用什么字体,或者东西应该如何着色,让它看起来更好。HTML 是我们制作的地图的包装,让每个人看起来都很好。

最后,把所有东西都部署到服务器上,或者只是静态压缩,然后用电子邮件发给每个人!

结论

Python 被用作分析和建模工具,Javascript 被用于使事物交互并呈现地图,HTML 被用于布局一切,将一切打包。两种语言(Javascript 和 Python)都可以用来开发全栈应用程序或任何与您喜欢的映射相关的东西!

一些笔记

本文提供了空间数据科学领域中事物如何工作的高级概念。技术细节是最重要的,因为一点点的错误都会导致整个项目的失败。例如,部署东西需要了解服务器如何工作,或者什么是 TCP/IP;用 Javascript/Python 创建模块;了解日志记录以减少错误;用 git 进行版本控制;更多!每个编码教程都有长达 17 小时的内容,这是有原因的,而且这只是代码。一切都需要工作,有些事情需要先工作;而本文所讨论的正是它后面的事情。

希望这篇文章足以作为那些想要追求空间数据科学路线的人的起点。

最好的是直接跳到一个项目,了解项目需要的背景;然而,一般的想法总是相同的。Python 用于分析,Javascript 用于呈现和交互,HTML 用于布局一切。

空间数据科学:交通规划的网络分析

原文:https://towardsdatascience.com/spatial-data-science-network-analysis-for-transportation-planning-7be048b8f37

来源:原创内容来自作者和里尼 A. (2022)

面向交通规划者的高级图论工具介绍(使用 Python)

介绍

交通/城市规划者进行交通分析。我想讨论这个分析,特别是在城市范围内。这条路线可行吗?最有效的公共交通路线是什么?这个地区交通便利吗?路线重叠吗?这类问题属于网络分析的范畴;在图论下。虽然图论本身对任何现实世界的现象都是不可知的,但我们可以用它来建模所有与网络相关的事物,包括城市交通分析。

社会学中的图论,马丁·格兰让—自己的作品,CC BY-SA 4.0,https://commons.wikimedia.org/w/index.php?curid=39967994

我从 2018 年(到现在 4 年)开始和网络分析打交道,用过一些工具。在本文中,我想与大家分享一下交通规划者如何开始使用 Python 工具进行网络分析。我希望读者都知道基本的图论知识,比如什么是节点和边,什么是图。

注意:这篇文章是苏坦穆夫提的空间数据科学系列的一部分,请查看这篇元文章以获得更多类似的文章!

计算机编程语言

对于像我这样没有编程背景的人来说,对于不需要非常低级编程技术的作品来说,Python 是一种非常棒的高级编程语言。在这种情况下,交通分析是我发现可以通过利用 Python 的功能进行优化的领域之一。这种优化需要工作流程的自动化,并使文件更符合字面意思;因此确保了再现性。它的语法哲学是人类友好和可读的;我们来对比一些语法。

在 Java 中,初学者的 hello world 程序如下:

public class Main {
  public static void main(String[] args) {
    System.out.println("Hello World");
  }
}

而在 Python 中,它如下

print('hello world')

对于像我这样不热衷于编程的人来说,Python 已经足够了。尽管计算机科学社区认为它很慢,但对于像我这样的交通/城市规划者来说,我觉得速度引发问题的可能性很低。

网络 x

网络 x

我不能强调这个软件包有多棒。这是一个用于图形/网络分析和构建的轻量级 python 包。它是用 C++编写的算法的接口,以其性能速度而闻名。再加上 Python 的人语法,我感觉在网络分析方面,Networkx 在语法和性能之间,两全其美。

我们直接一头扎进去吧!

(注:本文中我没有使用 QGISArcGIS图形用户界面 GIS 软件!让我们用 Python 来实现自动化,显式地,&缩放目的)

示范

一个用例:我下载了一个 OSM 网络数据并运行了一个geopandas绘图脚本。

import matplotlib.pyplot as plt;
import contextily as cx;
import geopandas as gpd;network_gdf = gpd.read_file('osm_network.shp');fig, ax = plt.subplots(figsize=(10,10));network_gdf.to_crs('epsg:3857').plot(ax=ax);
cx.add_basemap(ax,
               source=cx.providers.OpenStreetMap.HOT
              );

代码将导出如下所示的地图。

来源:作者(2022)

嗯,这个案子很复杂。让我们把它简化为主次干道。

来源:作者(2022)

完美!我们下一步应该做的只是清理工作,我认为这对于本文来说是微不足道的;我用草'v.clean搭配pyqgis检查该螺纹。这是一种质量控制措施,可确保网络拓扑结构的稳定性。

我们现在有了道路形状文件。

图形模型

我们现在有的只是一个普通的“shapefile”。为了对连通性和运输进行建模,需要将这些数据转换成图形模型。这就是networkx进入游戏的地方。

识别边缘

首先,确定每条道路的端点,并创建由这些端点组成的数据框。我使用pandas来管理表格数据框。下表是数据框片段。每一行反映了 shapefile 中的一条道路。使用来自id列的简单 SQL 连接,我可以识别 shapefile 中的几何图形。

+----+----------------------+----------------------+
| id |        start         |         stop         |
+----+----------------------+----------------------+
|  0 | (-0.13492, 51.5246)  | (-0.13499, 51.52457) |
|  1 | (-0.11953, 51.52866) | (-0.11956, 51.52871) |
|  2 | (-0.14517, 51.52391) | (-0.14513, 51.52375) |
|  3 | (-0.14423, 51.52399) | (-0.14473, 51.52398) |
|  4 | (-0.14398, 51.52287) | (-0.14415, 51.52325) |
+----+----------------------+----------------------+
note: the decimal place is reduced for presentation purposes

构建边的方法是我们以数据帧的形式拥有的 2 个节点。每一排都是一条边!使用简单的 Python 构建了一个networkx图形对象。生成的图形如下图所示。

道路网络的图形模型来源:作者(2022)

这是反映道路连通性的图形模型。我们可以对它进行各种图形分析算法,并获得关于我们的道路网络的见解。让我们通过执行两点之间的最短路径分析来演示它;

运输分析

本节演示了networkx在运输建模中的应用。

好吧,让我们做些有趣的事情!(:

最短路径

让我们试着找出最短的路径。假设我想确定(-0.13894,51.53115)和(-0.12162,51.53054)之间的路线,如下图所示。

来源:作者(2022)

然后,使用 Dijkstra 的算法,最短路径路线将如下图所示。

来源:作者(2022)

那么,它在图形模型中看起来如何?拓扑如下图所示。

图模型中的最短路径来源:作者(2022)

按比例放大

现在假设有 1,000,000 条记录或源和目标文件,给定一个网络数据集,如 OpenStreetMap 的。我们可以在算法中加入 for 循环,并遍历记录来确定最短路径。如果重量是一个变量,那么我们可以将其设置为算法中的成本变量。这都是因为 Python 才有的能力。

其他应用—等时线

我的一个个人应用是分析公共交通可达性。例如,从一个点出发,仅用 30 分钟步行+使用公共交通工具,你能到达多远的城市?我们可以从一个点创建一个等时线。幸运的是,有一个 networkx 算法可以解决这个问题!

公共交通

这对于评估公共交通网络和创建模拟非常有用。例如,下图显示了伦敦地铁网络。我们可以插入一个额外的网络来模拟它将如何影响整个网络!

伦敦地铁网络图来源:作者(2022)

结论

图论是城市交通分析的基础理论。Networkx 是一个利用 Python 编程执行图形分析的工具。它是一个用于扩展交通/网络分析的快速 API 一个让非硬核程序员(我认为对于城市/交通规划者来说刚好够用)执行交通分析的 shell。我个人认为这是进行运输分析的最佳和最容易使用的工具。你所需要的就是学习 Python 语法,理解 GIS,学习图论。这需要时间,但我觉得很有收获!

个人笔记

在空间和网络数据分析中,我经常尝试不使用 QGIS 或 ArcGIS,而是使用 Python 脚本将工作转移到pyqgisarcpy。这是为了扩大规模,并使项目可追踪。有了 Python 脚本,我总能明确地说出我做了什么。正如本文所展示的,我认为我已经非常清楚地表达了我的想法;本文所做的所有工作都是使用 Ms Visual Studio 代码和 Jupyter 扩展开发的!当然,探索数据的工具是 QGIS 和 ArcGIS,但是对于分析过程,我发现 Visual Studio 代码是完美的工具。

目前…(:

空间数据科学:使用 Git 的再现性和版本跟踪

原文:https://towardsdatascience.com/spatial-data-science-reproducibility-and-version-tracking-with-git-cd0b47d9ba52

作者和里尼(2022)

空间模型构建器和 Git:开发中的分析、跟踪和文档

简介—文档

文档是典型 GIS 工作流中的常见问题。点击哪些按钮?先用哪个工具,再用什么?我们的模式发生了什么变化?为什么有人做了什么事之后这就不管用了。更不用说软件版本和机器环境的差异了。当我们的工作必须对报告负责时,这变得非常必要。或者,在我们离开团队时进行交接,并培训接替我们的人。给定一组数据和环境,我们工作的可重复性是一个很大的保证。我们应该如何记录我们的空间数据过程?

在这篇文章中,我将讨论一些我们可以用来确保可重复性的工具。这些工具是模型构建器、git & Python 和 Notebook (Jupyter,以前称为 iPykernel)。如果你不做 python 或者任何编程,那么 model builder 是一个非常好的入门;一个非常简单的工具,说明设计过程。这个我先讨论一下。

注意:这个系列是空间数据科学的一部分:Sutan 的系列,请查看下面的元文章以获得更多空间数据科学文章

https://medium.com/rekayasa-data-spasial/spatial-data-science-the-series-81344a3ead29

模型生成器——制作空间模型的图形用户界面工具

如果你不使用编程语言,那么这是一个完美的工具。

如果我们将问题的范围限制在分析部分,那么我认为 model builder 是一个非常方便的解决方案。模型构建器是一个关于我们的空间数据过程的图形模型;我们拖放流程,并在流程上画箭头。因为它不需要用户了解任何编程语言,所以它对每个人来说都很容易使用并且非常容易理解。

让我们创建一个简单的模型:首先我们缓冲一个特征,然后我们用另一个特征裁剪缓冲结果。请看下图。让我们把它命名为简单 _ 模型。

带模型构建器的 QGIS 中的模型示例(作者,2022)

首先,输入进入缓冲区,缓冲过程的输出用另一个特征进行剪裁。当然,这非常简单,但是在实际项目中,这可能会变得复杂。这很有帮助,因为它非常直观地展现了正在发生的事情。然后,我们可以将模型保存到我们的目录中。

我创建的模型基本上是一堆文本文件。QGIS 只是渲染这些文本,这样我们就有了图形表示。下图显示了我们保存模型时的文本表示。嗯,它实际上是用 UTF-8 编码的,但我们现在不关心它。重要的是模型(来自模型构建器)基本上是一个文本文件。请注意,这种文本表示对于我将在下面讨论的版本跟踪非常重要。

名为 simple_model 的已保存模型的“实际”外观(作者,2022)

开发模型—添加变更

如果我们做了改变,那么这些文字也会改变。一些新的行将被创建或删除,这取决于我们做什么。这就带来了识别什么已经改变以及何时改变的问题。因此,跟踪什么发生了变化是一个明智的措施,这可以用 git 来完成。

版本跟踪— Git

现在我更想开发这个模型,并添加一些功能,因为我收到了一些反馈。我需要改变模型,这需要改变模型的文本文件。比方说,该模型显然将用于识别脆弱城市中的河流洪水危险区,我需要完成更多的流程。因此,我对模型做了一些修改。我做的一些改变:

  • 更改输入和输出属性名称;从默认名称input_geometryclip_featurethe_riverthe_city;诸如此类。
  • 添加difference叠加过程来消除琐碎的区域。

我们模型中的附加过程(作者,2022)

我添加了这两个改动,但是我的队友怎么知道我改了什么?此外,如果我的团队成员对模型做了其他事情,我怎么知道他做了什么?一封电子邮件就足够了,但它不如 git 有效。

Git 是一个命令行工具,用来控制我们代码中的变化。由于它是一个命令行工具,我们将使用终端或命令提示符。我们键入命令,然后单击 enter 执行。不要害怕,如果我们给一点时间,它不是那么复杂!基本命令是

  • git log显示更改的内容、更改者和时间
  • git show显示所做的实际更改
  • git add添加已更改但尚未提交的文件
  • 正式提交更改!git 记录这个变化。

更改日志

每次我在模型中做小的改动,我都会用 git 创建一个日志。在这里,如果我执行git log,它将显示我对来自我的消息/便笺做了什么。下图显示了我已经完成的 3 次提交。您可以看到是谁做的,什么时候做的,以及消息。

记录发生了哪些变化(作者,2022)

显示更改的内容

如果我想看到一个特定的提交/更改,我可以执行git show,后跟提交 id 的第一个字符,例如git show 75fb8。看下图。

展示改变了什么(作者,2022)

红线是删除的行,绿线是新的/插入的行。这里我可以告诉大家,我把input_vector改成了the_river,把clip_feature改成了the_city。谁干的?git 日志显示是我;但是如果我们和队友一起工作,我们可以看到其他人可能会改变这一点。

还原更改

比方说,经过几次反馈后,我们反对琐碎的区域差分程序;所以我们必须重新做我们的改变!这很烦人,但我们如何后退一步?我们使用git revert来提交。让我们恢复我们的提交 id fac543并后退一步。

恢复更改(作者,2022)

现在,您可以看到使用提交 id 0c018b6完成了恢复,消息描述了恢复操作及其恢复的提交 id。当我看到发生了什么变化时,这个模型又回到了以前的版本!

分支和协作

开发模型通常需要迭代和模块化特性。这些功能是模块化的,可以由其他团队成员开发。有了 git,我们可以同时一起处理相同的文件,进行修改,并跟踪每个人都做了什么。这是通过分支实现的;从初始模型创建分支;最重要的是,将这些分支合并成一个具有丰富功能的单一模型。

我们可以创建代表相同模型的分支,允许在这些分支上进行相应的更改。每个团队成员将并行处理他们的分支。当团队成员完成他们的工作时,我们将分支合并到一个模型中,每个团队成员都开发了这个模型的特性。

下图显示了如何制作分支并将其合并回初始模型。它只是一个名为“somebranch”的单独分支;我在那里开发了一些模型,然后将其合并回初始模型。*代表我所做的一个提交,后面是提交 id。

git 中的分支(作者,2022)

如果我们已经开发了一个稳定的模型,那么我们可以创建一个稳定版本的分支来保持它。稳定的分支不会被编辑,只是保留下来,让你知道它工作得很好。当一个开发阶段再次开始时,从稳定分支中创建另一个分支,然后重复。

结论

这就对了。使用模型构建器可以实现分析的自动化和形式化。模型构建器创建算法的直观图形表示,确保再现性。git 可以跟踪开发中的变化。Git 还允许协同工作环境和控制模型的版本。

这样,模型的文档可以随着模型开发的时间一起进行。更改可以并行进行,并且所有更改都会被记录。

不足之处

  • git 和模型构建器相当复杂。建立直觉并使之自然需要数月的练习。你需要投入时间去真正享受它,一开始并不好玩。我认为一旦你习惯了,它就会有回报。
  • 在传统的典型 GIS 工作流中,单击按钮并保存项目文件,使用 git 是不合适的。项目文件,如。qgz 或者。mxd 包含布局配置、层顺序等琐碎信息。这对于结果信息来说是不重要的。
  • 为了更复杂的方法,我们牺牲了点击按钮的便利性。通常要花更多的时间来写东西并使之正式化。虽然 word 文档可能足够方便,但它不一定是可复制的。在这里,有了模型和 git,我们甚至可以使用 Docker 来形式化模型,并确保机器的环境需要理解 linux OS 和 bash 脚本!但是,什么是码头工人和巴什,我说的对吗?另一篇文章的故事。

最终,它的价值是要付出代价的。它的价值取决于每个团队;但是大多数时候,我发现这样做是值得的,尤其是当事情需要谨慎和复杂的时候。有这样的保证肯定感觉很好。

注意

对于那些熟悉编程尤其是 python 的人,我最初想把 Jupyter Notebook 和 Python 脚本与pyqgisarcpyshapely放在一起。然而,在我重复了这篇文章之后,对于一篇单独的文章来说,这似乎是一个太多的主题了;此外,它与那些不写剧本的人没有关系。我认为 Git 已经够可怕的了,因为它已经使用了终端,但是我希望这篇文章可以作为那些想要解决文档问题的人的入门和第一个参考。我呼吁人们更多地使用终端。

因此,下一篇文章将讨论笔记本和脚本如何改进这些文档过程。这更有趣,也是我一直做的事情。我很少在 ArcGIS / QGIS 中进行空间分析,因为我无法有效地控制文档。

空间数据科学:空间数据存储

原文:https://towardsdatascience.com/spatial-data-science-spatial-data-storage-364a8df4038c

来源:作者&里尼,2022 年

可选空间数据格式介绍;没有 shapefile!

空间数据科学家使用空间格式的空间数据。通常,这种格式是由 ESRI 在 90 年代开发的“shapefile”格式。我希望本文的读者在继续本文的其余部分之前熟悉“shapefile ”,因为我将向您介绍超越 shapefile 工作流。如果您对 shapefile 感到满意,这是一个很好的开始。然而,随着项目变得越来越复杂,您将处理更复杂的数据,如.osm.pbf文件,或 JSON (JavaScript 对象符号)。那些是什么?为什么 shapefile 不够用?

Shapefile 的限制

大型项目需要灵活性和稳定性,以减少人为错误。我认为,Shapefile 作为一种老技术,对普遍的现代挑战提出了几个限制:

  • Shapefile 只允许最大 2 GB 的数据,而有时我们需要处理更多这样的数据
  • 列名只允许一定数量的字符。你必须明智地命名你的专栏!我还是觉得.dbf很烂。
  • 天生凌乱。核心文件是.shp,但是如果没有.dbf.prj和更多的补充,它就一文不值了!如果您想要重命名 shapefile,该怎么办?您必须对它们进行重命名,或者打开桌面 GIS 来为您进行重命名。或者,用花里胡哨的方式,在命令行界面使用 GDAL/ogr2ogr。

虽然 shapefile 广为人知,很突出,这很好,但我真的不喜欢 shapefile。然而,我必须说,用它工作很舒服!所以我认为恰当的说法是,

我讨厌我爱 shapefile…

但是外面还有什么呢?让我们探索一些替代方案…

在这篇文章中

本文向您介绍了各种格式和数据库管理,以支持各种处理空间数据的方法。最后,根据项目的独特环境,您将有多种选择。

存储空间数据

存储空间数据的标准方法是遵循 OGC 标准。对于点、多边形、折线等简单的矢量数据,标准应该足够了。空间数据可以以众所周知的文本(WKT)格式存储。;或其替代的著名二进制(WKB ),但它看起来很丑,因为它是二进制的。微软有一篇很棒的文章解释了什么是 WKT。

# example of storing spatial data in WKT formatPOINT(-122.349 47.651)LINESTRING(-122.360 47.656, -122.343 47.656)POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))

一个简单的矢量空间要素仅仅由点和它是什么的定义(它是一个点吗?多边形?还是线?).因此,WKT 是空间数据的一种表现形式。考虑到这一点,让我们来看看格式。

空间数据格式

本节讨论存储空间数据的各种格式,作为 shapefile 的替代方案。如果我们打开 QGIS 导出选项(下图),会有很多选项。我认为没有必要全部都学,下面的检查是最有可能用到的。

QGis 导出选项

CSV —逗号分隔值

逗号分隔值实际上是一个文本文件。可以在 excel 中打开 CSV。让我给你举个例子:

CSV 格式的空间数据(来源:作者,2022)

我认为这是相当直截了当的,文本本身就说明了问题。几何信息以 WKT 格式存储,作为一个人,我们可以阅读它,但不是真的。您可以在 Ms Excel 中打开此 CSV 文件,编辑属性并将其返回到桌面 GIS。够搞笑的,还可以用 Ms Words 打开编辑。

geo JSON-Javascript 对象符号(JSON)

JSON 是 web 或应用程序相互通信的一种格式。只是一个文本文件,像 csv...然而,以不同的形式。它由“键”和“值”对组成;当查询“键”时,返回“值”。文字看起来像下图的右边。

geojson.io(来源:作者,2022 年)

为了理解 JSON 文本,我建议阅读更多关于数据结构主题的内容。这本书挺受欢迎的(数据结构&算法)。试着理解什么是数组、字典、索引等等。一旦掌握了这一点,JSON 和 GeoJSON 就更有意义了。

当处理在线/网站数据时,您经常会收到这类数据。我 80%的时间都在使用 Python GIS,当我看到一个以 GeoJSON 格式提供服务的网站时,我的心中燃起了一丝喜悦。这意味着我可以创建应用程序,检索 GeoJSON,并让我的应用程序无缝、干净地处理它。

你可以去 geojson.io 画一些线或者多边形,了解一下。

因为它是基于文本的,所以您可以打开。GeoJSON 用 Ms Word 照我说!或者,再次,文本编辑器。请确保分机保持不变。GeoJSON 保留其格式。

微软 Word 中的 Geojson(来源:作者,2022)

地理包-文件地理数据库

GeoPackage 基本上是一个包含“shapefiles”的“文件夹”。标记引号是因为它们的行为类似于 folder 和 shapefile,但是它们背后的技术本质是不同的。我们称空间数据为“特征”,而不是“形状文件”。您可以在 QGIS 或 ArcGIS 中浏览地理包“文件夹”。ESRI 也开发了一种类似的格式,称为地理数据库(。广发)。它在 ESRI ArcGIS 环境中进行了优化。

地理数据库格式(来源:作者,2022 年)

PostGIS —数据库管理器

Postgis 是 postgresql(关系数据库管理器,RDBMS)的扩展。嗯,它本身并不是一种格式,但是为了简单起见,我们可以这样看。首先,我们需要安装 PostgreSQL 并创建一个数据库。然后,我们将 postgis 扩展到数据库,以支持地理空间处理功能。我们的数据库现在是“邮政地理信息系统”!

PostgreSQL 中的 Postgis 几何处理(来源:作者,2022)

我认为 Postgis 是最佳的地理空间数据格式。这是因为您可以执行 SQL 并只选择您需要的数据,而无需加载全部数据并对其进行过滤。除此之外,当您使用 geodjango 构建 web 应用程序时,您将使用 postgis。如此美丽。

我通常使用 QGIS 作为代理来管理 postgis 数据库,或者使用 PgAdmin。您可以使用工具“导出到 postgres”或“导出到 postgis”(取决于您的 QGIS 版本)将数据加载到 postgis,反之亦然。

Qgis 中的 Postgis(来源:作者,2022)

结论

现在,我已经向您介绍了一些替代数据格式,而不是常见的 shapefile 格式。这 5 种格式是。csv,。乔森还有。gpkg(地理包)或。gdb(地理数据库)和 PostGIS。我个人认为有趣的是,我们可以在 Ms Word 中写一堆单词,并根据它们创建一个地图,只要我们能够精确定位数据的精确坐标。如果 excel 女士可以处理多边形数据,我们当然可以创建地图;我们可以,但只能用点和线数据。当然,有一点需要注意:UTF 8 编码的文本文件格式效率非常低;另一方面,我发现 shapefiles 非常快。然而,这些优点并没有消失。

就个人而言,在从事 GIS 工作 4 年之后,我并不那么喜欢 shapefile。它在 90 年代有过辉煌的日子。现在,我更喜欢将我的数据存储在 PostGIS、CSV(尤其是当它是点数据时)或 geojson 中,尽管在性能方面有点低效。处理 CSV 和 WKT 数据非常方便,因为像 geopandasqgis 这样的工具已经提供了快速处理这些数据的命令。

在下一篇文章中,我将讨论使用 PostGIS 管理空间数据库。这是非常奇妙和令人兴奋的!

空间数据科学:空间查询

原文:https://towardsdatascience.com/spatial-data-science-spatial-queries-8d6709fd9747

里尼和作者(2022)

用 SQL 回答每个“在哪里”的问题;举例说明

介绍

我们通常通过地理过程(如 ArcGIS 和 Qgis 中的缓冲区、联合和相交)逐个过程地进行适宜性分析。单击一个工具按钮,创建一个输出,并将其用作其他流程的输入。这导致在你的缓存文件夹中有许多暂存文件,从而产生了记住使用哪些文件的奇迹。对于像这样简单的适宜性分析,从技术上讲,我们可以在 ArcGIS 和 Qgis 中使用模型构建器,但更好的方法是以更易于计算机解析的方式来表达我们的适宜性请求。

适宜性分析只是我们对空间数据集提出的一组要求,例如:

我想寻找土地开发成农场,所以我们有这样一套说明:

  • 获取非保护区和可耕地之间的交集
  • 从交叉点,查询所有现有的空地土地使用
  • 从那些确定的空地中,过滤掉那些不在 1 公里灌溉范围内的空地,以保证农场的用水

有一种方法可以翻译这些指令,只需单击一次“enter”就可以从我们所有的数据集中进行查询。

SQL —结构化查询语言

对于本文上下文,结构化查询语言(SQL)是一种将查询数据参数表达成数据库管理员能够理解的指令的语言。我们可以做得更多,但就目前而言,这个解释就足够了。它是一个指令;请求数据库返回我们想要的数据。相信大家都已经知道什么是数据库了。

SQL 看起来怎么样?它看起来像这样:

SELECT * FROM land_use

这意味着“从 land_use shapefile 中选择所有列”。不是 shapefile 本身,而是来自land_use表(包含地理信息)。另一个例子:

SELECT name, length, geometry FROM irrigation WHERE length < 2000

这意味着“返回长度小于 2000 的灌溉线,但只有列名、长度及其几何图形”。这省略了除名称、长度和几何图形之外的其他列,因为灌溉表可能包含许多不必要的列,如类型、开发者等等。返回太多的列会导致不必要的带宽和大量计算资源的消耗。

空间表中的 SQL 示例(来源:作者,2022)

这个想法

我们的想法是制定查询指令,使其返回满足我们查询的空间数据。这意味着我们字面上键入指令;使未来的分析师能够审计我们的流程并复制它。这也有助于我们跟踪我们一直在做的事情。看看 PostGIS 文档中的一些空间查询示例。

空间查询的一些示例(从 where 开始):

  • 我应该住在哪里?
  • 我应该在哪里开我的新店?
  • 工业区旁边的住宅区在哪里?
  • 我的办公室应该搬迁到广告中的物业吗?
  • 濒危动物在哪里?
  • 如果我建造一条高速铁路,这会对该地区的生物多样性产生什么影响?

空间查询

当我们在 SQL 脚本中引入空间查询函数时,空间查询就开始了。这些功能因软件而异,但我认为最流行的是带有 PostGIS 扩展的 Postgresql,或者我们称之为(只是)Postgis。我将在本文末尾讨论 PostgreSQL。其他软件也提供这种能力;例如使用 Google BigQuery 或 Microsoft SQL Server。

以下脚本是 postgis 中 SQL 的官方示例。

WITH city AS 
(SELECT 'Gotham' AS name,  ST_Buffer(ST_Point(0,0), 10) AS geom),superhero(name,geom) AS (VALUES
('Bat Boy', ST_Point(0.1,0)), 
('Bat Girl', ST_Point(1,1)))SELECT superhero.name FROM city INNER JOIN superhero ON ST_Contains(city.geom, superhero.geom);

你可以在这个官方参考文档中看到所有的函数和操作符。像 buffer、union 和 clip 这样的常用工具就在那里!

让我们开始行动吧

案例:使用空间 SQL 识别濒危住区

假设有一个城市很容易发生洪水之类的自然灾害,但我们不知道哪个居民区处于危险之中。我们需要为这些市民制定缓解措施,但我们不知道为谁。

我们有土地利用数据,还有洪水危险区(红色散列多边形)。当我们叠加数据时,就产生了这张地图。

案例数据(来源:作者,2022 年)

这对我们来说很容易理解,但在现实生活中,这可能是一个地区或一个省的大小;大到我们的大脑无法理解。

这里我们可以看到,定居点和危险区之间有重叠;我们如何获得那块区域?换句话说,哪些地方是容易发生洪水的聚居地?

普通的方式

常见的方法是

  • 根据土地使用数据的属性进行选择,其中土地使用等于“定居点”(顺便说一下,这实际上是 SQL)
  • 选择仍处于活动状态时,打开裁剪/相交工具
  • 将所选土地利用设置为输入,将危险区域设置为相交要素
  • 执行交集,并将结果保存在内存或某个目录中

现在让我们用空间 SQL 来做这件事

更好的空间 SQL

使用空间 SQL 函数创建 SQL 脚本:ST_Intersection()如下

select **ST_Intersection**(danger_zone.geom, landuse_demo.geom) as geom FROM danger_zone , landuse_demo where landuse_demo.landuse = settlements

并且结果如下层(回答危险区的居民点在哪里?).

(来源:作者,2022)

看起来更复杂,为什么要这么麻烦呢?

有几个主要原因:

  • 速度和性能:更快地完成工作,加载您不需要的所有数据!
  • 再现性和责任性:记录和了解你所做的事情
  • 多功能性:灵活,可与其他应用程序互操作。
  • 问责:审计脚本更容易,因为它是流程的文字形式,而不是流程图。

这是复杂性和灵活性之间的权衡,但如果以上几点是您正在寻找的,这是一个选项。

让我们进一步讨论原因

速度和性能

每次执行 SQL 时,我都会生成一个地图。因此,转换标准来制作地图将会非常快

现在,我想将标准从定居点切换到工业区,我可以像这样编辑脚本的结尾

select ST_Intersection(danger_zone.geom, landuse_demo.geom) as geom FROM danger_zone , landuse_demo where landuse_demo.landuse = **pastures**

请注意,我将最后的landuse参数从 settlement 切换到 industrial。结果现在是这样的

(来源:作者,2022)

或者像这样切换到industrial

select ST_Intersection(danger_zone.geom, landuse_demo.geom) as geom FROM danger_zone , landuse_demo where landuse_demo.landuse = **industrial**

(来源:作者,2022)

这只是掌握空间 SQL 概念的一个简单例子。在自动化至关重要的开发环境中,这种脚本是一种提高性能的技术。我甚至没有使用 QGIS 或 ArcGIS 来制作您看到的地图。所有这些都可以通过一个显示器和一个键盘来完成(),前提是你在 PostGIS 服务器上有高质量的数据,但是,嘿,这是另一篇文章的不同故事 )

Geopandas 中的 Postgis SQL,(来源:作者,2022)

执行 SQL 会在不加载整个数据集的情况下生成图层。你不需要加载 4 GB 的 shapefile 然后过滤它;只要输入你想看的东西,图层就在你的画布上了。你做错什么了吗?只需更改查询脚本。

想象一下用老方法做,你必须再次点击工具,当事情变得复杂时,你会迷失方向。当然,我们可以用模型构建器来实现这一点;它是这类脚本的图形化表示。当然,如果这对你有用,这也没什么不好;但是很多时候,一旦我学会了如何写脚本,我就不喜欢点击按钮了。

我们还将处理任务切换到服务器,而不是我们的机器。当数据复杂且庞大,但我们没有一台非常强大的计算机时,这非常有用;虽然这意味着我们需要一个更可靠的服务器。但是,嘿,这就是为什么我们有云计算!

多功能性、可复制性和责任性

因此,空间 SQL 实现了通用性和可再现性。我可以把这个脚本给我的同事,他们执行的时候也会有同样的结果。同事们可以看到我们在做什么,并在 git 工作流环境中协作编辑脚本。从我个人的经验来看,一旦你熟悉了 SQL 和脚本环境,你会变得非常快,对你的分析更加负责。当事情出错时,我们可以追溯我们做了什么,只需读取我们执行的指令。

它是通用的,因为我们可以复制 SQL 脚本来开发许多应用程序。SQL 是通用语言;我们可以在 javascript web-gis 应用程序上传递它,比如传单/OpenLayers,或者作为服务器端的视图传递它。与在 GUI 桌面工作流环境中点击按钮或步骤的模糊指示相比。对于桌面环境,您只能在软件、操作系统和软件版本中实现。从 ArcGIS 切换到 QGIS,说明会有所不同;但不是用 SQL 脚本,会是一样的!

将查询加载到 GIS 图层

在 QGIS 中执行 SQL 并以图层形式查看结果是可能的。如果你正在使用 Qgis 3.2 以上,有一个工具可以做到这一点。该工具的名称是使用 PostgreSQL 作为数据库引擎来执行和加载 SQL。

QGIS 的 SQL 提示截图(来源:作者,2022)

我们可以键入我们的 SQL 指令,工具会返回我们所请求的内容。

顺便说一下,我们一直在用 PostgreSQL 和 postgis 讨论 SQL 但是什么是 PostgreSQL 呢?

PostgreSQL 和 Postgis — RDBMS

PostgreSQL 是一个数据库管理器。像 ArcGIS 中的 ArcCatalog 或 Qgis 中的浏览器一样查看它。它旨在用于一般的数据库目的,但通过其扩展 Postgis,我们可以从引擎中获得空间数据功能。如果我们开发一个 web-gis 应用程序,通常的做法是使用 PostgreSQL 作为存储数据的数据库引擎。出于 GIS 的目的,人们通常只说 PostGIS 而不是 PostgreSQL,尽管 Postgis 并不是真正的引擎。只是更朗朗上口,更优雅!

"您是否将空间数据保存在 shapefile 或地理数据库中?"

“不,我将它存储在我的 PostGIS 服务器中”(:

使用 PgAdmin for PostgreSQL 管理空间数据(来源:作者,2022)

结论

SQL 是我们构造查询的方式,这样计算机就能理解我们的问题,从而给出答案。SQL 本身并不是 GIS 的专属。它是数据科学社区中使用的通用语言,而空间数据恰好是包含地理信息(如坐标和高程)的普通数据。Postgis 允许我们在 SQL 中考虑空间数据之间的交互。

然而,欣赏空间交互性需要一般数据科学中的一个额外步骤,正如我提到的,通常是空间不可知的。这种交互性可通过 Postgis 使用空间 SQL 进行查询,从而确保再现性和分析保证。这是一种对空间数据建模的方法和处理空间数据的技术。

空间数据科学:SQL 空间连接

原文:https://towardsdatascience.com/spatial-data-science-sql-join-spatially-ecd2f7400753

(来源:里尼和作者,2022)

如果这些表有空间关系,则连接它们。额外收获:我在 Excel 中做

介绍

结构化查询语言 SQL 是一种从数据库中获取表格/数据的通用语言。最简单的语法是select * from some_table,它将返回来自some_table的所有行和列。还有一个是select column1, column2 from some_table只返回column1column2;and select * from some_table limit 10返回所有列,但只返回 10 行。我们向数据库询问一些事情,数据库会回答。SQL 很重要,因为我们可能使用超过 2,000,000 行的表,如果我们只需要其中的一部分,我们不必加载所有的 2,000,000 行。

我们很少只使用一个表,我们经常使用多个相互连接的表。vlookup是我们在 Ms. Excel 中的做法,或者在我看来,更高级的版本:xlookup。一个更好的方法是使用 power-pivot 来设计连接,这样你就可以图形化地看到连接。

SQL 使我们能够在一个脚本中做到这一点,例如,观察下图。假设我有一个表superhero,它是publisher;我想把这些桌子连接起来。最后一行(第 17 行)是连接superheropublisher的查询语法。从表publisher中查看comic列如何连接到name列。

注意:由于突出的 postgis 扩展,我使用 postgresql 作为我的 RDBMS。下图由 PgAdmin 4 制作,GUI 客户端操作 postgresql 数据库。

SQL 连接示例(来源:作者,2022)

加入

join命令只是一个vlookup。这是一种结合两张桌子的方法;一种连接两个表的方法。这种连接可以通过链接两个有关系的列(名为主键和外键)来实现。可以进行连接,因为两个表的列之间存在关系。

如果这两列是几何列,它是空间数据框架的一个组成部分,则可以建立空间关系。

首先,什么是空间数据框架?

空间数据框架-简短介绍

空间数据帧只是一个普通的数据帧,增加了一个几何列。该几何列包含每一行/特征的几何,它定义了形状。

在 postgresql 中,安装 postgis 扩展模块时会启用空间数据框架功能。一旦安装完毕,我们就可以访问空间数据函数和构造函数了。我们来构造一些数据:citizenscity

https://postgis.net/

构建样本数据

比方说,我们有两个空间数据帧citizenscity。每个数据帧都有一个名为geomgeometry列。geom包含表示空间信息的 WKT 数据;但是在 Python 中这应该包含shapely点对象。数据帧看起来像这样。

样本数据框架(来源:作者,2022)

这些表是在 postgresql 查询工具中使用具有以下语法的 SQL 构建的。

-- producing city and citizens spatial dataframe
with city(city,geom) AS 
(
VALUES
('jakarta', ST_GeomFromText('POLYGON((0 0,0 1, 1 1,1 0,0 0))')), 
('bandung', ST_GeomFromText('POLYGON((2 0,3 0, 3 1, 2 1, 2 0))'))
),
citizens(name,geom) AS 
(
    VALUES
      ('john', ST_Point(0.5, 0.7))
    , ('jane', ST_Point(2.5, 0.9))
    , ('adi', ST_Point(0.3, 0.1))
    , ('budi', ST_Point(2.5, 0.5))
    , ('siti', ST_Point(2.1, 0.5))
)

当我们使用matplotlib或仅仅使用 QGIS 来可视化geom列时,它看起来像这样。

样本数据(来源:作者,2022 年)

我们看到红点(citizens)位于蓝框(city)内。geom列有关系;这意味着我们可以连接两个表。我们知道每个公民都生活在一个城市里。哪个住哪个?我们可以知道 john 住在雅加达,但是我们如何连接该表呢?我们可以告诉你的是citizens点和city多边形重叠,这是一种空间关系。这是空间连接进来。

空间关系

拓扑是表达空间关系的术语。在 PostGIS 中,通过应用空间函数来定义关系。请查看以下参考资料,了解可用的空间关系(来源:第 8 章。PostGIS 参考

  • ST _ 3D intersects-如果两个几何在 3D 空间相交,则返回 true 仅适用于点、线串、多边形、多面体表面(区域)。
  • ST_Contains —如果 B 的任何点都不在 A 的外部,并且 A 和 B 至少有一个共同的内部点,则返回 true。
  • ST _ contains proper—如果 B 与 A 的内部相交,但不与边界或外部相交,则返回 true。
  • ST_CoveredBy —如果 A 中没有点在 B 之外,则返回 true
  • ST_Covers —如果 B 中没有点在 A 之外,则返回 true
  • ST _ Crosses-如果两个几何图形有一些(但不是全部)共同的内部点,则返回 true。
  • ST _ LineCrossingDirection—返回一个数字,指示两个线串的交叉行为。
  • ST _ Disjoint-如果两个几何图形不相交(它们没有共同点),则返回 true。
  • ST _ Equals-如果两个几何图形包含相同的点集,则返回 true。
  • ST _ Intersects-如果两个几何相交(它们至少有一个公共点),则返回 true。
  • ST _ ordering equals-如果两个几何图形表示相同的几何图形并且具有相同方向顺序的点,则返回 true。
  • ST _ Overlaps-如果两个几何图形相交并具有相同的尺寸,但彼此不完全包含,则返回 true。
  • ST _ Relate-测试两个几何图形是否有匹配交集矩阵模式的拓扑关系,或计算它们的交集矩阵
  • ST_RelateMatch —测试 DE-9IM 交集矩阵是否匹配交集矩阵模式
  • ST _ Touches-如果两个几何图形至少有一个公共点,但它们的内部不相交,则返回 true。
  • ST_Within —如果 A 的任何点都不在 B 的外部,并且 A 和 B 至少有一个公共的内部点,则返回 true。

如果我们有一个名为a的空间数据帧和另一个名为b的空间数据帧,另一个例子如下图所示。将每个形状(线、多边形或点)视为数据帧中的一行。数据帧中的一行也称为特征。

空间拓扑学——由克劳斯——自己的作品,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=21299138

您可以在 PostGIS 的空间关系部分了解有关空间关系的更多信息。

http://postgis.net/workshops/postgis-intro/spatial_relationships.html

空间连接 SQL 空间连接

在我们的citycitizens的例子中,我们想要识别每个公民居住的城市。空间关系可以是包含、相交、重叠等。对于这些上下文,我们可以选择其中一个函数;我用ST_Contains函数来定义这种关系。ST_Intersects也管用。

.
.
.
-- 
SELECT name, city
FROM citizens
JOIN city
ON ST_Contains(city.geom, citizens.geom)

该查询基于空间关系返回查询表。

样本连接(来源:作者,2022)

Microsoft Excel:超级查询

显然在 Excel 中可以做到这一点,请看下表。

excel 演示(来源:作者,2022)

这是通过连接 Postgresql 和 Excel 的 PowerQuery 完成的。默认情况下,它会转到 SQL Server,如果您想在 SQL Server 中执行空间 SQL,这非常方便。无论如何,在 Postgresql 中,Postgis 扩展允许我们使用空间功能来执行 sql。因此,本质上,excel 中的 GIS 分析,但没有可视化。

PowerQuery 高级编辑器(来源:作者,2022)

我将在下一篇文章中详细讨论 excel 中的空间分析。

结论

vlookupexcel 中的函数可以在两个表上完成,因为这两个表包含有关系的列,但是如果这两个列包含空间数据呢?这种关系仍然存在,但它变成了一种空间关系;该关系可以通过空间 SQL 中的关系函数来定义。一旦我们定义了这种关系,我们就可以连接两个表。

个人笔记

空间连接的常用方法是使用 QGIS 或 ArcGIS 中的空间连接工具。我认为这会产生转储中间文件,从而使过程效率低下。使用 postgresql 和 postgis 等 RDBMS,我们实际上不需要 QGIS 或 ArcGIS 来进行空间连接。postgis 下面是 GEOS 和 GDAL,其中是大多数 gis 软件的几何引擎。所以,SQL 只是调用操作 geo 的函数的另一种方法。我更喜欢用 postgis 来做,因为处理工作转移到了服务器上,而不是主计算机上。

有趣的是,空间分析功能在 Excel 中也是可能的!如果只有 Ms Excel 具有制作多边形图形的能力,我们基本上可以在 Excel 中制作地图。我是在完成了本文中的空间连接之后才知道这一点的。

关于空间数据科学系列

本文是我创作的空间数据科学系列的一部分。查看元文章。

https://perkotaan.medium.com/spatial-data-science-the-series-81344a3ead29

我的 github 账号里有一些算法。查看一些公共存储库!

https://github.com/sutanmufti

使用虚构数据的空间回归

原文:https://towardsdatascience.com/spatial-regression-using-fabricated-data-bbdb35da4851

R 中建模的另一个维度

关于空间回归建模、处理地理数据以及根据位置坐标制作精美的数据图,已经有很多帖子可用。对我来说,我无意发表关于空间回归的文章,实际上我正忙于探索使用制作包轻松模拟分层数据的方法。这让我想到了一个包含空间数据的示例,并进一步探索了处理包含空间协方差矩阵的数据的方法。所以,我在这里,写下了它。

这不是一个全面的帖子,但它确实展示了空间回归的一些关键元素。以及如果数据实际上不包含任何东西,为什么花哨的技术没有任何作用。让我们一起来看看和探索吧!

library(fabricatr)
library(lattice)
library(sf)
library(mapview)
library(data.table)
library(tidyverse)
library(sp)
library(gstat)
library(geoR)
library(viridis)
library(ggExtra)
library(grid)
library(gridExtra)
library(GGally)
library(MASS)
library(mgcv)
library(tidymv)
library(sjPlot)
library(lme4)
library(wakefield)
library(simsurv)
library(forecast)simulated_quake_data <- fabricate(
  data = quakes,
  fatalities = round(pmax(0, rnorm(N, mean = mag)) * 100),
  insurance_cost = fatalities * runif(N, 1000000, 2000000))
head(simulated_quake_data)
simulated_quake_data<-simulated_quake_data%>%
  group_by(lat, long)%>%
  mutate(mag=mean(mag),
         fatalities=mean(fatalities),
         insurance_cost=mean(insurance_cost))%>%
  distinct(., lat,long, .keep_all= TRUE)
table(duplicated(simulated_quake_data[,c(1,2)]))

所以,把我带到现在这个位置的数据集就是地震数据集,你可以用制作包来扩充它。看起来是这样的:

现在,没有一个空间模型喜欢重复的位置数据,所以你需要去掉它。

然后是一些精美的图形,显示数据实际上是从哪里来的。它清楚地显示了斐济地区的地震。糟糕的是数据没有时间成分。这将使建模变得更加有趣。

mapview(simulated_quake_data, 
        xcol = "long", 
        ycol = "lat", 
        crs = 4269, 
        grid = FALSE)

不错的叠加数据。图片作者。

sbux_sf <- st_as_sf(simulated_quake_data, 
                    coords = c("long", "lat"),  
                    crs = 4326)
plot(st_geometry(sbux_sf))

相同的图形,但没有覆盖。图片作者。

sqbin <- ggplot() +
  geom_bin2d(
    data=as.data.frame(st_coordinates(sbux_sf)),
    aes(x=X, y=X))+theme_bw()
hexbin <- ggplot() +
  geom_hex(
    data=as.data.frame(st_coordinates(sbux_sf)),
    aes(x=X, y=Y)) +
  scale_fill_continuous(type = "viridis")+theme_bw()
grid.arrange(sqbin, hexbin, ncol=2)
ggplot(sbux_sf, aes(x=mag))+
  geom_histogram(bins=50, aes(y=..density..))+
  geom_density(fill="#FF6666", alpha=0.5, colour="#FF6666")+
  theme_bw()

数据被分类,用六边形来观察大多数地震发生的地方。图片作者。

数量的密度。图片作者。

ggplot(data = sbux_sf) +
  stat_density2d_filled(
    data = as.data.frame(st_coordinates(sbux_sf)),
    aes(x = X, y = Y, alpha = ..level..),
    n = 16) + 
  scale_color_viridis_c() +
  theme_bw() +
  labs(x="Longitude", y="Latitude")+
  geom_sf(alpha=0)+
  theme(legend.position = "none")

另一种方法是根据纬度和经度绘制数据。在创建图表的过程中似乎出现了一些问题,因为我没有得到坐标轴上的所有数据。图片作者。

在这里,另一种方法是连接数据上的点,并使用纬度和经度坐标来建立几何点和模型,预测和绘制数据。使用的模型应用了反距离加权,尽管也可以使用许多其他方法,例如克里金法

sd.grid <- sbux_sf %>%
  st_bbox() %>%
  st_as_sfc() %>%
  st_make_grid(
    n = 100,
    what = "centers"
  ) %>%
  st_as_sf() %>%
  cbind(., st_coordinates(.))
idw.hp <- idw(
  mag ~ 1, 
  locations = sbux_sf, 
  newdata=sd.grid,
  nmax = 150)
idw.hp = idw.hp %>%
  cbind(st_coordinates(.))
g1<-ggplot(idw.hp, aes(x = X, y = Y, fill = var1.pred)) +
  geom_raster() +theme_bw()+theme(legend.position = "none")
g2<-ggplot(idw.hp, aes(x = X, y = Y, fill = var1.pred)) +
  geom_raster() +
  scale_fill_viridis_b() +
  theme_void() +
  geom_sf(alpha=0)+theme(legend.position = "none")
grid.arrange(g1,g2)

不错的绘图——您可以绘制斐济群岛的轮廓。图片作者。

我也是。这些情节只是原情节的翻版。图片作者。

mapview(sbux_sf, map.types = "Stamen.Toner") 
coordinates(simulated_quake_data) <- c("long", "lat")
proj4string(simulated_quake_data) <- CRS("+proj=longlat +datum=WGS84")
lzn.vgm <- variogram(log(mag) ~ lat + long, simulated_quake_data, width=0.1)
plot(lzn.vgm)
lzn.fit = fit.variogram(lzn.vgm, 
                        vgm(c("Gau", "Sph", "Mat", "Exp")), 
                        fit.kappa = TRUE, 
                        fit.method= 1)
plot(lzn.fit, 1200)

还有一个。图片作者。

我想我们现在手头已经有了相当多的地图和数据视图。移入变异函数的时间。对于不知道变异函数是什么的人来说,变异函数是一种将位置之间的距离及其(协方差)结合起来的方法。空间协方差矩阵。诀窍是建立一个图,显示在什么距离点之间的差异不再重要。换句话说,当它们不再连接时。正如有不同类型的非空间协方差矩阵一样,也有不同类型的空间协方差矩阵。事实上,术语空间并不真正意味着什么,因为任何协方差矩阵被确定为连接相邻点和较远点之间的方差和协方差。这里的空间仅仅意味着您将包括位置数据。仅此而已。

在我看来像是一条直线——有很多噪音。这个情节一点帮助都没有。图片作者。

这个图是上面的 lzn.fit 模型结果的图形描述。图片作者。

上图中,你可以看到两种型号, NugSph ,意思是块状和球形。球形部分正是构建协方差矩阵的方式,就像自回归.),托普利兹非结构化。稍后,我将展示不同的空间协方差矩阵

金块是最有趣的。掘金的意思是“起点”-因此,根据高度,您希望空间协方差矩阵从哪里开始。这种基体不一定要包括块状物。

让我们开始探索这种数据。有很多包,但我最终会选择我最喜欢的 nlme 包。许多其他包调用该包来扩展它。

simulated_quake_data <- fabricate(
  data = quakes,
  fatalities = round(pmax(0, rnorm(N, mean = mag)) * 100),
  insurance_cost = fatalities * runif(N, 1000000, 2000000))
simulated_quake_data<-simulated_quake_data%>%
  group_by(lat, long)%>%
  mutate(mag=mean(mag),
         fatalities=mean(fatalities),
         insurance_cost=mean(insurance_cost))%>%
  distinct(., lat,long, .keep_all= TRUE)
EC97 <- as.geodata(simulated_quake_data, 
                   coords.col = 1:2, 
                   data.col = 4)
Var1.EC97 <- variog(EC97, trend = "1st")
Var2.EC97 <- variog(EC97, trend = "2nd")
plot.new()
par(mfrow = c(2, 1))
plot(Var1.EC97, pch = 19, col = "blue", main = "1st order variogram")
plot(Var2.EC97, pch = 19, col = "red", main = "2nd order variogram")
plot.new()
par(mfrow = c(1, 1))
plot(Var1.EC97, pch = 19, col = "blue", main = "Variogram Simulated Quake Data")
par(new = TRUE)
plot(Var2.EC97, pch = 19, col = "red", xaxt = "n", yaxt = "n")
ini.vals <- expand.grid(seq(0, 30, by = 1), seq(2,30, by = 1))
ols <- variofit(Var1.EC97, ini = ini.vals, fix.nug = TRUE, wei = "equal")
summary(ols)
wls <- variofit(Var1.EC97, ini = ini.vals, fix.nug = TRUE, wei = "npairs")
lines(wls)
lines(ols, lty = 2, col = "blue")

一阶和二阶变异函数都显示相同的结果。当试图将位置与地震震级联系起来时,真的没有太多的空间依赖性。我听汤姆说,我确实觉得这很有趣。但是我没有足够的知识去尝试去理解为什么会这样。从地质学的角度来看。图片作者。

让我们试着通过关注死亡人数而不是数量来模拟不同的数据。在这里,你可以看到我在坐标数据上建立了一个简单的线性模型。变异函数自然会遵循。

Model1=lm(fatalities~depth+mag,
          data=sbux_sf)
sbux_sf$residuals=residuals(Model1)
sbux_sf<-sbux_sf%>%cbind(., st_coordinates(.))
Vario_res = variogram(residuals~X+Y,
                     data=sbux_sf,
                     cutoff=100,
                     alpha=c(0,45,90,135))
plot(Vario_res)

尽管角度发生了变化,但它似乎是由一条*坦的线组成的。不要让差异欺骗了你。图片作者。

让我们再试一次,虽然我很确定结果会是一样的。我们可以构建一个来自 nlme 包的一般线性模型,并要求它构建一个球面协方差矩阵。

modSpher = gls(fatalities~depth+mag,
               data=sbux_sf,
               correlation=corSpher(c(100),
                                    form=~X+Y,
                                    nugget=F)) 
VarioSpher_raw = Variogram(modSpher, 
                           form =~ X+Y,
                           robust = TRUE, 
                           maxDist = 100, 
                           resType = "pearson") 
VarioSpher_normalized = Variogram(modSpher, 
                                  form =~X+Y,
                                  robust = TRUE, 
                                  maxDist = 100, 
                                  resType = "normalized") 
plot(VarioSpher_raw)
plot(VarioSpher_normalized)

我猜模型,尤其是学生化残差变异函数,看到的不仅仅是直线。我总是觉得这很棘手,但它表明,通过增加距离,你减少了协方差。有道理。图片作者。

在这里,另一个例子来引导一个 glm 模型,没有空间协方差矩阵,并保存其输出以构建一个绘制在原始数据坐标上的残差气泡图。当您觉得数据建模并不适合您时,这实际上是一种非常方便的绘制空间数据的方法。

YF.glm = glm(fatalities ~ depth+mag, 
             family = gaussian,
             data = sbux_sf)
temp_data = data.frame(error = rstandard(YF.glm), 
                       x = sbux_sf$X, 
                       y = sbux_sf$Y)
coordinates(temp_data) <- c("x","y") 
bubble(temp_data, "error", col = c("black","grey"),
       main = "Residuals", xlab = "X-coordinates", ylab = "Y-coordinates")
plot(temp_data$error ~ temp_data$x, xlab = "X-coordinates", ylab = "Errors")
plot(temp_data$error ~ temp_data$y, xlab = "Y-coordinates", ylab = "Errors")
plot(variogram(error ~ 1, temp_data))
plot(variogram(error ~ 1, temp_data, alpha = c(0, 45, 90, 135)))

话说回来,如果我看剧情,我发现很难破译模式。当然,斐济岛可以被认出来,但我看不到一个清晰的模式。这是一个绘制数据的好方法,但在这里并不能提供很多信息。图片作者。

这是一种分别在 x 和 y 坐标上绘制误差的方法。上面的情节有更完整的画面。图片作者。

和变差函数-整体或不同角度。90 度角显示了一些有趣的东西,突然向下倾斜。图片作者。

角度增加了不同级别的信息,因为空间协方差可能根据路线表现不同。你可以清楚地看到,从任何以前的地图,以及泡沫图。尽管难以解读,但很容易看出,在空间环境中,你看的角度(以及方向)起着作用。就像看地图一样。

让我们在模型中加入一个球形相关矩阵。

f1 = fatalities ~ depth+mag
model1 <- gls(f1,  
              correlation = corSpher(form =~ X + Y, nugget = TRUE), 
              data = sbux_sf)
plot(model1)
plot(Variogram(model.1))

看起来不错。但是变异函数无法绘制出来。这不是一个好的迹象,也许我应该要求从不同的角度来绘制。图片作者。

这里,一个线性混合模型,包括作为解释方差分量使用的站的数量。我不得不承认,在这个数据集中,没有一个变量看起来像真正的方差分量,这些变量后来被包含在混合模型中过滤掉了。因此,我使用了由整数组成的单一变量,该变量在包含的站点数量的相同级别上具有重复值。因此,在模型中它是可行的,但从生物学角度来看,我看不出包含它有什么好处。另一方面,一个技术例子没有什么生物学价值。至少对我来说是这样。

图片作者。

model.1<-glmmPQL(f1, random=~1|stations,
                    data=sbux_sf,
                    correlation=corExp(form=~X+Y, nugget = T),
                    family=gaussian)
plot(Variogram(model.1), 
     main = "Exponential Correlation")
model.2<-glmmPQL(f1, random=~1|stations,
                 data=sbux_sf,
                 correlation=corGaus(form=~X+Y, nugget = T),
                 family=gaussian)
plot(Variogram(model.2), 
     main = "Gaussian Correlation")
model.3<-glmmPQL(f1, random=~1|stations,
                 data=sbux_sf,
                 correlation=corSpher(form=~X+Y, nugget = T),
                 family=gaussian)
plot(Variogram(model.3), 
     main = "Spherical Correlation")
model.4<-glmmPQL(f1, random=~1|stations,
                 data=sbux_sf,
                 correlation=corRatio(form=~X+Y, nugget = T),
                 family=gaussian)
plot(Variogram(model.4), 
     main = "Rational Quadratic Correlation")

没什么意思,不管你用什么样的协方差结构。然而,模型变得越来越不稳定,如果你包括这么多额外的参数来估计,而没有任何额外的好处,就会发生这种情况。图片作者。

f2 = fatalities ~ s(depth)+s(mag)
model2.base <- gamm(f2, method = "REML",
                    data=sbux_sf,
                    family=gaussian)
model2.1 <- gamm(f2, 
                 method = "REML",
                 data=sbux_sf,
                 correlation=corExp(form=~X+Y, nugget = T),
                 family=gaussian)
model2.1$lme
model2.1$gam
par(mfrow = c(1, 2))
plot(model2.1$gam)
plot(model2.1$lme)
predict_gam(model2.1$gam)%>%
  ggplot(., aes(x=depth, 
                y=mag, 
                fill=fit)) +
  geom_tile()+
  scale_fill_viridis_c(option="magma")+
  theme_bw()

GAMM 模型揭示了与震级和死亡率的深层关系,但与深度和死亡率无关,也与深度和震级无关(我们没有真正建模,但仍然如此)。右图清楚地显示了震级的主要影响。图片作者。

f3 = fatalities ~ s(depth) + s(mag) + ti(depth,mag)
model3.base <- gamm(f3, 
                    method = "REML",
                    data=sbux_sf,
                    family=gaussian)
par(mfrow = c(1, 3))
plot(model3.base$gam)
model3.rand <- gamm(f3,
                    random = list(stations=~1),
                    method = "REML",
                    data=sbux_sf,
                    family=gaussian)
model3.1 <- gamm(f3, 
                 method = "REML",
                 data=sbux_sf,
                 random = list(stations=~1),
                 correlation=corExp(form=~X+Y, nugget = T),
                 family=gaussian)
predict_gam(model3.1$gam)%>%
  ggplot(., aes(x=depth, 
                y=mag, 
                fill=fit)) +
  geom_tile()+
  scale_fill_viridis_c(option="magma")+
  theme_bw()predict_gam(model3.1$gam) %>%
  ggplot(aes(depth, fit)) +
  geom_smooth_ci()+theme_bw()predict_gam(model3.1$gam)%>%
  ggplot(aes(depth, mag, z = fit)) +
  geom_raster(aes(fill = fit)) +
  geom_contour(colour = "white") +
  scale_fill_continuous(name = "y") +
  theme_minimal() +
  theme(legend.position = "top")

与单阶 GAMM 相同,但我们也包括了一个相互作用。图片作者。

我们包含的交互是这样的——需要展示一些东西,但它没有太多意义。图片作者。

这是另一个很好的方式来证明增加一些东西实际上是什么也没有增加。图片作者。

最后一句话结束了这个简短的介绍。空间回归是“空间的”,因为它试图使用已知的距离度量,如经度和纬度,尽管不同的变量也可以包括在内。例如,可以包括来自聚类技术(也使用度量来评估“距离”)的数据,以形成空间协方差矩阵。由时间点组成的纵向数据也使用距离,但在这里,连接要*坦得多,因此添加的参数如角度不起作用。

您拥有空间数据这一事实并不意味着对其进行建模或将其包含在模型中会让您的生活变得更加轻松。在上面看到的数据中,地震并没有描绘出图片中看到的那么多变化。这是一幅受地理空间影响很小的“*面”图像。这就是变异函数也是*坦的原因-除了可能是一个很小的点之外,你在斐济的位置并不重要。就是这样。

有了这些类型的例子,你总是可以试着去找一个完美地展示技术增加了什么的例子,但是我没有改变我不能预见的。原因是在大多数例子中,数据分析总是看起来很好,但很少是令人兴奋的。

我希望你喜欢它。如果有什么不对劲,请告诉我!

马达加斯加每年野火的时空可视化

原文:https://towardsdatascience.com/spatiotemporal-visualization-of-yearly-wildfires-in-madagascar-d92c2bd3374f

热图-VIRS(375 米)活动火数据展开时的动画

图片由作者提供。最终在线演示。2020 年马达加斯加的野火。

多亏了美国国家航空航天局,地球数据如今可以公开获取。资源管理系统的火灾信息部门(公司)在卫星观测的 3 小时内发布接*实时的火灾/热异常数据。空间分辨率适中的产品之一是 375 万像素的可见红外成像辐射计套件。 VIIRS 主动火灾产品探测火灾或热烟雾,具有夜间探测能力。关于 VIIRS 主动消防产品的这些和其他细节可以在 VIIRS 乐队网站上找到,你也可以找到关于 VIIRS 用法和特性的出版物

如果您对下载公司数据感兴趣,您可以在这里创建一个新的数据请求:下载公司数据

关于可视化

一旦你能够下载数据集,就有一种简便的可视化方法,通过展开的时空方法。在每年的时间范围内,您可以使用时间戳创建地图动画,而坐标可用于创建热图。确切地说,由于展开,您能够创建 wildfire 数据的热图动画。

查看在线演示:2020 年马达加斯加野火

这张马达加斯加年度热图动画是 2021 年 30 天地图挑战的一部分,它是与来自数字地理实验室的博士后研究员 Johanna Eklund 合作完成的。我感谢 Johanna 提供这个数据集并支持可视化的创建。

图片由作者提供。2020 年马达加斯加的野火

图片由作者提供。马达加斯加野火热图动画的推特帖子

关于数据许可和引用

美国航天局促进与研究和应用界、私营企业、学术界和公众全面和公开共享所有数据。阅读 NASA 的数据和信息政策

美国国家航空航天局要求使用公司数据产品或图像进行后续分发、获得增值产品或在书面或口头陈述中使用或引用公司产品的最终用户添加以下声明。

我们承认使用了美国宇航局资源管理系统(FIRMS)(【https://earthdata.nasa.gov/firms)的火灾信息的数据和/或图像,这是美国宇航局地球观测系统数据和信息系统(EOSDIS)的一部分。”

展开时的可视化参数

注意!您可以根据属性(如昼夜置信度)对数据进行子集划分。在决定你想要可视化什么之前,要知道元数据。

将 CSV 格式的 VIIRS 数据集上传到展开的。您可以选择热图选项,选择经度和纬度列来做好准备。

然后,下一步是设置热图的半径。半径确实能给出好的或坏的视觉效果。看看半径 20。

图片由作者提供。半径夸大的例子。

我们决定选择 0.1 级别的半径和无权重值。

锐化热图

我们想用这些数据来可视化一年中野火的蔓延和空间分布。为了清楚地了解它们在哪里,我们设置了强度阈值。我使用了强度较低的 0.8,因为它指出了非常高浓度的野火(黄色区域),我将阈值设置为适中值 0.3,以了解低浓度野火位于何处(红色区域)。

为了制作热图动画,我们创建了一个基于时间戳的过滤器。建议将间隔可视化设置为 1 个月。

在此之后,你将能够看到在马达加斯加的旱季和雨季野火的空间分布是如何变化的。2020 年 3 月,西南部的野火发生率较低。

图片由作者提供。2020 年 3 月马达加斯加野火

后来,在 2020 年 6 月,马达加斯加西北部的野火高度集中。

图片由作者提供。2020 年 3 月马达加斯加野火

当季节变化时,野火会转移到马达加斯加的东北部。现在,在季节性变化的国家的另一边,集中度很高。

图片由作者提供。2020 年 11 月马达加斯加野火

建议

请注意,将现象可视化为热图是聪明的做法。它可以帮助您确定特定区域内事件的空间集中度。事件的集中是关键,因为它关系到因果关系。这意味着事件的高度集中与附*的其他变量有关。斯诺将热图用于流行病学,并成功地发现了霍乱的病因。罗布·卡恩斯(Rob Carnes)写的一个关于它的好故事可以在这里找到:早期数据科学

所以,提高你的视觉化参数,直到你第一眼看到现象。相信我,作为一个地理学家,我能区分出一张地图是什么时候被仔细制作和锐化以帮助其他人形象化目标的。当你对你的地图感到满意时,你会让别人理解你所看到的。

我希望这个关于绘制野火的热图动画的简短建议对你和你未来的工作有用。如果您有任何问题,请随时联系我,我很乐意为您提供帮助。就在这篇文章里留言或者在 LinkedIn 上找我。

说生命的语言:AlphaFold2 和公司如何改变生物学

原文:https://towardsdatascience.com/speaking-the-language-of-life-how-alphafold2-and-co-are-changing-biology-97cff7496221

人工智能正在重塑生物学研究,并开辟治疗的新领域

OpenAI Dall-E 2 生成的图像,然后被作者修改(这里使用的蛋白质

蛋白质是生命的基本构件,参与细胞中的任何过程。它们独特的结构和多样的功能允许它们在细胞中执行任何任务。DNA 和 RNA 可以被看作是 ROM 和 RAM 存储器。

理解蛋白质的结构和功能需要科学界付出相当大的努力。去年, Alpha-fold2 彻底改变了如何预测蛋白质结构。本周(与欧洲生物信息研究所合作),他们发布了最完整的人类蛋白质预测三维结构数据库。这篇文章旨在讨论为什么预测蛋白质很难,为什么它很重要,以及人工智能和最新研究如何影响未来。

为什么对蛋白质及其结构大肆宣传?

蛋白质是任何生物体的微型马达,从单细胞到更复杂的生物体,如人类。尽管我们只有 20 个氨基酸作为蛋白质的组成部分,但我们可以有无限个不同形状和功能的蛋白质。

蛋白质与生物体生存、生长和繁殖的功能有关。事实上,任何细胞都使用蛋白质来消化、繁殖、维持自身,等等。蛋白质能够实现这一切要归功于它们独特的结构,而它们的结构决定了它们的功能。新生成的蛋白质序列(或肽)在一个叫做折叠的过程中达到最终形状。

这张图片展示了蛋白质是如何由氨基酸结合而成的。在折叠过程中,蛋白质到达折叠结构(或其功能形状)。图片由作者使用不同来源(这里这里这里这里这里这里)

然而,DNA 中的突变可以改变蛋白质序列,并导致蛋白质的结构修饰。这种结构可以被改变以至于失去它的功能。这是许多遗传疾病或癌症发生期间的基础。

也有蛋白质以错误的结构折叠的情况,这是不同疾病的基础。例如,老年痴呆症就是这种情况,异常折叠的β淀粉样蛋白在患者的大脑中堆积。朊病毒疾病也是其他例子,其中的病理因素是蛋白质的错误折叠。

蛋白质玩耍的无限棋盘

20 世纪 50 年代,香农估计大概可以玩 10120 (10 的 120 次方)个游戏。考虑到在已知的宇宙中应该不超过 1082 个原子,这可不是个小数目。

1969 年,Levinthal 指出,由于肽(一组氨基酸)具有大量的自由度,它可能以 10**300 种可能的构象结束。如果一个蛋白质要尝试所有可能的组合,它需要的时间会比宇宙的年龄还要长。由于一个蛋白质在几毫秒内折叠(找到正确的构象或结构),这被称为 莱文塔尔悖论

图片来源:此处。蛋白质折叠是一种生物学过程,其中蛋白质链(氨基酸结合的序列)在二级结构中自我组装,然后达到其最终构象或结构(也称为三级结构)。

蛋白质的结构决定了其功能,这对于设计治疗药物非常重要。传统上,研究人员依赖于实验方法,如https://en.wikipedia.org/wiki/X-ray_crystallography冷冻电子显微镜 ( cryo-EM) 。然而,虽然 X 射线晶体学正在返回详细的结构,但它既费力又昂贵(在某些情况下,蛋白质甚至需要 10 万美元)。因此,许多研究小组试图开发算法来预测序列的结构。

尽管事实上一个序列一对一地映射到一个 3D 结构,但是从序列中预测一个结构是非常困难的。一个单一的突变可以改变结构,两个非常不同的序列可以交出一个相似的结构。此外,该肽非常灵活,可以以多种方式旋转(此外,氨基酸也具有可以旋转的侧链)。此外,我们已经通过实验确定了大约 100,000 个蛋白质结构(蛋白质数据库),我们还有数百万个结构未知的序列。

蛋白质如何成为生物的语言

由于蛋白质是氨基酸序列,人类语言和蛋白质序列之间有着惊人的相似性:

  • ****层次组织:字组织成字,字组织成句,句组织成段。段落也可以合并成更长的文本。同样,蛋白质字母表由 20 个氨基酸组成,它们可以在二级结构(通常具有功能或结构作用)和三级结构中的二级结构中结合。蛋白质可以组装成复杂的复合体。
  • ****错别字和语法:虽然单词中的错别字可以极大地改变句子的意思(一个错别字曾经让一家旅行社损失了一千万美元),但你也可以写出语法正确但毫无意义的句子。同样,突变会破坏蛋白质的功能,导致疾病。

图片来源:此处。这张图片展示了如何比较人类语言和蛋白质序列。事实上,你可以注意到有一个类似的层次结构(面板 a)。此外,错别字可以改变句子的意思,因为突变正在改变蛋白质的结构(图 b)。

  • ****进化:语言不是一成不变的;它们不断发展,以表达新的概念,反映我们社会的变化。虽然今天有 8000 多种语言(归入 140 个家族),但它们可能起源于中非使用的一种共同的祖先语言(大约 5 万到 7 万年前)。所有的生物体(和它们的蛋白质)似乎都是从大约十亿年前的一个共同祖先进化而来的。

图片来源:此处此处。左图展示了一个语言系统树的例子(一个语系是如何从一种初始语言发展而来的)。右边的面板显示了所有的生物体是如何来自一个共同的祖先(被称为卢卡,最后一个普遍的共同祖先)

  • ****依存:在人类的语言中,词与周围的词是相互联系的。一个词的意思取决于上下文。此外,我们有长期依赖,这也可能是在不同的句子之间。蛋白质三维结构中存在相互作用,序列开始处的氨基酸可以与蛋白质序列末端的氨基酸相互作用。与此同时,蛋白质的二级结构之间以及蛋白质之间也有相互作用。

然而,也有一些不同之处。当表征蛋白质时,我们有时不知道单词的边界。此外,有许多蛋白质没有指定的功能(我们仍然需要破译它们)。

用变形金刚对付蛋白质

变形金刚注意力机制、以及衍生模型在自然语言处理以及更远的领域取得了巨大成功。最初设计用于处理单词序列,它们被证明对图像(视觉变形器)和其他类型的数据(音乐、图表等等)都很有用。

简而言之,经典变换器由编码器和解码器构成,用于序列间问题(例如机器翻译)。自从他们问世以来,他们已经攻克了自然语言处理领域,逐渐在几乎任何应用程序中取代了 RNN 和 LSTM。为什么?因为它们更具并行性,所以它们可以对长依赖关系进行建模,并且预先训练使得大型转换器可以在许多领域中重用。现在大多数最大的模型都是基于变形的( GPT-3伯特,以及他们所有的兄弟姐妹)。

事实上,变形金刚已经允许人工智能从业者用大量数据训练模型。这些年来,数据量持续增长(预计 2025 年将达到 80 兆字节)。同样的事情也发生在生物学和医学上:自从组学革命以来,我们积累了 DNA、RNA 和蛋白质序列。因此,为什么不使用 transformer 模型来利用生物学中的所有这些大数据

图来源:此处。左图显示了最新变压器模型中模型参数的指数增长。右图显示了保存在 UniParc 中的蛋白质序列的指数增长。请注意,蓝线代表实验确定的沉积在 PDB 的结构数量(相比之下,它似乎是*的)。

AlphaFold2 或 DeepMind 如何让整个科学界感到惊讶

2020 年,DeepMind 参加了结构预测的关键评估(CASP)挑战,这被认为是蛋白质结构预测最重要的挑战。它不仅仅是赢了,它的表现超过了 100 多个团队(超过 20%的准确率)。 AlphaFold2 能够以原子级精度预测蛋白质结构。他们是如何做到的?

第一个直觉是,他们可以将数据表示为图形,并将任务视为图形推理问题(氨基酸作为节点,邻*度作为边)。然后他们抛弃了卷积网络,发明了 Evoformer。其他有趣的技巧是注意力机制的使用,从多个序列比对开始,以及端到端的学习。

gif 源码,官方 AlphaFold2 GitHub 资源库

结果令人印象深刻,即使 AlphaFold 在建模柔性区域或寡聚体装配(四级结构,这甚至更难预测)方面存在问题。由于大多数蛋白质实际上是通过与其他蛋白质相互作用来工作的,这是一个重要的方面(DeepMind 还发布了α折叠多聚体,一种专门为寡聚体装配训练的模型)

回到序列:AlphaFold 的克星

最*,META 在 ICML 发表了一篇文章,展示了 ESM-IF1 ,这是一种能够进行反向折叠的模型(根据蛋白质的结构预测其序列)。有趣的是,为了组装训练集,他们使用 AlphaFold2 预测了来自 Uniprot 的 1200 万个序列的结构。这是一种克服实验确定的蛋白质结构数量有限的聪明方法。

然后,他们创建了一个模型,可以从主链结构(没有氨基酸链的蛋白质结构)预测蛋白质序列。氨基酸链对定义功能很重要,但它使问题更具挑战性,这已经是一个里程碑。该论文将反向折叠的方法描述为序列对序列的问题(使用自回归编码器-解码器从骨架坐标中检索序列)。

来自原。方法概述

有趣的是,他们将这个问题作为一个语言建模任务来处理,他们训练模型学习条件分布 p(Y|X):给定空间坐标 X,你预测氨基酸的序列 Y。此外,他们表明添加高斯噪声有助于模型训练(高斯噪声也是一个新的大趋势)。

他们考虑了不同的任务,例如从主链预测序列、多重构象、寡聚体和部分掩蔽的主链。这些都是生物学中可能的情况,因为蛋白质可以被截短(被掩盖的部分),与其他蛋白质相互作用(寡聚体),或在不同的环境中改变构象(pH 值的变化,药物的存在)。因此,这个模型在不同的研究领域都是有用的。

出自原。所考虑任务的概述。

懂蛋白质语言写我们自己的浪漫

“大自然是历史上最好的生物工程师。为什么不利用进化过程来设计蛋白质呢?” —弗朗西斯·阿诺

首先,我想着重谈谈到目前为止出现的两点:

  • 重新制定任务。AlphaFold2 和 ESM-IF1 都以一种巧妙的方式完成了转换数据的任务,这使他们能够以一种更简单的方式面对挑战。
  • ****AlphaFold2 作为模型组件。ESM-IF1 解决了缺乏使用 AlphaFold2 生成结构来训练其模型的例子的问题。

这两种想法都将影响未来,因为我们可以很容易地在生物学挑战中从 NLP 引入聪明的想法(从而使用已被证明有价值的方法)。随着几何学习的激增,我们也可以利用从图形中学到的经验(但这是另一个故事)。此外,AlphaFold 和 ESM-IF1 都是开源的,我希望它们能用于未来的研究。事实上,AlphaFold2 正在从序列中预测结构,而 ESM-IF1 正在做相反的事情,将它们结合起来对于许多生成任务来说可能是很棒的。

AlphaFold2 发布了一个预测结构的大型数据集,他们希望将其扩大到其他生物。了解这种结构对许多应用都很有用:

  • 许多我们不太了解的蛋白质的功能
  • 疾病和治疗选择。蛋白质结构用于暗示疾病,你需要这种结构来设计作用于蛋白质靶点的药物。
  • 虽然这有助于治疗传染性疾病,但我们也可以设计抗昆虫和植物疾病的药物

然而,这些模型将有助于为不同的应用设计新的蛋白质。事实上,当使用迁移学习时,预训练模型不一定用于原始任务。我们可以生成功能未知的蛋白质,然后通过反向折叠恢复序列。一旦你有了序列,你就可以在实验室里制造出蛋白质。

AlphaFold2 和 ESM-IF1 可以是一些人工智能模型的单独组件,甚至是一起组件(其中,您可以将这些预训练的模型用作多组件模型的构建块)。这可以用于不同于这两个模型所设计的原始任务的不同任务。图片由作者提供(里面的蛋白质来自这里)

结论

在我们面前,有激动人心的时刻。这是更具变革性的研究和应用的开端。基因编辑很快进入临床,再加上可能了解突变如何改变研究的想法,可以成为许多疾病(从神经系统疾病到癌症)的解决方案。例如,阿尔茨海默氏症和朊病毒是蛋白质错误折叠在发病中起主要作用的疾病。

然而,几乎任何大型语言模型都是由大公司发布的。AlfaFold2 和 ESM-IF1 也是两家蓝筹公司的研究产品。当然,这些项目创造了与学术机构的合作,但由于它们的许多应用将影响许多人的未来生活,我们也需要机构的努力。

此外,当我们预测临床应用的结构时,我们还需要知道为什么模型会得出这样的预测。因此,我们还需要讨论这种技术的可解释程度,以及如何提高其可解释性。

其他资源

  • 关于蛋白质结构预测(此处)和折叠问题(此处此处、此处)。如果你对实验方面感兴趣,可以查看这些视频(这里这里)
  • 关于为什么我们需要大型语言模型(此处)和人工智能驯服语言多样性(此处)的制度性努力
  • 关于蛋白质从头设计(视频)

如果你觉得有趣:

你可以寻找我的其他文章,你也可以 订阅 在我发表文章时得到通知,你也可以在LinkedIn上连接或联系我。感谢您的支持!****

这里是我的 Github 资源库的链接,我计划在那里收集代码,以及许多与机器学习、人工智能等相关的资源。

****https://github.com/SalvatoreRa/tutorial ****

谱熵——一个被低估的时间序列特征

原文:https://towardsdatascience.com/spectral-entropy-an-underestimated-time-series-feature-94e18ae5b958

时间序列无处不在。作为数据科学家,我们有各种时间序列任务,如分段、分类、预测、聚类、异常检测和模式识别。

根据数据和方法,特征工程可能是解决这些时序任务的关键步骤。精心设计的功能有助于更好地理解数据,提高模型的性能和可解释性。将原始数据输入到黑盒深度学习网络可能不会很好,特别是当数据有限或可解释的模型更受青睐时。

如果您从事时间序列的特征工程,您可能已经尝试过构建一些基本特征,如均值、方差、滞后和基于滚动窗口的统计。

在这篇文章中,我将介绍基于谱熵的建筑特征。当您的数据适用时,我建议您将其作为必须尝试的功能之一(频域分析有意义)。我将展示我如何利用谱熵解决时间序列的两个分类问题。我已经展示了如何将谱熵应用于异常检测问题。请参考用谱熵进行单变量随机时间序列的异常检测

我的重点是在应用方面,所以我会跳过一些基本的介绍和理论。

频域分析和谱熵

通常,时间序列数据保存在时域中。索引是以固定间隔采样的时间戳。有些时间序列具有波形或季节性,如感觉数据(地震、声音等)。).我们可以认为有东西在振荡,产生像波一样的数据。

当存在强波形时,在频域中转换和分析数据是有意义的。快速傅立叶变换是一种经典的从时域到频域的变换方式。频谱熵基于香农熵将频谱密度(频域中的功率分布)编码为一个值。如果你对基础介绍感兴趣,请查看单变量随机时间序列中的异常检测与谱熵

这里有一个不深入公式的快速介绍的类比。

假设我们研究人们如何度过业余时间。一个人花 90%在足球上。另一个 90%花在象棋上。虽然他们的兴趣不同,但几乎可以肯定的是,他们将把业余时间奉献给自己最喜爱的爱好。他们在某种程度上是相似的。这种相似性就是熵。它们将具有相同的较低熵,意味着较低的不确定性。

另一个人花 20%在徒步旅行上,30 %在阅读上,20%在电影上,30%在任何事情上。显然,第三个人与前两个人不同。我们不知道第三个人具体在做什么活动。在这种情况下,熵很高,意味着更高的不确定性。

光谱熵的工作原理是一样的。时间如何分配对应于功率如何跨频率分配。

接下来,让我们看两个真实世界的例子,看看谱熵是如何创造奇迹的。数据集不在公共域中。请允许我使用模糊的描述并隐藏数据集细节。

信号选择

该数据集的目标是建立一个包含数百个样本的二元分类器。每个样本都是标有“通过”或“失败”的测试结果。一个样本有接* 100 个信号。信号的长度是恒定的。图 1 显示了一个例子(每个小图有三个信号)。

图一。一个样本有大约 100 个信号(图片由作者提供)。

如果我们从每个信号中提取 X 个特征,我们将有 100*N 个特征。考虑到样本量小,我们会遇到“维数灾难”问题。

由于我们对每个样本都有大量的数据,所以让我们有所选择,只关注最有预测能力的信号,忽略不相关的信号。我计算了每个信号的光谱熵,所以我们只有 100 个特征。然后浅树被训练。

从最重要的特征来看,只有三个信号显示出与标签的高度相关性。在我研究了这些信号之后,我根据这些信号构建了定制的特征。最后,我构建了一个高性能的成功模型。此外,该模型只需要十个左右的输入特征,具有很好的通用性和可解释性。

频带选择

这个例子是另一个二元分类问题。每个样本只有一个不同长度的时间序列。总样本量小于 100。

长度的变化没什么大不了的。我们可以将时间序列分割成更小的固定长度的片段,并使用样本标签作为片段标签。

小问题是我们有一个相对较大的频率范围。因为采样频率是 48000HZ(覆盖了人耳能听到的声音),基于奈奎斯特定理,频域最高频率会是 24000。图 2 是一个例子。

图 2。原始信号和 FFT 结果示例(图片由作者提供)。

我直接尝试了光谱熵,但无法明确区分积极和消极。主要原因是两个标签具有相似的峰值频率和谐波。光谱分布是相同的。因此,整个频域上的谱熵不会显示出显著的差异。

既然频率分辨率高,我们就放大到某些频段而不是整个频谱。希望微妙的分离隐藏在某处。

我把频率分成了更小的波段。每个波段从 X 到 X+100。所以 24000 会给我们 240 个乐队。较高的频率只包含最小功率的噪声。因此,我忽略了较高频率的噪声,从 0 到 3000 中选取较低的频率,并将其分成 30 个频段。然后我计算了每个波段的光谱熵。最后,仅使用 30 个特征来训练基于树的模型。这种方法出奇地有效。图 3 显示了前两个特征(两个波段的光谱熵)。只使用 1200 到 1300 和 2200 到 2300 频段时有一个合理的界限。

图 3。前 2 个频段的频谱熵散点图(图片由作者提供)。

下面的图 4 显示了模型在标记为阳性的测试数据上的表现。顶部曲线是原始信号。中间的图是对每个部分的预测。底部是每个片段的频率 1200 到 1300 的熵。你可以看到大多数的预测都接* 1,熵可能在 0.9 到 0.93 之间。图 5 显示了标记为负的测试数据。现在预测下降到接* 0,熵在 0.8 到 0.9 的范围内变化。

图 4。标记为阳性的测试数据示例(图片由作者提供)。

图 5。标记为阴性的测试数据示例(图片由作者提供)。

结论

我展示了光谱熵如何帮助我快速找到最重要的信号和频带,以进行进一步的特性工程。

在这两个例子中,我们不必使用谱熵。例如,我们可以构建峰值频率、频带的*均幅度等特征。我们甚至可以将整个频域数据作为一个输入向量。毕竟,我们可以在频域中分离目标。

我喜欢从谱熵的角度探索特征,因为:

这很容易解释和计算。

显著压缩频域所包含的信息,保留核心信息。

缺点是一些信息丢失了。例如,根本不考虑量值。

此外,因为我们将频域中的值列表转换成信号值,所以不同的频谱分布可能具有相同的熵值。这就好比哈希冲突。例如,公*的硬币和有偏见的硬币具有不同的熵。但是对于一个偏向正面的概率为 X 的硬币和一个偏向反面的概率为 Y 的第二个硬币,如果 X 和 Y 相等,它们的熵将是相同的。

希望你能学到谱熵的好处,并在以后的时间序列工作中应用。

感谢阅读。

享受你的时间序列。

更多关于时间序列的文章

使用相干与扩散功率比估计器(CDR)的语音去混响

原文:https://towardsdatascience.com/speech-dereverberation-using-coherent-to-diffuse-power-ratio-estimators-cdr-e632a5a79a51

自动语音识别系统的预处理技术

作者创造的形象

介绍

当我们想要记录房间里的谈话时,会产生不同的音响效果。例如,我们可能会有一些不想要的背景噪音和房间表面的语音反射。

这些反射被称为 混响 。它们相互堆积,随着时间的推移而衰减,因为声音被物体表面吸收。已经表明,在混响环境中,我们的语音清晰度会受到不利影响。

此外,已经证明混响是自动语音识别 (ASR)系统https://asp-eurasipjournals.springeropen.com/articles/10.1186/s13634-015-0245-7中一个显著的误差来源。有些 ASR 系统在其设计中包含了在开始语音识别任务之前消除混响的方法。

因此,去混响是从声音中去除混响的过程,是信号处理中的一项关键任务。

在过去的十年中,已经提出了多种算法来抑制混响。在这篇文章中,提出了一种多信道去混响技术,该技术利用了相干与扩散功率比(CDR)【2】估计,提高了 ASR 系统的性能。

CDR 度量是相干(期望的)和扩散(不期望的)信号分量之间的比率,其允许构建能够消除混响的后置滤波器(通常称为基于 CDR 的后置滤波器)。这个关系告诉我们有多少干净的语音超过了混响,并且取从零到无穷大的值,其中零表示高混响,无穷大表示只有干净的语音存在。

它类似于SNRmetric、但噪声主要是混响。****

倾向于估计 CDR 度量的技术被称为 CDR 估计器。要查看一些 CDR 估值器,您可以查看参考文献【2】,其中包含原始推导和更深入的解释。

这项技术的有趣之处在于,它易于应用,并且不需要训练任何机器学习算法。

双麦克风阵列中 CDR 估计器的去混响

如何在双麦克风阵列中使用 CDR 估计器进行语音去混响?在图 1 中,我们可以看到带有 CDR 估计器的去混响标准流水线。

图 1 :基于预处理器和基于 CDR 的后置滤波器的去混响流水线。
图片取自【2】

其中【l,f】Y (l,f)Z(l,f) 分别是基于 CDR 的后置滤波器预处理信号,去混响信号分别在https://en.wikipedia.org/wiki/Short-time_Fourier_transform中。它们由 Z(l,f) = Y (l,f)G(l,f) 联系起来。******

此外,【l,f】估计(在图 1 中以表示估计的帽子出现)依赖于在 CDR 估计块中处理的其他变量(参见【2】),对它们的理解超出了本文的范围。**

预处理信号 Y (l,f) 是输入记录的*方幅度和来自其中一个麦克风的相位的组合*均,如等式 1 所示。这个等式发生在图 1 的块预处理中。

等式 1

除此之外,基于 CDR 的后置滤波器通过采用来自块 CDR 估计估计和两个参数来获得。这如等式 2 所示:

等式 2

其中为过量系数, G_min 为用户设定的最小增益。在实际应用中,这些参数往往被优化以获得更好的听觉体验。

【l,f】估计为不定式时,则【G(l,f)*** 取值 1,当为零时,【G(l,f)* 取值在之间的最大值* 因此,【l,f】的值介于 G_min 和 1 之间。*****

简而言之,基于 CDR 的后置滤波器作为针对每个时间和频率仓的低 CDR 值的混响* 衰减器工作。***

此外,这种方法考虑了两个麦克风的阵列,但也可能应用于更多麦克风的阵列,如本 未决问题 中所建议的,通过成对并执行*均

去混响的视觉示例

去混响效果如何?在图 2 中,可以理解在 STFT 域中使用基于 CDR 的后滤波的预处理信号及其去混响版本的例子。这两个图像是通过从公共存储库【3】标绘它们相应的光谱而获得的

图 2 :使用 CDR 估计器的 STFT 域去混响示例。由作者通过绘制来自公共知识库【3】的各个信号的结果而创建的图像

可以将预处理信号 Y (l,f) 中的混响视为频谱拖尾。换句话说,那些具有高能量(红色)的普遍频率成分往往持续更长时间。这表明由于反射,这些分量出现的时间比它们应该出现的时间要长。然而,在去混响信号中,这些反射比预处理信号中的反射短,表明基于 CDR 的后置滤波器正常工作。

使用 CDR 估计器消除混响的良好示例

如果你想听原声录音,图 2 中的去混响版本可以在资源库【3】中找到。

可以在文件roomC-2m-75 deg . wav中听有混响的原声,在 out.wav 中听去混响的版本。

自动语音识别系统中的应用

如前所述,使用 CDR 估计器的语音去混响的一个潜在应用是在 ASR 系统中。有几项研究表明混响对语音识别性能的损害有多大。

在研究【4】中,介绍了 CDR 估计器的使用如何显著提高了单词错误率 (WER),这是众所周知的语音识别性能的度量标准。在同一研究中,强调了 CDR 估计器也与 ASR 系统中的其他传统去混响方法竞争。

图 3 :两个不同房间的混响未处理信号(红色)和使用 CDR 后置滤波器技术去混响后(绿色)的 ASR 误码率。修改后的图像取自【4】

在图 3 中可以看到,对于混响未处理信号(红色)和应用 CDR 估计器后的去混响版本(绿色),WER 的不同值之间的比较。

A 室是演讲厅,混响时间 1 秒,B 室是大门厅,混响时间 3.5 秒。有关本次实验的更多条件和调整的参数,请参考【4】

结论

如果您正在构建一个具有麦克风阵列的应用程序,并且需要执行一些预处理技术来消除混响,并且计算资源很少,那么您应该考虑使用这种技术。

此外,如图 3 所示,使用 CDR 估计器进行语音去混响可以将 ASR 中的 WER 降低* 30%。

这种技术的一个很大的优点是如何在不使用经过训练的模型的情况下实现高质量的去混响结果。然而,一个简单的应用技术背后,隐藏着复杂的推导和理解。

参考

【1】德尔克罗伊,马克等《混响环境下远距离语音识别策略》。《EURASIP 信号处理进展杂志,2015 年,第 2015 卷,第 1 期,第 1–15 页。**

【2】施瓦兹和沃尔特·凯勒曼。去混响的相干-扩散功率比估计。IEEE/ACM 音频、语音和语言处理汇刊,23(6):1006–1018,2015。****

3安德里亚斯·施瓦茨。cdr-dereverb。https://github.com/andreas12345/cdr-dereverb,2019。

https://www.researchgate.net/publication/262639033_Coherence-based_Dereverberation_for_Automatic_Speech_Recognition**施瓦茨、安德里;布伦德尔、安德列亚斯;沃尔特.凯勒曼。用于自动语音识别的基于相干性的去混响。在程序中。DAGA 。2014.****

用于分析的语音识别

原文:https://towardsdatascience.com/speech-recognition-for-analytics-e98c1e496873

梁杰森Unsplash 上的照片

利用语音到文本处理充分利用您的音频数据

为了在竞争中占据优势,企业正在收集比以往更多的数据。大数据架构使这种增长成为可能,促进了海量和多种数据的快速捕获。除了常规的表格数字事务,我们还可以看到事件日志、文本消息,甚至是多媒体内容

如果适当利用这些多媒体内容,可以为你所在的企业带来额外的关键洞察力。想象一下,如果您可以从客户服务电话中分析客户的主要问题和最常用的解决方案,或者在您的电子商务*台上消除垃圾/无关的文本/图像/视频评论。在本文中,我们将重点关注音频分析,特别是处理/转录它们,以便使用文本自然语言处理(NLP)工具进行进一步分析。

本次分析使用的完整代码共享在this Github project下。

本文旨在使用 Python 中的语音识别库。如果你有兴趣了解更多关于音频数据内在的、与波形相关的特性,你可以查看这篇文章

音频文件的预处理

Python 中的大多数音频处理库要求音频文件为波形(。wav)格式进行处理。WAV 本身是最受欢迎的数字音频格式之一,因为它具有无损文件特性,不同于已经被压缩的 mp3/m4a。因此,如果您的音频文件不是 WAV 格式,建议您在继续分析之前先将其转换为 WAV 格式。

在 Python 中,我们可以使用 pydub 包进行音频文件的转换。从输入文件创建 AudioSegment 实例并使用“导出”功能导出所需的输出文件非常简单。

from pydub import AudioSegmentAudioSegment.converter = "Downloads/ffmpeg"
AudioSegment.ffmpeg = "Downloads/ffmpeg"
AudioSegment.ffprobe ="Downloads/ffprobe"from pydub.playback import play# Create an AudioSegment instance
raw_file = AudioSegment.from_file(file="Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.ogg", format="ogg")# Check the type
print(type(raw_file))# Export the .ogg file as wav
raw_file.export(out_f="Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.wav",
                format="wav")

除了转换文件格式,pydub 包还可以用于其他音频文件操作,如扩展/附加音频文件,分割音频文件,增加音量,或设置淡出过渡。

通用音频功能探索

现在我们有了正确的输入文件格式,我们可以通过查看音频文件的一般属性来开始我们的音频分析,如音频文件本身的(声压的强度或级别)(单位时间的振动),或 长度 。**

在这个分析中,我使用了来自 Wikimedia 的两个开放许可的音频文件:

这些文件源的详细信息可以在本文末尾找到(参考资料部分)。

我们可以使用 Python 中的 librosa 包来提取音频文件特征。Librosa 是一个扩展包,允许我们提取各种音频文件属性,从振幅/频率等直接属性到更高级的导数属性,如过零率梅尔频率倒谱系数(MFCCs) 。它还允许各种可视化,从常规的线形图到光谱图格式。

下面是在 librosa 中生成振幅可视化的代码。从可视化中,我们可以看到从文件的开始到大约 2.30 分钟的整个过程中相当一致的信号。

**import librosa
import librosa.displayy, sr = librosa.load('Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.wav', mono=False)fig, ax = plt.subplots(figsize=(15, 3))
img = librosa.display.waveshow(y, sr=sr, ax=ax)ax.set(title='Envelope view, stereo')
ax.label_outer()plt.show()**

美国之音新闻文件的振幅图

下面是在 librosa 中生成频谱图的代码。从这个图像中,我们可以看到音频文件的 5 个大片段。有一个是从 0:00 到 0:08,然后从 0:08 到 0:25,然后从 0:25 到 1:17,然后从 1:17 到 1:30,然后 1:30 到文件结尾。如果我们听音频文件,这些片段代表文件的变化设置——在清晰的录音室录音和轻微嘈杂的现场采访之间。

**# Load audio file to Librosa
y, sr = librosa.load('Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.wav')# Converts data into short term Fourier transform. 
# STFT converts signals such that we can know the amplitude of the given frequency at a given time
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)# Display spectogram
fig, ax = plt.subplots(figsize=(15, 3))
img = librosa.display.specshow(D, y_axis='log', x_axis='time', sr=sr)
ax.set(title='Logarithmic-frequency power spectrogram')
ax.label_outer()plt.colorbar()**

美国之音新闻音频的对数频率频谱图

语音识别

在探索了一般的音频功能之后,是时候进入这个项目令人兴奋的亮点了——语音识别部分

很简单,你可以 将你的音频文件运行到一个预先确定的引擎中,以获得文本转录 。我们在这个转录过程中使用 Python 中的语音识别库。它支持多种语音识别引擎,如 CMU 斯芬克斯谷歌云语音 API微软必应语音识别IBM 语音转文本。请注意,其中一些引擎需要使用 API 令牌。对于这个项目,我们使用谷歌语音识别和默认的 API 键

语音识别功能

语音识别库有一个识别器类,它有一组用于语音识别设置和功能的内置函数。

首先,我们导入库并设置识别器。然后,我们导入要转录的音频文件。

**import speech_recognition as sr# Set up recognizer
r = sr.Recognizer()# Import Audio data
test_audio = sr.AudioFile('Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.wav')**

从这里开始,我们可以使用recognize_google函数直接进行转录,该函数接受音频文件输入并给出转录作为输出。然而,根据您的音频文件的质量,其他一些功能可以用来增强您的音频文件,从而提供更好的转录。****

****如果您的音频文件包含一些您不需要转录的不必要内容,您可以使用record 功能中的“持续时间”和“偏移量”变量来选择要转录的音频文件的特定部分。

****如果您的音频文件有噪音,您可以使用adjust_for_ambient_noise功能校准环境噪音水*的能量阈值。校准允许识别器忽略噪声并专注于实际语音。

下面是 VOA 新闻音频文件的语音识别代码。

**# Set up recognizer
r = sr.Recognizer()# Import Audio data
test_audio2 = sr.AudioFile('Downloads/2010-06-29_VOA_News_report_-_FIFA_to_Re-Examine_Video_Replays_After_World_Cup_Referee_Mistakes.wav')# Covert the AudioFile to AudioData, adjust for noise, cut by duration
with test_audio2 as source2:
    r.adjust_for_ambient_noise(source)
    audio2 = r.record(source2, duration=60)
    audio3 = r.record(source2, duration=60)
    audio4 = r.record(source2)# Generate transcription
text_audio = r.recognize_google(audio2) + " " + r.recognize_google(audio3) + " " + r.recognize_google(audio4)
text_audio**

它将生成如下转录文本。

美国之音新闻音频的语音识别转录

语音识别引擎可能有几种来自不同识别技术的转录结果。识别器类自动地挑选具有最高置信度得分的一个。如果你想检查其他的转录结果,你可以在recognize_google函数中添加show_all=True变量。

有各种转录结果可用

关于语音识别库的更多细节,你可以在这里浏览他们的文档。

非英语语言的语音识别

语音识别也允许识别一些非英语语言。在这种情况下,你只需要在recognize_google函数中添加language="id-ID"变量。

**# Set up recognizer
r = sr.Recognizer()# Import Audio data
test_audio = sr.AudioFile('Downloads/Aldi_-_Indonesian_language_-_Bible_Verse_John_3-16.wav')
with test_audio as source:
    r.adjust_for_ambient_noise(source)
    audio = r.record(source)

r.recognize_google(audio, language="id-ID", show_all=True)**

非英语(印度尼西亚语)语言的转录结果

此处列出了可用语言的列表

使用标点符号

从上面的结果可以看出,语音转录是以纯文本的形式交付的,没有任何格式。如果你在抄写一篇冗长的演讲或对话,这可能会令人困惑。

我偶然发现了标点符号库,可以用来在文本文件上标注标点符号,提高转录的可读性。使用起来非常简单,你只需要运行punctuate函数,就可以得到带注释的文本。

**from punctuator import Punctuatorp = Punctuator('Downloads/INTERSPEECH-T-BRNN.pcl')
text_audio_punc = p.punctuate(text_audio)
text_audio_punc**

标点转录结果

在某些情况下,注释可能无法完美地工作,但它确实有助于提高可读性。

语言分析

现在以文本格式的形式,你可以将各种自然语言处理技术应用到你的音频转录中。可能性是无穷的,从做情感分析,话题抽取,分类等等。

在这个项目中,我只是使用空间提供了一个简单的命名实体识别的例子,这对主题/对象分析和分类很有用。

**import spacy# Load spaCy language model
nlp = spacy.load("en_core_web_sm")# Set up the transcribed text as doc
doc = nlp(text_audio_punc)# Find named entities in doc
for entity in doc.ents:
    print(entity.text, entity.label_)**

命名实体识别结果空间

查看该结果,我们可以确定音频文件正在谈论世界杯事件(来自国际足球协会和世界杯实体),其中提到了国家之间的一些比赛(来自国家名称和一些球员姓名实体)。

结束语

包括音频数据在内的多媒体数据虽然已经被收集,但还没有被普遍用于产品和商业分析。有一些 Python 库包可以用来从这些音频数据中提取特征,包括使用语音识别技术的内容;允许我们从这些音频数据中获得最大收益。一旦转录成文本,这些数据就可以利用自然语言处理技术用于各种目的。可用性各不相同,从通过情感分析了解客户满意度,从客户服务电话评估关注的话题,到从*台删除垃圾/辱骂内容,等等。如果您已经有了数据(或者在收集和存储它们方面没有任何困难),为什么不充分利用这些数据来进行产品分析呢?

本分析中使用的完整代码共享在this Github project下。**

使用的音频参考

[1] 美国之音新闻报道——国际足联重新审查世界杯裁判失误后的录像回放。该媒体在美国属于公共领域,因为它完全由美国联邦政府官方对外广播机构美国之音制作和提供的材料组成。

[2] 阿尔迪语——印尼语——圣经约翰福音 3–16 节. ogg;根据知识共享协议许可 CC0 1.0 通用公共领域专用

如何使用 Python 执行语音转文本和删除 PII

原文:https://towardsdatascience.com/speech-to-text-pii-python-assemblyai-45dfcc02eebe

使用 AssemblyAI 和 Python 从文本转录中移除个人身份信息(PII)

本·斯威特在 Unsplash 上的照片

介绍

在我最*的两篇文章中,我讨论了关于音频转录的 脏话过滤敏感内容检测。数据处理和存储的另一个重要方面是如何处理敏感数据。

用来描述敏感数据的一个常用术语是个人身份信息 (PII)。一般来说,任何可以用来(或结合其他信息)识别、联系人定位个人的信息都被认为是 PII。

根据美国国家标准与技术研究院 (NIST)的规定,以下数据元素被视为个人身份信息:全名、面部数据、地址、电子邮件地址、身份证或护照号、指纹、信用卡号、出生日期、出生地、基因信息、电话号码、登录名、昵称、驾照或车牌号。

例如,考虑客户和代理之间的电话呼叫,其中前者需要提供信用卡信息。同时,该公司可能会出于监控或培训目的对通话进行录音。因此,我们需要编辑信用卡信息,以确保没有 PII 出现。另一个例子是医疗记录中的健康信息编辑。

在今天的教程中,我们将使用 AssemblyAI API 和 Python 来对音频文件执行语音到文本转换,同时确保消除个人身份信息。

执行语音到文本转换并消除 PII

在本节中,我们将逐步介绍如何对输入音频文件执行文本转录,同时删除任何个人身份信息。

为此,我们将使用 AssemblyAI API。如果你想跟随这个教程,你首先需要从他们的网站发布一个访问令牌(这是完全免费的),我们将在发送到 API 端点的请求的头中使用这个令牌。

现在让我们开始导入一些今天将使用的库,并为请求准备头部,包括我们的访问令牌。

准备请求的标题—来源:作者

现在,我们需要做的第二件事是读取输入音频文件,然后将其上传到 AssemblyAI 的托管服务。API 的端点将返回一个链接,指向我们将在后续请求中使用的上传文件。

读入输入音频文件并将其上传到 AssemblyAI 主机端点—来源:作者

既然我们已经在托管服务上上传了音频文件,我们就可以继续进行语音到文本的转换了。同时,我们将使用 AssemblyAI 的 PII 修订功能,以便在转录文本返回给我们之前,从转录文本中删除个人身份信息,如电话号码和社会保险号。

为了执行语音到文本转换,我们只需要在向 AssemblyAI 的相应端点发送我们的POST请求时提供audio_url参数。对于 PII 修订,我们只需要提供一个名为redact_pii的附加参数,并将其设置为True

默认情况下,音频文件中说出的任何 PII 都将被转录为一个哈希码(#)。比如提到名字John,就会转录成####。该默认行为可通过redact_pii_sub参数控制,该参数用于定制如何替换 PII。该参数可以有两个值,或者是hash(这是我们已经提到的默认值)或者是entity_name。如果选择后一个值,则检测到的 PII 将替换为关联的策略名称。比如用[PERSON_NAME]代替John这是为了可读性而推荐的。

今后,我们甚至可以从执行 PII 修订的检测算法中指定应该应用什么类型的策略。这种策略的列表可以通过接受各种有效选项的redact_pii_policies参数来确定。其中包括(但不限于)email_addressblood_typemedical_processdate_of_birthphone_number。要更全面地了解可用政策选项,请务必参考官方文件的相关章节。

现在,让我们使用其中的一些参数来执行语音到文本转换和编辑 PII,如下所示:

PII 修订版语音转文本—来源:作者

在上面分享的要点的最后,我们可以看到一个来自端点的示例响应。您还可以观察所有参数的值(包括redact_piiredact_pii_sub)。

所做的转录是异步,这意味着当一个音频文件被提交用于文本转录时,它通常会在音频文件持续时间的大约 15–30%内完成。因此,我们需要不断地发出一些GET请求,直到响应的状态为completederror

从异步转录中获取结果—来源:作者

最后,我们现在可以将文本转录到输出文件中,这将使我们在检查结果时更加容易。

将文本转录写入输出文件—来源:作者

解读回应

现在,输出文件应该包含从相关 API 端点返回的文本转录。输入音频文件的(口述)内容如下。

John was born in 12/02/1993\. 

生成的文本转录如下所示:

[PERSON_NAME] was born in [DATE_OF_BIRTH]

下面给出了返回响应的更完整形式。

来自汇编 AI 转录端点的示例响应—来源:作者

文本转录位于响应的text参数下。转录算法检测到的单词将与观察到所说单词的时间戳和confidence分数一起包含在words字段中。关于响应中包含的各个字段的更完整描述,请参考官方文件中的相关章节。

完整代码

本教程中使用的完整代码可以在下面分享的 GitHub Gist 中找到。

文本转录和个人身份信息编辑完整代码—来源:作者

最后的想法

在今天的文章中,我们讨论了个人身份信息的重要性以及哪些数据被视为个人身份信息。此外,我们展示了如何使用 AssemblyAI API 执行语音到文本的转换,并从生成的文本转录中消除 PII。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

你可能也会喜欢

使用 OpenAI 的耳语进行语音转文本

原文:https://towardsdatascience.com/speech-to-text-with-openais-whisper-53d5cea9005e

轻松语音转文本

纪尧姆·德·日耳曼在 Unsplash 上的照片

OpenAI 最*发布了一个新的语音识别模型,名为 Whisper。与 DALLE-2 和 GPT-3 不同,Whisper 是一个免费的开源模型。

Whisper 是一个自动语音识别模型,根据从网络上收集的 680,000 小时多语言数据进行训练。按照 OpenAI 的说法,这种模型对口音、背景噪音和技术语言具有鲁棒性。此外,它支持 99 种不同语言的转录和翻译。

本文解释了如何使用 Whisper 模型和 Python 将语音转换成文本。并且,它不会涵盖模型如何工作或者模型架构。你可以在这里查看更多关于密语的信息。

Whisper 有五款(参考下表)。下面是 OpenAI 的 GitHub 页面上的表格。根据 OpenAI 的说法,纯英文应用程序有四种模式,表示为.entiny.enbase.en车型表现更好,但是small.enmedium.en车型的差异会变得不那么显著。

Ref: OpenAI 的 GitHHub 页面

对于本文,我将 Youtube 视频转换为音频,并将音频传递到 whisper 模型,以将其转换为文本。

作者图片

我用 Google Colab 和 GPU 来执行下面的代码。

导入 Pytube 库

!pip install -— upgrade pytube

阅读 YouTube 视频并下载为 MP4 文件进行转录
在第一个例子中,我正在按照下面的 Youtube 视频阅读著名的拍摄电影对话

**#Importing Pytube library**import pytube**# Reading the above Taken movie Youtube link**video = ‘https://www.youtube.com/watch?v=-LIIf7E-qFI'data = pytube.YouTube(video)**# Converting and downloading as 'MP4' file**audio = data.streams.get_audio_only()audio.download()

输出

上面的 YouTube 链接已经下载为“MP4”文件,并存储在 content 下。现在,下一步是将音频转换成文本。我们可以使用 whisper 用三行代码来实现。

导入 Whisper 库

# Installing Whisper libary!pip install git+https://github.com/openai/whisper.git -qimport whisper

装载模型

我在这里使用了medium多语言模型,并传递了上面的音频文件I will find YouI will Kill You Taken Movie best scene ever liam neeson.mp4并存储为一个文本对象

model = whisper.load_model(“large”)text = model1.transcribe(“I will find YouI will Kill You Taken Movie best scene ever liam neeson.mp4”)**#printing the transcribe**text['text']

输出

以下是音频中的文字。和音频完全吻合。

I don’t know who you are. I don’t know what you want. If you are looking for ransom, I can tell you I don’t have money. But what I do have are a very particular set of skills. Skills I have acquired over a very long career. Skills that make me a nightmare for people like you. If you let my daughter go now, that will be the end of it. I will not look for you. I will not pursue you. But if you don’t, I will look for you. I will find you. And I will kill you. Good luck.

如何转换不同的音频语言?

我们知道,Whisper 支持 99 种语言;我正在尝试使用印度语言,并把下面的电影剪辑视频转换成文本。

在这个例子中,我使用了large模型

#Importing Pytube libraryimport pytube# Reading the above tamil movie clip from Youtube linkvideo = ‘https://www.youtube.com/watch?v=H1HPYH2uMfQ'data = pytube.YouTube(video)# Converting and downloading as ‘MP4’ fileaudio = data.streams.get_audio_only()audio.download()

输出

加载大型模型

**#Loading large model**
model = whisper.load_model(“large”)text = model1.transcribe(“Petta mass dialogue with WhatsApp status 30 Seconds.mp4”)**#printing the transcribe**text['text']

输出

模型转换成文本以上泰米尔语音频剪辑。模型很好地转录了音频;然而,我可以看到语言中的一些小变化。

சிறப்பான தரமான சம்பவங்களை இனிமேல் தான் பார்க்கப் போகிறேன். ஏய்.. ஏய்.. ஏய்.. சத்தியமா சொல்கிறேன். அடிச்சி அண்டு வேண்டும் என்று ஓழ்வு விட்டுடுவேன். மானம் போலம் திருப்பி வராது பார்த்துவிடு. ஏய்.. யாருக்காவது பொண்டாட்டி குழந்தைக் குட்டியன் சென்றும் குட்டும் என்று செய்துவிட்டு இருந்தால் அப்டியே ஓடி போய்டு.

主要试了中大型。它是稳健的,并准确转录音频。还有,我用带 GPU 的 Azure Synapse 笔记本转录了一段最长 10 分钟的音频,效果非常好。

这是完全开源和免费的;我们可以在您的项目中直接使用它进行语音识别应用。我们也可以把其他语言翻译成英语。我将在我的下一篇文章中用长音频和不同语言的英语来介绍它。

可以多查一下 Whisper 的型号;请访问 Whisper 的 G ithub 页面

感谢阅读。继续学习,并期待更多!

参考

  1. https://github.com/openai/whisper
  2. https://openai.com/blog/whisper/

速度改变了分析,但我们的流程也需要改变。

原文:https://towardsdatascience.com/speed-changed-analytics-but-our-processes-need-to-change-too-e5a1588fb672

让我们喘口气,重新思考我们的工作方式。

图片由作者提供。

当汽车在 20 世纪 20 年代成为美国的主流时,美国重建了道路,制定了新的法律,竖立了标志和信号来控制交通流量。没有这些工艺的改进和基础设施的大修,汽车的出现是不可能的。在过去十年中,我们在数据世界中经历了类似的事情:云数据仓库的出现。

现代云数据仓库是。你可以询问数据的速度是前所未有的。但是,就像汽车的出现一样,我们需要重新思考我们如何处理数据,以确保我们不会鲁莽地加快[响应数据请求]的速度。我们需要:

  • 📏更好地与业务目标保持一致。
    速度意味着分析量更大。这使得在翻箱倒柜之前就目标达成共识变得更加重要,以确保领导层接受正确的数据叙述。否则,我们就有可能成为“垃圾进来,垃圾遍地”的牺牲品。
  • 📒围绕生成和共享分析工作流程的更好实践。我们工作得越快,一切就越乱。我们需要确保工作可持续、可重复和有组织的标准。
  • 🕵️‍♀️调整我们的责任和期望。我们可以考虑一个新的头衔:我们需要密切参与决策过程的“决策科学家”。

在本文中,我没有解决上述问题的灵丹妙药——只是一些指向一个新兴主题的观察结果:我们的分析过程的重构

2010 年代的数据生态系统……很慢。

当我在 way fair(2017–2019)时,拉数据慢得令人痛苦。从 Hive 或 Vertica 中提取哪怕是最简单的数据片段也需要几分钟到几个小时。您可能会怀疑这种缓慢只是限制了可以完成的工作量,但其影响更广泛。这种缓慢影响了我们优先考虑的工作的性质。我经常喜欢不需要从仓库中提取的工作——在 Jupyter 笔记本的内存中玩数据,聪明的算法解决方案,机器学习。

我不认为我是唯一有这种倾向的人。这个时代的数据工作文化似乎天生偏爱深入和详尽。我敢打赌,我们通过做这种缓慢的工作所强调的用例甚至塑造了集体的企业对数据工作的态度:解决问题胜于利益相关者管理;解释的复杂性;甚至可能是数据科学而不是分析。我们看到的只有,我们有耐心(或时间)看到的只有仓库外的作品。迭代的 SQL 工作流非常慢,因此被放在次要位置。

这里有一个*凡而强大的原则在起作用:

如果你不能快速获取数据,你就不能专注于依赖快速获取数据的事情。

在过去的十年里,这种安静的想法在数据工作中非常普遍,对我们如何使用数据产生了持久的影响。因为从传统的数据仓库中提取数据一直以来都很麻烦,所以我们在这个前提下设计了一切—从我们的决策过程到我们的组织结构。

现在事情很快,但我们的流程还没有跟上。

当我在 2019 年初搬到 Airbnb 时,我个人与数据的关系发生了一个阶跃函数变化。虽然 Airbnb 拥有与 Wayfair 类似的堆栈,但他们优化的 Presto + Druid (Minerva)层使查询能够比 Wayfair 更快、更可靠地执行。因为 Airbnb 已经这样做了一段时间,文化有时间去适应。实际的结果是:产品请求几乎没有停止过,探索性的工作支持了几乎每一个重大决策,SQL 是一种基础语言而不仅仅是一个接入点,数据科学人员(越来越多的“分析”风格)被嵌入到每个团队中。

但是这种转变带来了大量的新问题。这场革命实际上是由利益相关方主导的,这意味着我们在数据领导的决策对话中缺少一个明确的角色定义。相反,我们遵循我们的产品同行的提示,并在我们的嵌入式工作中处理通常正交的团队计划。在缺乏来自领导层的清晰信息的情况下,我们经常在我们认为需要我们的地方、我们的经理希望我们做的事情和我们认为真正重要的事情之间的紧张关系中航行。Airbnb(公*地说,其他科技独角兽同行的数据科学/分析组织)领导层的普遍观点是“尽可能多地回答请求,但也要做自己的项目。”在一天结束的时候,我们构建了太多的仪表板,但是仍然做了太多的重复工作,并且最终关注了错误的问题。

根本原因:虽然我们有能力回答比以前多得多的问题,但我们并没有按照这个新世界的方式建立我们的团队、基础设施或目标

我们的流程正在改进,但我们可以做得更好。

如果您在过去几年中一直关注数据社区的文献,您可能会注意到一些新兴的叙述试图围绕我们应该如何在这个勇敢的新世界中工作建立更好的实践。我们已经达成共识,即利益相关方的一致至关重要。很像产品团队靠用户反馈为生,每一份数据工作都可以被看作是利益相关者使用的“数据产品”。如果利益相关者的问题没有得到解决,产品就是失败的。我们还开始以不同的方式构建我们的团队以不同的方式雇佣,建立紧密耦合的工作组,使从数据准备到决策的流程顺畅。尽管如此,我们还是犯了错误,过于关注反应速度,在追求自助服务的过程中构建了过多的仪表盘。我们还没有破解可扩展、简化流程的公式,我很高兴看到我们将如何解决一些遗留的大问题:

  1. 📏分析团队仍然需要更好地与业务目标保持一致。 最*几年,有人呼吁像管理产品团队一样管理你的数据团队,这对整个行业来说是一个福音。但是我们仍然没有解决如何精确地激励我们的团队以一种将业务目标置于其核心的方式运作,这种方式让我们超越我们的能力,简单地回答更多关于数据的问题。第一步当然是雇佣能够跨越技术-商业背景鸿沟的IC,但这只是解决方案的一半:这里缺少的部分仍然是自上而下的建立一致性。
  2. 📒围绕生成和共享分析工作流程的更好实践。 我们现在几乎可以随时做出数据驱动的决策(随着即将兴起的度量层,这一点将变得更加真实),因此我们需要仔细思考围绕这项工作的流程——如何达成一致,如何策划和呈现解释,以及在一个分析越来越多的世界中,如何减少重复工作。dbt 已经将其中的一个方面整理成文,使可重用的数据模型能够被集中起来,以代码的形式存储,被其他人发现和利用。但分享见解的过程仍有许多不足之处。更多的工作需要被写出来,而更少的工作应该以标签的形式存在于你的 IDE 中(我们相信这可以通过在文档中执行 SQL 工作而得到很大程度的解决)。
  3. 🕵️‍♀️我们需要决策科学家吗?过去的十年感觉像是数据科学家的十年。过去几年,分析工程受到了推动。我想接下来的几年将会是决策者的天下。“决策科学”已经成为一些大型科技公司的一个角色,在这些公司中,核心数据模型、指标层和基本高管报告仪表板已经在很大程度上构建、扩展和集中化,这意味着对数据*台的需求减少,而对解释和忠实利用数据的需求增加。

我很期待故事的发展。该行业在我们如何获取和准备数据方面经历了巨大的变化,但现在是我们重新考虑决策和分析工作流程的时候了——这是我们最初获得这些数据的原因。

我们正在打赌,世界需要朝着更好的调整和流程前进,我们已经建立了hyperquery来反映我们对此的雄心。hyperquery是一个用于 SQL +分析工作的 doc 工作区。通过在有组织的文档工作区中进行分析工作,我们不仅可以更轻松地编写和共享基于查询的分析,还可以推动协调、扩大影响、避免重复工作。在hyperquery . ai查看我们正在构建的内容。

推文@ imrobertyi/@ hyperquery来问好。👋
关注我们LinkedIn。🙂

使用 SageMaker 分布式数据并行库加快 AWS 的高效网络培训

原文:https://towardsdatascience.com/speed-up-efficientnet-training-on-aws-by-up-to-30-with-sagemaker-distributed-data-parallel-library-2dbf6d1e18e8

理解大数据

使用 SageMaker 分布式数据并行库加快 AWS 的高效网络培训

深入探讨 SageMaker 分布式数据并行如何帮助将最先进的 EfficientNet 模型的训练速度提高 30%

卷积神经网络(CNN)现在广泛用于执行计算机视觉任务。自动驾驶汽车、安全系统和医疗保健等领域正朝着在应用工作流中采用 CNN 的方向发展。这些用例通常需要高精度模型,根据部署位置的不同,计算要求也有所不同,例如,基于边缘的安全系统可用的计算基础设施与基于云的医疗成像系统非常不同。

然而,训练机器学习模型并将其集成到应用程序中可能会很麻烦,这就是为什么我们在 AWS 开发了亚马逊 SageMaker ,这是一个完全管理的端到端机器学习(ML)*台。SageMaker 提供工具并管理基础设施;因此,ML 科学家和开发人员可以专注于模型开发。你可以仔细阅读 SageMaker 示例 GitHub 知识库,深入了解 SageMaker 如何简化你的机器学习管道。

SageMaker 还允许您通过在多个 GPU 上分配训练来更快地训练模型。为了帮助您更快更便宜地训练您的模型,我们在 AWS 的团队开发了 SageMaker 分布式数据并行(SMDDP) 库,以实现接*线性的缩放效率最少的代码更改。SMDDP 通过利用 AWS 的专业网络基础设施和 Amazon EC2 拓扑信息来执行优化的节点间通信,以加快您的分布式培训工作量。我们已经发表了一篇论文来描述 SMDDP 的内部设计和背后的科学原理,供您参考。

在这篇文章中,你将看到与 Horovod 相比,SMDDP 如何帮助你在 EfficientNet 的训练中获得高达 30%的速度,efficient net 是一种用于计算机视觉任务的最先进的模型。我们将首先对 EfficientNet 和 SMDDP 进行概述,然后我们将一步一步地指导您如何修改现有的 EfficientNet 代码,这些代码使用带有 TensorFlow 的 Horovod 来替代使用 SMDDP。最后,我们将通过查看一些性能测量来帮助您了解 SMDDP 的优势。到本帖结束,你应该能使用 SMDDP 加快自己模型的训练速度了!

EfficientNet 概述

卷积神经网络的一个关键挑战是扩展网络,即增加模型参数的数量以获得更高的精度。扩展 CNN 的常用策略是开发具有更高层数的更深模型。事实上,多年来, ImageNet 大规模视觉识别挑战赛(ILSVRC) 的获奖作品毫不奇怪地采用了更深层次的 CNN,AlexNet 在 2012 年使用了 8 层,ResNet 在 2015 年使用了 152 层。

然而,以这种方式放大 CNN 是乏味的,需要大量的微调和实验来达到具有所需精度和资源要求的网络。谷歌的研究人员在他们的 ICML 的论文中解决了这个问题,他们开发了一种放大 CNN 的原则性方法,他们称之为复合缩放。复合扩展的关键在于神经网络可以在三个维度上扩展

  1. 深度:增加网络的层数,这是 ResNets 中使用的主要缩放方法。
  2. 宽度:增加单层中神经元的数量,或者更具体地说,增加卷积层中使用的滤波器的数量。
  3. 分辨率:增加输入图像的宽度和高度。

EfficientNet 模型中使用的复合缩放。图片来自 EfficientNet:反思卷积神经网络的模型缩放,ICML 2019

复合缩放本质上是以一个恒定的比率沿着上述三个维度均匀地缩放网络,作者称之为复合系数ɸ.通过使用更大的复合系数,可以生成更精确且计算成本更高的模型。

虽然作者表明复合扩展可以普遍应用于任何基线网络架构,但复合扩展的效率受到基线网络架构选择的严重影响。为此,作者利用神经架构搜索构建了一个最优网络架构 EfficientNet-B0 。该基线网络的主要构建模块是 MobileNetv2 中使用的移动反向瓶颈模块。EfficientNet-B0 在只有 530 万个参数的 ImageNet 上实现了 77.1%的准确率。相比之下,ResNet-50 提供了 76%的准确性,但使用了 5 倍数量的参数。这使得 EfficientNet 成为自动驾驶汽车和安全系统等需要较低计算开销和高精度的系统的首选。此外,可以使用复合缩放来缩放 EfficientNet 以获得更高的精度,从而产生 EfficientNet-B1 至 EfficientNet-B7,该整数位于名称的末尾,表示复合系数ɸ 你可以在这篇详细博客中读到更多关于 EfficientNet 的技术细节。

分布式数据并行训练和 SMDDP

这些年来,可用于训练模型的训练数据量一直在增长,并且将来还会继续增长。例如,在这篇文章中,我们使用 ImageNet 数据集训练 EfficientNet,该数据集有超过一百万个训练图像。根据我们与 AWS 客户合作的经验,训练数据的大小可能会大得多,因为训练作业通常会使用超过 1000 万到 1500 万张训练图像!有了如此大的训练数据,在单个 GPU 上运行单个时期(整个训练数据集的一个完整周期)的训练的时间增加了,使得训练非常长,并且不符合业务需求。

人们可以通过使用多个 GPU 并利用一种称为数据并行的技术来减少训练时间。数据并行工作流程如下-

  1. 驻留在每个 GPU 上的工作者进程具有其自己的模型副本,并且训练数据在工作者之间被分割。
  2. 对于每个工人,我们在相应的训练数据片段上运行一次迭代训练,并使用反向传播算法计算梯度。
  3. 在每次迭代结束时,所有工人使用 AllReduce 算法交换本地计算的梯度,并计算全局*均梯度,然后用于更新模型的本地副本。

AWS 上的数据并行分布式深度学习。照片摘自 AWS 文档sage maker 分布式数据并行库介绍

在分布式培训中,我们上面看到的所有减少步骤都涉及到通过网络在工人之间进行梯度交流。对于像 EfficientNet 这样拥有超过一百万个参数的先进模型,通过网络交换梯度会导致大量的通信开销,因为大型模型梯度会在实例之间争夺有限的网络带宽。除了降低训练速度之外,通信开销还限制了分布式数据并行训练的可扩展性。

理想情况下,难道我们不都喜欢线性缩放效率,其中训练速度与用于训练的 GPU 数量成比例地提高!通信开销成为实现线性扩展效率的障碍,并导致昂贵的 GPU 资源未得到充分利用。

我们在 AWS 的团队已经认识到这个关键问题,并开发了 SageMaker 分布式数据并行库(SMDDP)来提供接*线性的扩展效率,以最少的代码更改实现更快的训练速度。该库利用 AWS 基础设施,如弹性纤维适配器 (EFA)和亚马逊 EC2 拓扑信息来实现定制的优化 AllReduce 算法。SMDDP 还使用 CPU 而不是 GPU(其他通信库,如 NCCL 只使用 GPU)来执行 AllReduce,提供更多的 GPU 周期来计算梯度。这允许反向传递和梯度传递之间有更大程度的重叠,从而减少训练时间。要深入了解定制 AllReduce 算法,请参考我们关于 SageMaker 分布式数据并行性的出版物

使用 SMDDP 培训效率网络

在这篇博文中,我们将派生出 NVIDIA 提供的 EfficientNet 的多 GPU 实现,该实现使用 Horovod 和 TensorFlow。在此基础上,我们将需要对进行微小的代码更改,利用 AWS SMDDP 库而不是 Horovod。SMDDP 有一个类似于 Horovod 的 API 规范。这使得熟悉Horovod API的用户可以直接采用 horo VOD 培训脚本来使用 SMDDP。为了您的方便,我们已经在我们的 SMDDP-Examples GitHub 资源库中发布了完整的培训脚本。下面,我们将带您大致了解所需的主要变化。

  1. 导入 SMDDP 的 TensorFlow 客户端而不是 Horovod 的 TensorFlow 客户端并初始化。
# Import SMDDP client instead of Horovod's TensorFlow client
# import horovod.tensorflow as hvd
import smdistributed.dataparallel.tensorflow as sdp# Initialize the SMDDP client instead of Horovod client
# hvd.init()
sdp.init()

2.EfficientNet 培训脚本使用 Horovod 提供的rank() API 来获取 worker 的全局等级(逻辑全局进程号)。对数据集进行分片、对模型进行检查点操作以及记录性能指标的一些操作都需要这样做。下面是一个例子。

# Checkpoint only on rank 0
# Replace hvd.rank() calls with sdp.rank() as illustrated below
# if model_checkpoint and hvd.rank() == 0:
if model_checkpoint and sdp.rank() == 0:
  ckpt_full_path = os.path.join(model_dir, 'model.ckpt-{epoch:04d}')
  callbacks.append(tf.keras.callbacks.ModelCheckpoint(
      ckpt_full_path, save_weights_only=True, verbose=1,
      save_freq=save_checkpoint_freq))

3.将优化器包装在 SMDDP DistributedOptimizer类中,而不是 Horovod DistributedOptimizer类中。

# Replace Horovod's DistributedOptimizer class with SMDDP's DistributedOptimizer
# optimizer = hvd.DistributedOptimizer(optimizer,
                  compression=hvd.Compression.fp16)
optimizer = sdp.keras.DistributedOptimizer(optimizer,
                compression=sdp.Compression.fp16)

4.训练脚本使用 Keras 回调将来自领导者等级(等级0)的初始模型变量传播给所有其他工作人员。用 SMDDP 的回调 API 替换 Horovod 的回调 API。

# Replace Horovod's BroadcastGlobalVariablesCallback callback with
# SMDDP provided BroadcastGlobalVariablesCallback callback
# callbacks=[hvd.callbacks.BroadcastGlobalVariablesCallback(0)]
callbacks=[sdp.keras.callbacks.BroadcastGlobalVariablesCallback(0)]

5.训练脚本使用allreduce()调用,特别是在验证阶段,来分发训练好的模型评估,并收集统计数据,如准确性。用 SMDDP 的oob_allreduce()(带外 AllReduce)调用替换 Horovod 的allreduce()调用。注意,SMDDP 同时提供了allreduce()oob_allreduce()API。allreduce() API 必须仅用于梯度张量。对于统计等非梯度张量,使用oob_allreduce() API。

# Replace Horovod's allreduce() call with SMDDP's oob_allreduce() call.
# SMDDP's oob_allreduce() does an average reduce operation by default.
# stats['training_accuracy_top_1'] = float(hvd.allreduce(tf.constant(
# train_hist['categorical_accuracy'][-1], dtype=tf.float32),
    average=True))
stats['training_accuracy_top_1'] = float(sdp.oob_allreduce(tf.constant(
  train_hist['categorical_accuracy'][-1], dtype=tf.float32))

在 SageMaker 上使用 SMDDP 训练 EfficientNet

现在我们已经修改了 EfficientNet 培训脚本以使用 SMDDP,接下来我们继续在 Amazon SageMaker 上培训 EfficientNet。为了您的方便,我们开发了一个详细的示例笔记本,带您完成 SageMaker 上培训 EfficientNet 的整个过程。我们建议启动一个 SageMaker 笔记本实例来运行示例笔记本,而无需做任何设置。以下是一些最重要步骤的概述。

  1. 准备 ImageNet 数据集作为 TFRecords 的集合。TFRecords 是包含定型数据的二进制记录序列。它使用 Google 的协议缓冲区格式进行序列化。您可以按照步骤下载 ImageNet 数据集并将其转换为 TFRecords 格式,然后将其上传到亚马逊 S3 桶。对于像 ImageNet 这样的大型数据集,我们建议使用 Amazon FSx 作为您的文件系统。FSx 文件系统大大减少了 SageMaker 上的培训启动时间,因为它避免了每次启动培训作业时下载培训数据(就像 SageMaker 培训作业的 S3 输入一样)。FSx 还提供了更好的数据 I/O 吞吐量。示例笔记本包含创建 FSx 的步骤,该 FSx 与保存 ImageNet TFRecords 的 S3 存储桶相关联。
  2. 默认情况下,SageMaker 使用最新的亚马逊深度学习容器(DLC) 图像进行训练。示例笔记本有一个脚本,它使用 TensorFlow 2.6 的 DLC 作为基础映像,安装基于英伟达 NGC 容器训练 EfficientNet 模型所需的附加依赖项,并将定制的 Docker 容器推送到亚马逊 ECR 。使用自定义 Docker 容器的图像 URI,您可以在下一步中构造一个 SageMaker 估计器。
  3. 使用 SageMaker Python SDK 提供的 SageMaker 估计器类来启动一个训练任务。estimator 类允许您配置参数来指定要使用的 Docker 映像、实例的数量和类型以及超参数。参见以下设置 SageMaker TensorFlow 估算器的示例。
import sagemaker
from sagemaker import get_execution_role
from sagemaker.estimator import Estimator
import boto3sagemaker_session = sagemaker.Session()# Configure the hyper-parameters
hyperparameters = {
    "mode": "train",
    "arch": "efficientnet-b4",
    "use_amp": "",
    "use_xla": "",
    "max_epochs": 5,
    "train_batch_size": 64,
    "lr_init": 0.005,
    "batch_norm": "syncbn",
    "mixup_alpha": 0.2,
    "weight_decay": 5e-6
}

estimator = TensorFlow(
    entry_point="main.py",
    role=role,
    image_uri=docker_image, # name of docker image uploaded to ECR
    source_dir="./tensorflow/efficientnet",
    instance_count=2, # number of instances
    instance_type="ml.p4d.24xlarge", 
    # Other supported instance types: ml.p3.16xlarge, ml.p3dn.24xlarge
    framework_version="2.6", # TensorFlow 2.6
    py_version="py38",
    sagemaker_session=sagemaker_session,
    hyperparameters=hyperparameters,
    subnets=["<SUBNET_ID>"],
    # Should be same as Subnet used for FSx. Example: subnet-0f9XXXX
    security_group_ids=["<SECURITY_GROUP_ID>"],
    # Should be same as Security group used for FSx. sg-03ZZZZZZ
    debugger_hook_config=False,
    # Training using SMDataParallel Distributed Training Framework
    distribution={"smdistributed": {"dataparallel": {"enabled": True}}},
)# Submit SageMaker training job
# data_channels is the FSx input
estimator.fit(inputs=data_channels, job_name=job_name)

性能比较

我们比较了 SMDDP 和 Horovod 在 SageMaker 上训练效率网的性能。我们使用多个 ml.p4d.24xlarge 实例进行训练。每个 ml.p4d.24xlarge 实例配有 8 个 NVIDIA A100 GPUs,并具有 400 Gbps 实例网络,支持 EFA 和 GPUDirect RDMA (远程直接内存访问)。我们根据 NVIDIA DeepLearningExamples 存储库中提供的脚本选择训练超参数,如批量大小和时期数。注意,使用 Horovod 和 SMDDP 对相同数量的历元进行训练将产生相同的参数集,因为库仅编排梯度的通信。我们展示了以下两种 EfficientNet 变体的性能结果:具有 530 万个参数的 efficent net-B0 和具有 1900 万个参数的 efficent net-B4。

NVIDIA A100 GPUs 支持用自动混合精度 (AMP)训练。SMDDP 支持开箱即用的放大器,当 FP16 中产生梯度时,SMDDP 会在 FP16 模式下自动降低梯度。当使用 AMP 训练 EfficientNet-B0 时,我们可以观察到,与 Horovod 相比,SMDDP 提供了高达 25%的性能提升。当使用 8 ml.p4d.24xlarge 实例时,Horovod 的缩放效率下降到 94%,而 SMDDP 能够保持 97%以上的缩放效率。

SMDDP 与 Horovod for EfficientNet-B0 的性能比较。图片作者。

XLA(加速线性代数)用于训练 EfficientNet-B0 时,我们注意到 SMDDP 相对于 Horovod 的性能收益下降到 7%左右。Horovod 和 SMDDP 等数据并行库使用的一个关键设计方面是使用反向传播将生成梯度的通信与梯度的计算重叠。实际上,这隐藏了高通信开销并提高了性能。由于 XLA 融合了 GPU 内核来优化性能,数据并行训练的一个意想不到的后果是,它减少了重叠计算和通信的机会。我们建议 ML 科学家和开发人员在使用和不使用 XLA 编译的情况下评估训练性能,以确定特定模型的最佳选择。

SMDDP 与 Horovod for EfficientNet-B0 的性能比较与 XLA 一起训练。图片作者。

我们观察到使用含 SMDDP 的 EfficientNet-B4 的类似结果,其性能比 Horovod 高约 16%,并且具有更好的扩展效率。

SMDDP 与 Horovod for EfficientNet-B4 的性能比较。图片作者。

然而,当 XLA 用于训练 EfficientNet-B4 时,SMDDP 的性能收益比 Horovod 提高了* 30%。

SMDDP 与 Horovod for EfficientNet-B4 的性能比较与 XLA 一起训练。图片作者。

结果表明,与 Horovod 相比,SMDDP 可以实现高达 30%的训练吞吐量改进。这意味着您可以训练您的模型更快地收敛,并减少使用如此昂贵的 GPU 资源的计费时间。最重要的是,只要对我们前面提到的培训脚本进行简单的修改,所有这些都是可能的。

结论

在这篇博客文章中,您了解了如何使用 SageMaker 的分布式数据并行(SMDDP)库来加速和扩展 EfficientNet 的训练,这是一种用于计算机视觉任务的最先进的神经网络架构。SageMaker 和 SMDDP 简化并加快了模型的训练,使 ML 科学家和开发人员能够更快地创新。我们介绍了如何修改现有的 EfficientNet 培训脚本,以采用 SMDDP,只需修改几行代码,就可以实现高达 30%的性能提升。

我们还有其他几个 PyTorchTensorFlow 的例子,供你进一步使用 SMDDP。我们也鼓励您利用您在这里学到的知识,使用 SMDDP 来加速我们自己模型的训练。如有任何问题或反馈,请联系我们,您可以在 SMDDP-Examples GitHub 资源库中提出问题。

用摩丁加速你的熊猫工作流程

原文:https://towardsdatascience.com/speed-up-your-pandas-workflow-with-modin-9a61acff0076

利用分布式计算的力量

克里斯·利维拉尼在 Unsplash 上的照片

支持摩丁的数据科学初创公司 Ponder 最*宣布了 700 万美元的种子投资。看起来他们在可扩展数据科学方面有着光明的未来。因此,我想给摩丁一个机会,和熊猫做一些比较。

Pandas 是 Python 的一个数据分析和操作库。考虑到典型数据科学工作流中的大部分时间都花在数据清理、操作和预处理上,Pandas 可能是数据科学生态系统中使用最广泛的 Python 库。

Modin 是另一个 Python 库,可以加速 Pandas 笔记本、脚本或工作流。它的逻辑很简单:它同时分配数据和计算。Modin 沿两个轴对数据帧进行分区,因此它在分区矩阵上执行。

(图片由作者提供)

关于摩丁最好的事情之一是你不需要学习新的语法。它提供了与熊猫代码的完全兼容性。您需要做的只是在导入库时更改一行。

我们将首先创建一个包含 1000 万行的示例数据帧,并将其保存为 csv 格式。然后,我们将对熊猫和摩丁进行常规操作。

让我们从导入熊猫和创建数据帧开始。

import numpy as np
import pandas as pddf = pd.DataFrame(np.random.randint(1,100,size=(10**7,50)))
df = df.add_prefix("column_")
df["group"] = ["A","B","C","D"]*2500000df.shape**# output**
(10000000, 51)

数据帧包含 1000 万行和 50 列,整数值在 1 到 100 之间。我还添加了一个分类列来测试 groupby 函数。

我们现在可以将该数据帧保存为 csv 文件。

df.to_csv("large_dataset.csv",index=False)

csv 文件的大小为 1.47 GB。

(图片由作者提供)

我们现在有了“大型”数据集。是时候做手术和计时了。我有一台装有 M1 芯片的 MacBook Pro 2000。您在机器上测量的时间可能略有不同,但您会发现与熊猫相比,摩丁有所改进。

我们要做的第一个操作是读取 csv 文件。

**# Pandas**
import pandas as pd%%time
df_pandas = pd.read_csv("large_dataset.csv")CPU times: user 20.5 s, sys: 5.44 s, total: **26 s**
Wall time: 28.6 s

用熊猫看文件用了 26 秒。

**# Modin**
import moding.pandas as pd%%time
df_modin = pd.read_csv("large_dataset.csv")CPU times: user 4.85 s, sys: 1.81 s, total: **6.66 s**
Wall time: 44.3 s

使用 Modin,我们能够在 6.66 秒内读取相同的文件,这意味着提高了 74%。

虽然我们可以使用相同的语法应用相同的操作,但是“df_pandas”和“df_modin”的类型是不同的。

type(df_pandas)
pandas.core.frame.DataFrametype(df_modin)
modin.pandas.dataframe.DataFrame

我们要做的下一个操作是过滤数据帧。

**# Pandas**%%time
df_filtered = df_pandas[df_pandas.group.isin(["A","B"])]CPU times: user 790 ms, sys: 1.35 s, total: **2.14 s**
Wall time: 2.92 s

我们选择组值为 A 或 b 的行。对于 Pandas,该操作耗时 2.14 秒。

**# Modin**%%time
df_filtered = df_modin[df_modin.group.isin(["A","B"])]CPU times: user 1.83 s, sys: 341 ms, total: **2.17 s**
Wall time: 3.15 s

我们看不到任何改善。事实上,熊猫在这个操作上比摩丁略快。

注:摩丁用的是雷或者 Dask 发动机。文档中解释了如何选择其中之一。

数据处理中的另一个常见任务是组合多个数据帧。让我们用 concat 函数将过滤后的数据帧和原始数据帧结合起来做一个例子。

**# Pandas**%%time
df_combined = pd.concat([df_pandas, df_filtered])CPU times: user 1.41 s, sys: 2.73 s, total: **4.14 s**
Wall time: 5.99 s**# Modin**%%time
df_combined = pd.concat([df_modin, df_filtered])CPU times: user 580 ms, sys: 622 ms, total: **1.2 s**
Wall time: 4.02 s

时间从 4.14 秒下降到 1.2 秒,提高了大约 71%。在这个任务中,摩丁比熊猫快多了。

让我们也用 groupby 函数做一个例子。下面的代码片段计算每个组的*均 column_1 值。

**# Pandas**%%time
df_pandas.groupby("group")["column_1"].mean()CPU times: user 405 ms, sys: 122 ms, total: **527 ms**
Wall time: 524 ms**# Output** group
A    50.033868
B    49.989013
C    50.009207
D    49.975092
Name: column_1, dtype: float64

熊猫需要 527 毫秒。

**# Modin**%%time
df_modin.groupby("group")["column_1"].mean()CPU times: user 536 ms, sys: 166 ms, total: **702 ms**
Wall time: 3.4 s**# Output** group
A    50.033868
B    49.989013
C    50.009207
D    49.975092
Name: column_1, dtype: float64

在这个例子中,Pandas 执行 groupby 操作的速度比 Modin 快。根据 Modin 的文档显示,groupby 函数尚未针对所有操作进行优化。这可能是它在这一步不能打败熊猫的原因。

我们做了一些例子来比较熊猫和摩丁在 1.47 GB csv 文件上的性能。在读取文件和组合数据帧方面,Modin 优于 Pandas。另一方面,Pandas 在使用 groupby 进行过滤和聚合时的表现优于 Modin。

我认为,随着数据量的增加,摩丁和熊猫之间的性能差异将变得更加明显。另一个将揭示与熊猫相比摩丁速度的要点是集群的数量。我在一台机器上(例如我的笔记本电脑)完成了这些示例。在多个集群上使用更大的数据集进行这些示例可能是更准确的比较。

你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果你已经是了,别忘了订阅如果你想在我发表新文章时收到电子邮件。

*https://sonery.medium.com/membership

感谢您的阅读。如果您有任何反馈,请告诉我。*

使用 Streamlit 加速 Python Web 应用程序的创建

原文:https://towardsdatascience.com/speed-up-your-python-web-app-creation-with-streamlit-b0ee0026eacc

学习机

不再摸索 HTML 页面

照片由 Shiro hatoriUnsplash 上拍摄

你能为这个功能制作一个演示吗?"我看到屏幕上弹出的延迟通知气泡。是我的经理发来的。

好吧,你明白了。你以前用 Flask 做过演示;您有一个可以从中复制模板的 GitHub repo。

链接到 GitHub repo 我总是用它作为那些好奇的人的烧瓶模板。

“当然,应该在星期一之前完成。”那天是星期四,所以我将有一天半的时间来复制模板中的所有内容,刷新我的记忆,并完成演示。另外,万一我惨败,我会有周末作为紧急后备计划,对吗?

“太好了,我推荐使用 Streamlit,这样你就不用处理 HTML 和 CSS 了。”我对这条信息的反应是竖起大拇指,表示我们的谈话结束了。

Streamlit 到底是什么?我开始恐慌,然后谷歌了一下。

快进到周一,我奇迹般地不用 Flask 就完成了演示页面。以下是我学到的。

烧瓶与细流

当你第一次听到一个新的图书馆时,你做的第一件事是什么?没错,你谷歌一下,我就是这么做的。第一个结果是 streamlit.io ,我点击了它。

作者截图自 streamlit.io

他们说我“不需要前端经验”

我之前提到的那个烧瓶模板?我的 SO 帮助我创造了它。她也是一名数据科学家,但不像我,她实际上可以很好地进行 web 开发。

自从我有了那个模板,我所有的演示页面看起来都是这样的。

作者的烧瓶应用程序截图

当然,我必须根据我需要显示的数据对布局进行一些编辑,但是您已经了解了要点。

任何使用过 Flask 的人都会知道,仅仅为了在网页上显示一个值而不得不编辑 Python 文件和 HTML 页面的痛苦。更不用说那些从用户那里获取输入的烦人的表单了。

下面是记录文本框输入的一段代码。

这是显示结果的部分。

重读代码让我回想起必须调试代码的所有后续部分,因为当我想显示更多项目时,我太骄傲了,不会向我的 SO 寻求帮助。

幸运的是,Flask 使用 Jinja2 语法({% ... %}部分中的语法),这让我更容易控制页面的流动。

无论如何,我尝试在 Streamlit 中重新创建页面,下面是页面的样子。

Streamlit 页面截图

我认为这是主观的,但我喜欢页面中等风格的干净设计。你甚至可以通过页面右上角的汉堡菜单把它改成深色主题。

这是代码。

就我个人而言,我非常喜欢如何通过纯 Python 代码创建网页。最重要的是,我相信任何人都希望不必处理任何单独的 HTML 和 CSS 文件。

Streamlit 入门

我会推荐你在安装 Streamlit 之前使用 Miniconda 。之后,安装 Streamlit 就非常容易了。

pip install streamlit

成功安装 Streamlit 后,您可以直接运行以下命令。

streamlit hello

在您的浏览器中打开localhost:8501,您将看到此页面。

Streamlit 的 Hello 页面截图

hello 页面包含一些演示模板,您可以进行试验。它还包括演示底部的实际代码,以便您可以实际使用它作为您自己的演示的基础。

Streamlit 演示页面示例

Streamlit 也有自己的图库,包含用户创建的演示页面,以及每个页面的源代码链接。如果您需要创建一个您认为其他人可能已经做过的通用演示页面,这是一个非常好的方法。

简化基础知识

如果您访问 Streamlit docs ,您会很容易被文档中的所有元素和页面所淹没。

对我来说,我急于把事情做完,所以我专注于做两件事。

  1. 获取输入
  2. 显示输出

获取输入

Streamlit 有一组输入小部件来帮助从用户那里获得输入。你可以选择从一个简单的文本框到相机输入和任何介于两者之间的东西。

Streamlit 输入部件的屏幕截图

对于像下载按钮这样的东西,你可以通过点击一个按钮来下载一个数据帧或者一个 JSON 文件,想象一下使用 Flask 来实现会有多复杂。或者类似于滑块,或者日期输入

从那以后,我使用 Streamlit 创建了多个演示页面,我从未遇到过需要特定类型的输入而 Streamlit 没有小部件支持的问题。

显示输出

由于 Jinja2,使用 Flask 打印变量非常容易,但是 Streamlit 使得显示文本数据更加容易。

使用 Streamlit 时,我最喜欢的事情是显示数据帧。

Streamlit 有一个叫做 magic 的东西,你可以通过简单地调用变量名来打印出数据帧和 matplotlib 图。

紧随其后的是 st.metric ,我们可以用增量显示指标,这使得创建仪表板变得非常容易。

细流的公制屏幕截图

我对 Streamlit 的输出有一个抱怨,那就是试图打印居中的文本时。

不幸的是,在撰写本文时,Streamlit 还没有对类似st.write(..., align="center")的东西的原生支持

我最终编写了自己的自定义函数,只是为了在 Streamlit 中将文本居中。

优化流线

在这一点上,在弄清楚这两件事之后,我的演示已经开始运行了。然而,我注意到演示页面没有任何保存的状态,实际上运行每个输入的所有代码。

幸运的是,我找到了两者的解决方法。

会话状态

它在文档中被列为状态管理,但我们将在代码中使用st.session_state。基本上,它是一个字典,可以为每个用户会话存储各种值。

简单地像下面这样使用它。

st.session_state["key"] = value

如果您想存储用户输入或任何需要在整个演示页面流程中保存的变量,这真的很有帮助。

贮藏

在 Streamlit 中缓存对于数据科学家来说至关重要。

当你为一个机器学习模型创建一个演示时,加载模型通常会花很长时间。模型越大,载入内存的时间就越长。

Streamlit 的问题是,每次收到输入时,它都会运行所有代码。包括我们加载模型的部分。

为了避免这个问题,我们需要在加载模型的函数上添加st.cache装饰器。这将使页面将模型保存在缓存中,直到函数的名称、函数内部的代码或函数的输入参数发生变化。

对于装饰者,我通常使用以下配置。

@st.cache(show_spinner=True, max_entries=1)

当功能运行时,使用show_spinner=True将给出一个旋转动画。添加max_entries=1会将保留的条目数限制为 1。当您在云服务器上部署演示页面以限制内存使用时,这最后一部分至关重要。

收场白

周一早上,我向经理展示了演示页面。

“哦,你是用 Streamlit 创建的?我记得你说过你从来没用过?”

“是啊,我很好奇,它似乎真的很有趣,所以我决定尝试一下。”我回答道。

“你只花了两天时间就学会并编写了代码?干净利落。让我们将它部署到 GCP,这样我们就可以让团队的其他成员试用它了。”

“我实际上花了周四学习并试图创建一个工作演示页面,周五实际创建一个工作演示页面,周末*滑流程,包括会话状态和缓存。”

当然不是我打的。

"当然,我会把它部署在 GCP,完成后会给你链接."已发送。

我整个周末都在学习 Streamlit 吗?是的。

我后悔吗?绝对不行。

学习机是一系列关于我所学到的,并且认为足够有趣可以分享的事情的故事。有时也是关于机器学习的基础。 定期更新 新故事和 成为中等会员 阅读无限故事。

https://chandraseta.medium.com/membership

通过部署这些采样策略来加速您的模拟

原文:https://towardsdatascience.com/speed-up-your-simulations-by-deploying-these-sampling-strategies-372993703ec5

更有效抽样随机变量的六种策略。了解对偶变量、重要性抽样、矩匹配、控制变量、分层抽样和准随机序列。

勒内·波特在 Unsplash 上拍摄的照片

尽管许多现实世界的系统(及其所有的不确定性)都可以被公式化为数学问题,但我们通常缺乏获得解析解的能力。即使是对现实的半现实的描述也包含许多不确定性的来源,不一定来自众所周知的理论分布。

通常,我们求助于蒙特卡罗模拟,从随机分布中抽取大量样本,并依靠中心极限理论来估计系统的真实值。然而,从数学上来说,这种*似只适用于无限多的样本。本文为那些没有无限时间的人讨论了六种抽样策略。

蒙特卡罗模拟的缺点

图片由 Nic RosenauUnsplash 上拍摄

模拟可以是系统的静态表示(例如,工厂布局),但通常也嵌入学习和优化的方面(例如,强化学习)。蒙特卡罗模拟提供了一个非常丰富的工具箱,但是它的主要缺点是它的 效率低下

假设你想确定用骰子(一个非常基础的模拟模型)投出的眼睛的*均数。如果你重复实验 100 次,你可能会在 3.2 和 3.8 之间结束——与 3.5 的真实*均值相比,误差相当大。重复 1000 次可能会得到 3.4 到 3.6 之间的值——更好,但也不会过于精确。

如果需要数千次重复来*似一个简单骰子的值,可以想象需要数百万甚至数十亿次重复来精确估计服从多重随机分布的随机系统的相关值。考虑到运行环境本身也需要计算工作(考虑生产车间或风力发电场建模),计算问题变得显而易见。

幸运的是,我们通常可以比随机采样做得更好,大大减少计算负担以达到一定的精度水*。本文讨论了一些采样策略,它们可以在蒙特卡罗模拟和强化学习等环境中使用。

作为一个运行示例,我将使用模拟来为一个欧式看涨期权定价。对于那些不熟悉金融衍生品的人来说:在未来的某个特定日期(例如,一年后),这样的期权赋予了以预先确定的执行价格 K 买入股票的权利,如果股价 S 超过了行权日的执行价格(S > K),那么行使期权并锁定利润是有意义的。如果 S < K,行使期权是不理性的。因此,收益可以表示为 max(S-K,0)。(贴现的)期权价值等于预期收益,我们试图使用蒙特卡洛模拟来了解这一点。对于这个特殊的期权,我们可以使用解析解(Black-Scholes 方程)来验证价格,从而得出期权的真实价值。

对立变量

“你走低,我走高。”

在许多模拟环境中,极端情况在频谱的两边都很重要。一个太阳能公园在晴天会产生很多能量,在乌云密布的日子里会产生很少的能量。当股市暴涨时,我们赚了很多钱,当股市暴跌时,我们损失惨重。自然地,如果我们碰巧选择了大部分阳光明媚的日子和繁荣的市场,我们对现实的描述就会有所偏差。

对偶抽样背后的想法是成对抽取随机变量,其中一个抽取是另一个抽取的“反面”。考虑从均匀[0,10]分布中提取。如果我们随机抽样 1.3,我们也会抽样 8.7 来完成配对。如果我们画正态分布的第 64 个百分点,我们也画第 36 个百分点。

更正式一点:对于每个生成的路径【ϵ_1,ϵ_2,…,ϵ_m】,我们生成一个对偶部分【-ϵ_1,-ϵ_2,…,-ϵ_m】。除了方差减小的好处之外,生成ϵ在计算上可能是昂贵的——对立的对应物则提供了“额外的”观察。

示例时间。股票价格路径通常是基于从标准正态偏差(几何布朗运动模型)中抽取的回报样本生成的。如果我们使用对偶抽样,生成的价格路径对看起来像镜像:

价格路径示例。蓝色路径代表“原始”路径。橙色路径是对立的路径,形成了原始路径的镜像。[图片由作者提供]

通常,以这种方式采样会降低标准误差。当然,为了公*地比较标准 MC 和对偶 MC,我们需要相同数量的数据点 M :产生标准误差 σ_strd/sqrt(2M)σ_anti/sqrt(M)。

让我们在我们的期权例子上试试:

常规 MC 和对偶 MC 的定价比较。后者始终产生较小的标准误差[图片由作者提供]

的确:标准误差越小,结果越准确!

控制变量

在许多情况下,我们有一些解决方案,我们可以对比我们的模拟结果。对于一个类似的问题,我们可能有一个解析的解决方案,甚至只是一个抽样观察。这样的知识是有价值的,因为它可以用来减少样本的方差。

控制变量是我们引入到具有零均值的估计器中的任何一项。它们可以被安全地添加,因为它们不影响期望;然而,它们确实会影响方差

这听起来可能有点抽象,所以让我们快速地看一个例子。如前所述,Black-Scholes 公式是评估欧式期权价值的解析表达式。对于美式期权——可以在到期前的任何时候行使——没有解析解。然而,收益曲线非常相似;我们期望美式期权价值 v_A 至少等于欧式期权价值 v_E (这实际上是欧式期权的一个有限案例)。

我们可以使用模拟( v_A^MC )来评估美式期权,同时使用样本来评估具有相同执行价格和到期日的欧式期权。对于后者,我们可以测量模拟值 v_E^MC 和分析值 v_E 之间的误差。然后,我们假设相同的误差适用于 v_A^MC 和真实(未知)值 v_A

是时候插入一些数字了。

假设我们对美式期权的蒙特卡罗模拟产生了 v_A^MC=4.49。欧洲蒙特卡罗估计(使用相同的模拟价格路径)是 v_𝐸^𝑀C =4.32 ,布莱克-斯科尔斯公式得出 v_E=4.08 。因此,欧式期权价值有一个误差v _ e −v_e^mc= 0.24。

我们假设这个错误也适用于美式期权。然后,我们可以估计v_a=v_amc+v_e-v_emc=4.25。请注意,我们已经修正了最初估计的 4.49。由于模拟高估了欧式期权的价值,很可能我们也高估了美式期权——修正后的估计应该更准确。

重要性抽样

抽样时,我们可能只对观察结果的子集感兴趣— 需要干预的罕见事件、极端结果、几乎不会发生的例外...问题是这些罕见的事件是——你不会相信的——罕见的。如果一个事件只发生 1/10,000 次,你将不得不产生许多随机样本来获得一些观察结果。

举个例子,考虑一个深度价外的期权。只有当股价爆炸时,它才会足够高,期权才会有回报。假设这样的价格路径只以 0.1%的概率出现。对于随机抽样,*均来说,每 1000 次重复我们只能得到一次非零回报。我们可能需要数百万条路径来得到一个合理的估计,99.9%的模拟路径没有产生额外的见解。

重要性抽样通过只从条件概率分布中抽样来解决这个问题。对于遵循正态分布的模拟回报,我们只能从右边的尾部取样,从分析知识中知道我们只覆盖了整个分布的 0.1%。显然,我们必须修正我们忽略的许多零值结果(调整*均收益是很简单的)。

重要性抽样的直观表示。我们只从阴影区域取样,利用我们对高斯分布的了解

看看下面的例子。对于常规抽样,我们不生成任何价内路径,因此得出期权价值为 0 的结论。另一方面,重要性抽样产生了许多积极的回报。在转换为无条件分布后,我们看到价格相当接*真实值——对于随机样本,这将需要更多的重复!

常规抽样与重要性抽样,以评估深度价内期权。由于我们只对增长非常强劲的价格路径进行采样,重要性采样只需要很少的观察就能得出准确的价格估计[图片来自作者]

力矩匹配

在蒙特卡罗模拟中,我们经常从已知的理论分布中取样,例如正态分布。分布可以通过其(例如,*均值、标准偏差、偏斜度、峰度)来表征,这些是相应图形的定量属性。

您可能认为,如果您从正态分布中抽取样本,那么构建的样本集也是正态分布的。这种直觉适用于大量样本,但不幸的是不适用于小样本。

考虑下面的例子,我们从一个标准的正态分布中抽取 10 个样本。通过扩展,我们希望我们的样本分布也具有均值 0 和标准差 1。然而,快速计算表明情况并非如此。

来自标准正态分布的一组样本。尽管我们从 N(0,1)分布中提取数据,但由于样本量较小,样本集呈现出不同的均值和标准差[图片由作者提供]

幸运的是,修复非常容易。在这种情况下,我们只需减去样本均值(将均值重置为 0)并除以样本标准差(将其重置为 1)。注意,我们可以对偏度或峰度做同样的处理。

力矩匹配。我们基于期望的分布特性来变换原始样本。转换后,样本集反映了一个适当的标准正态分布[图片由作者提供]

转换后的样本反映了我们希望从中采样的真实分布,这加快了收敛速度。

分层抽样

从收敛的角度来看,从随机分布中足够频繁地取样是可靠的。然而,在实践中,抽样是非常有限的,这就引出了为什么抽样需要是“随机”的问题。相反,给定有限数量的样本,为什么不试着让尽可能好地覆盖分布

分层抽样探索了这一概念。假设我们从 1 到 10 之间的均匀分布中抽取 10 个样本。随机样本可能如下所示:

随机抽样:[7 10 5 6 9 8 7 2 5]

请注意,1、3 和 4 未显示,而 2、5 和 7 采样两次。接下来让我们考虑分层抽样:

分层抽样:[1 2 3 4 5 6 7 8 9 10]

我们简单地以与分布的概率质量成比例的方式分布我们的样本,提供分布的均匀覆盖。请注意:( I)我们需要知道分布情况,( ii)我们必须预先指定样本的数量,以便正确抽取样本。

这个过程最容易用均匀分布来形象化。下面分别是一维、二维和三维的例子。

随机抽样(左)与分层抽样(右)。请注意,对于相同数量的观察值,分层抽样提供了更均匀的抽样空间覆盖。[图片由作者提供]

对于正态分布,我们将从钟形曲线的中心(高概率质量)采样更多的点,并且仅从尾部稀疏地采样。换句话说,样本的密度与分布成正比。

下面的数字例子表明,对于有限的样本,分层抽样更加准确。在这种情况下,我们为一个只有 10 次复制的选项定价(这是一个极低的数字)。然而,结果是相当准确的。相比之下,随机抽样与真实值相差甚远。

一个选项的价值估计,常规蒙特卡罗和分层抽样都只使用 10 个样本。尽管样本数量非常少,分层抽样还是非常接*真实价格,而常规 MC 则完全不一样。[图片由作者提供]

准随机抽样

准随机抽样(也称为低差异抽样)非常类似于分层抽样,因为它旨在均匀覆盖理论分布。主要区别在于分层抽样依赖于预先确定的数量的观察值,而准随机抽样是顺序

如果我们能再做一次观察,哪一点会产生最深刻的见解?

在许多情况下,您对运行给定数量的迭代不感兴趣,而是希望运行足够的迭代来满足某个标准误差。你可能想要达到一个标准误差<0.1%, without knowing whether this requires 10,000 or 1,000,000 replications. In such cases, quasi-random sampling can 扩展序列直到达到期望的精度。

例如,假设我们想要生成介于 0 和 1 之间的样本。我们可以从中间开始,采样 0.5。左边和右边的“间隙”一样大,所以下一个样本可能是 0.25,然后是 0.75。之后,我们可能会在 0.25 和 0.5 之间、0.5 和 0.75 之间进行采样,等等。

有各种方法来填补分布中的“缺口”,每种方法都有相应的算法。例如,使用 scipy 可以如下生成 Sobol 序列:

**from** scipy.stats **import** qmc
>>> sampler **=** qmc**.**Sobol**(**d**=2,** scramble**=False)**
>>> sample **=** sampler**.**random_base2**(**m**=3)**
>>> sample
*array([[0\.   , 0\.   ],*
 *[0.5  , 0.5  ],*
 *[0.75 , 0.25 ],*
 *[0.25 , 0.75 ],*
 *[0.375, 0.375],*
 *[0.875, 0.875],*
 *[0.625, 0.125],*
 *[0.125, 0.625]])**scipy documentation:* [https://scipy.github.io/devdocs/reference/generated/scipy.stats.qmc.Sobol.html](https://scipy.github.io/devdocs/reference/generated/scipy.stats.qmc.Sobol.html)

2D Sobol 序列的例子,包含序列的前 10 个(红色)、100 个(红色+蓝色)和 256 个(红色+蓝色+绿色)元素[图片来自WikiMediabyJheald

虽然没有预定义的样本数,但对方差的影响与分层抽样非常相似。直觉仍然是为手头的分布获取一组有代表性的样本。

结束语

随机采样是无偏的,理论上可以保证无限重复下的收敛,但通常效率很低。我们可能会彻底探索没有增加新见解的问题区域,而忽略感兴趣的区域。

采样策略利用我们对问题结构的了解,转换样本,或者简单地尝试以更智能的方式覆盖底层分布。

所描述的技术可能并不总是有效。最关键的是,我们经常需要了解概率分布;事实上,我们经常依赖经验分布。

抛开局限性不谈:经验证明,采样策略通常可以大大加快模拟速度。当你时间紧迫,而你的算法似乎不收敛时,一定要试一试!

参考

赫尔,J. C. (2014 年)。期权、期货和其他衍生品。全球版。

  <https://en.wikipedia.org/wiki/Low-discrepancy_sequence>    <https://en.wikipedia.org/wiki/Stratified_sampling> 
posted @ 2024-10-18 09:34  绝不原创的飞龙  阅读(245)  评论(0)    收藏  举报