TowardsDataScience 博客中文翻译 2020(五百七十一)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

使用 nbQA 保持 Jupyter 笔记本的可维护性

原文:https://towardsdatascience.com/keep-your-jupyter-notebooks-maintainable-968c9a5e759c?source=collection_archive---------44-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Julia JoppienUnsplash 上拍摄的照片

我们数据科学家喜欢 Jupyter 笔记本:它们支持快速原型制作,让我们用代码讲述故事,并允许我们彻底探索数据集。然而,任何试图将 Jupyter 笔记本套件置于版本控制之下的人都会告诉你,它们真的很难维护。

数据科学家喜欢 Jupyter 笔记本电脑…

这有许多原因,例如:

  • 实现快速原型制作
  • 让你用代码讲故事
  • 允许您彻底探索您的数据集

…但是 Python 代码质量工具没有!

如果我们想使用以下任何一种优秀的工具:

  • [flake8](https://flake8.pycqa.org/en/latest/)(风格指南强制执行)
  • [pylint](http://pylint.pycqa.org/en/latest/)(建议重构,带来代码味道)
  • [mypy](http://mypy-lang.org/)(检查静态类型注释)
  • 还有很多很多…

然后,我们需要放弃我们非常喜欢的 Jupyter 笔记本,转而使用 Python 脚本。

我们能两全其美吗?

是啊!

[nbQA](https://github.com/nbQA-dev/nbQA)允许你在 Jupyter 笔记本上运行任何标准的 Python 代码质量工具。就像你通常在 Python 脚本上运行flake8一样:

my_script.py:1:1: F401 'pandas as pd' imported but unused

[nbQA](https://github.com/nbQA-dev/nbQA)允许您在 Jupyter 笔记本上运行它!

my_notebook.py:cell_1:1:1: F401 'pandas as pd' imported but unused

查看此演示,了解一些示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

用作提交前挂钩

如果你使用预提交,这里有一个例子,告诉你如何编写一个钩子,根据black格式化你的笔记本,检查它们是否遵循flake8,并用mypy对它们进行类型检查:

nbQA 还能做什么?

查看 nbQA 文档了解如何配置[nbQA](https://github.com/nbQA-dev/nbQA)以及它能做什么。

再也不要写不可维护的 Jupyter 笔记本了

如果您将[nbQA](https://github.com/nbQA-dev/nbQA)集成到您的工作流程中(参见文档中关于如何使用[pre-commit](https://pre-commit.com/)或在持续集成期间使用它的示例),那么您将能够保留 Jupyter 笔记本的所有优点,而不必牺牲它们的可维护性。

保持数据的包容性,同时不稀释您的结果

原文:https://towardsdatascience.com/keeping-data-inclusivity-without-diluting-your-results-27dc92c29171?source=collection_archive---------37-----------------------

性别案例研究:如何将数据公平纳入数据分析?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

假设您正在调查 10,000 人中的 100 人。你想分析你的 100 人样本的数据,以得到关于整个 10,000 人的可能行为和偏好的答案。

你项目的一部分关注性取向的公平。你不想遗漏任何人,而且你知道,在人们选择“异性恋或同性恋”的情况下,提出一个关于性取向的问题是不够全面的。你咨询了专家和当地社区,决定在这个问题中包括“异性恋、同性恋、女同性恋、双性恋、泛性或无性”选项。

一旦你的回答来了,你就有了来自这些类别的回答者的数据,然而只有少数回答者被确定为双性恋,只有一个人分别被确定为泛性和无性。当试图分析这些数据来代表所有这些取向的反应时,你意识到你从一些类别中获得的数据如此之少,以至于你不能说出关于它们的任何统计相关的东西,你不能从一个人的数据中推断出你的 10,000 人口中所有无性认同者的偏好和可能的观点。

与其完全忽略那些回答很少的类别,不如将它们合并到一个合并的类别中,这样可以更好地代表它们。当你发表你的发现时,你把你的结果框定为异性恋、同性恋和其他,这正是你试图避免的。人们对自己没有得到很好的代表感到愤怒和受伤,并觉得自己被归为“其他”一类。接受您调查的回答者感觉被欺骗了,因为他们被问到了您刚刚组合的详细问题。

这种数据类别的“崩溃”或“合并”一直在发生,不仅仅是性取向。几乎所有的人口统计问题都容易在调查中受到限制,或在分析中被压缩;种族、民族、性别、语言等。想象一下,在一项调查中列出所有可能的口语选项是多么困难,在统计学上又是多么无用。我们怎样才能做到包容而不使少数群体的类别变得如此之小,以至于只有多数群体的数据才具有统计相关性?

竞争优先事项:

  1. 尊重受访者的多样性非常重要。
  2. 重要的是你展示的结果在统计学上有意义。

选项 1:折叠

当在初步分析后将数据分类折叠成“异性恋/非异性恋”时,第一个伦理问题是,它将分类框定为异性恋是正常的,而其他人是“其他的”。第二,它以一种他们没有给自己分类的方式给你的回答者分类,去掉了你提供给他们的选择机构。当人们在面向公众的调查中使用大量的包容性类别,只是为了显得具有包容性,掩盖自己与公众的关系,同时计划以任何方式压缩数据时,最不道德的崩溃情况就会发生。

从数学的角度来看,将性取向分成两组是一个问题,因为你的结果会变得不那么准确。男同性恋、女同性恋或双性恋受访者之间的态度和行为可能会有很大差异,这一点很重要,需要衡量和承认。如果你只报道异性恋/非异性恋,你的结果会掩盖这一点。

选项 2:不崩溃

如果你有一堆数据类别,但只有少量的回答,这将降低你对总体人口的统计确定性。当一种身份有数百个回应,而另一种身份只有一个回应时,说类似“73%的异性恋和 88%的无性认同者赞成新法律”的话是不可接受的。你必须用他们的统计置信度来报告你的发现,这几乎总是对应于该类别中的回答者的数量。

因此,不折叠会导致一个问题,即只有大多数类别,即有大量响应的类别,才具有很强的统计意义。你的包容努力实际上削弱了最没有代表性的群体的声音。当然,在许多情况下,你只是在组之间有统计学上的置信度差异。如果您的回答者分三类,分别是 60%、30%和 10%,您仍然可以报告他们中的每一类,只包括他们统计权重的差异。

那么,该怎么办呢?当然,一如既往,答案是“视情况而定。”这取决于你试图回答的研究问题。这取决于和你一起工作的人需要知道什么。这取决于你收集数据的人对他们的代表性有什么感觉。这些只是其中的几个因素。

您可以做什么:

  1. 在制作调查之前和分析之前,决定如何处理这个*。*
  2. 以多种方式报告你的结果,包括折叠、未折叠和混合视角。
  3. 对你正在与你的数据团队、你的调查受访者和你的听众解决的困境、妥协和选择保持透明。

在创建调查或进行分析之前,在项目设计阶段决定如何处理这个问题是显著增加项目公平性的第一个方法。如果你决定最重要的是在最后有三个类别,有很强的统计可信度,你可以为此设计一个系统。假设您已经预先决定报告前三个类别:最多的回答者、第二多的回答者和所有剩余回答者的组合。这给了你很多优势。它将允许在调查中询问两个或三个以上的类别,从而增加包容性。它将允许你不假设那三个类别是什么;你不知道在所有的调查中,异性恋受访者永远会多于双性恋受访者。它可以让你告诉调查对象你打算如何分析数据,这样当你合并一些类别时,他们就不会感到不公平。在项目设计中有各种各样的方法来解决这个问题,包括包含什么问题,如何加权类别,如何报告类别,等等。提前决定你的项目系统和最佳实践将帮助你回避许多股权问题。

你不必只以一种方式报告你的发现。你可以用一种显示最强统计意义的方式来分解你的结果;也许双性恋者、无性恋者和女同性恋者确实对某个问题有同样的感觉,结合他们的数据会让他们的反应更有说服力。然后也可以单独显示所有类别,同时包括关于统计置信度的信息,以向您的受众显示您的调查确实包括了泛性取向,即使您没有得到该类别中的任何受访者。您可以创建一个混合体,其中一些类别以您认为最有意义的方式折叠。也许你报告了他们回答的交叉类别中的取向:最有可能说是:女同性恋、双性恋和泛性取向;最有可能说不:同性恋、异性恋和无性取向。向你的听众提供所有的信息增加他们对你的报告和方法的信心,同时强化你的结果。

最后,在这个问题上,你必须对项目的所有利益相关者保持透明:从事该项目的人,参与该项目的人,以及你的发现的观众。数据科学与人类在本质上充满了艰难的决策和妥协。不要精心设计调查,从回答者中去除代理,不要隐藏不同类别之间统计置信度的差异,不要隐藏你所做的假设和选择。这可能很难,因为你往往倾向于做这些事情来保护股权。让人们看到你是如何处理这样的问题的,只会增加信任和你的数据项目的真正公平。

原载于 2020 年 1 月 17 日 https://weallcount.com

“我们都算” 项目分享例子,构建工具,并提供旨在帮助更好地理解数据的培训和教育——以便我们可以使数据对每个人来说更加透明和公平。因为你做数学题的时候,我们都算。

保持我们数据的真实性

原文:https://towardsdatascience.com/keeping-our-data-stories-honest-eedacd5987e7?source=collection_archive---------40-----------------------

完美的数据故事是无聊的故事。它们也是糟糕的科学。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jr Korpa 在 Unsplash 上拍摄的照片

项目组和我遇到了一个问题。我们的客户在我们帮助优化的广告活动中投入了数百万美元。然后,我们实施了一项跟踪计划,看看这场运动是否奏效。很多事情都取决于它,现在结果出来了。

问题?确实有两个问题。第一个是,虽然这场运动对我们的大多数观众起了作用,但它在一个关键的州不起作用,我们不知道为什么。

另一个问题是,在其他地方,这场运动在也进行得很好。鉴于这场运动的时间跨度很短,其影响高得不合理。这似乎是一个好问题,但事实并非如此。如果我们不相信这些数字,为什么我们的客户会相信?

我们需要对正在发生的事情有一个解释,一个能解释这一切的故事。如果我们找不到,麻烦就在前面。

我们通常热情洋溢地谈论数据故事。我们讨论如何发现改变游戏规则的见解,如何使用出色的可视化技术让这些见解更容易被更广泛的受众所接受。

那都是好的和真实的。但是数据故事也有我们应该更多谈论的黑暗面。

它源于这样一个事实,在本质上,故事是谎言。他们有议程,即使他们有一个真实的核心。他们圆了角落,忽略了不方便的事实。

相比之下,好的数据科学是关于寻找真理的,不管它是什么。这是关于拥抱混乱和细微差别,关于接受有时数据不会产生一个答案,无论我们如何努力。

忽视数据科学和故事之间的紧张关系可能是危险的。它产生了基于糟糕的数据科学的简洁的小故事。为了产生这些数据,数据科学家忽略、合理化甚至隐藏不方便的数据。他们避免问尖锐的问题。这让他们和他们的观众离真相越来越远。

这不是假设的危险。在学术界、医学界和商界,这种情况时有发生。

问题是,那些被抹平的故事甚至都不是特别好。毕竟,数据科学家很少学习讲故事的艺术。就像刚起步的小说家写好人做好事一样,他们讲述了数据完全按照其应有的方式运行的故事。很无聊,毫无意义,也没教会我们什么。

我们如何才能成为更好、更诚实的数据故事讲述者?

作为数据科学家,真理是我们的朋友。它让我们和我们的观众走上了通往成功的最直路。这也造就了更好的故事。这就是为什么保持我们的数据故事的真实性非常重要。

以下几个步骤将帮助您讲述一个美丽而真实的数据故事:

1。先做好准备

你应该预料到杂乱的数据,你应该对此有一个计划。

在实践中,这意味着现在收集数据,以便以后帮助您以不同的方式分割返回数据。例如,对你的因变量进行多种测量通常是个好主意。这有助于你确定你观察到的模式是由真实的东西驱动的,还是由那个变量的定义驱动的。也会帮你讲更多微妙的故事。例如,程序没有改变 X,但它改变了 y。

你还应该收集你认为可能有重大影响的关键独立变量的数据。这似乎是显而易见的,但要做到这一点尤其困难。我参与了无数的调查,事后我非常希望我们刚才问了一个额外的问题。

2。让你自己和你的观众处于探索奥秘的心态中。

当我们想到阅读或观看一部推理小说时,我们想找出大的答案,但我们也期待一些松散的结局,一些在它结束后让我们继续好奇的东西。这是数据故事的完美心态。

在实践中,当在忙碌的高管面前演示时,你应该总是尽早清晰地阐述你的关键发现,但要在其中加入一些神秘感。例如:“我们发现 X 和 Y 细分市场最有可能购买您的产品。但是我们也注意到了一些奇怪的现象——Y 群体更有可能在工作日的早上购买你的产品。这让我们发现了一些东西,我们相信这可以显著改善您在 Y 细分市场的战略,几分钟后我们将向您展示这一点。”

3。分享你的过程

在演讲中,这不需要花费超过一两分钟的时间,但重要的是要设身处地地为你的听众着想,带他们一起踏上旅程。

例如:“我们对 Y 段的惊人观察使我们假设它是由变量 a 引起的。所以我们研究了它,但排除了它。但后来我们认为变量 B 可能是原因,我们确实发现变量 B 与 Y 群体在工作日早上购买你的产品的倾向密切相关。”

分享你的过程会带来几个真正有价值的好处。首先,它有助于重申你的严谨性,增加你的听众对你的发现的信任。第二,当他们和你一起经历这个过程时,你的观众自然会意识到你的工作不是什么外星火箭科学,而是他们也可以参与的事情。这将鼓励他们提出其他想法和解释,这将改进你的工作,并使整个过程更有吸引力和乐趣。毕竟,你的听众通常比你有更多的领域知识。

4.将遗留问题作为未来调查的机会

像一个真正的谜,有一些松散的结束是一件好事。数据科学是一个迭代过程,在这个过程中,每个答案都可能引发许多其他问题。让你的观众处于一种迭代的心态对他们有好处,对你有好处,也有利于让你的故事越来越接近真相。

回到我的团队,我们有几个选择来处理我们的两个数据问题。最简单的方法就是把它们扫到一边。如果我们无视自己的疑虑,我们就有了一个伟大的成功故事。这种异常状态可以被视为统计噪声。为什么要把事情复杂化?

但是,抛开严重的道德疑虑,讲述这个简单的故事很容易适得其反。如果下一波跟踪数据显示该活动并没有真正发挥作用呢?如果这些问题告诉我们一些重要的事情呢?

所以相反,我们接受了混乱。通过排除替代解释的过程,我们发现有证据表明,该活动确实像看起来那样有效——但只是在一些关键绩效指标(KPI)上,特别是在一些推动大部分改善的微观细分市场上。这为我们的客户提供了比简单的成功故事更有价值和可操作的信息。

至于那个竞选失败的异常状态,无论我们如何努力,我们仍然目瞪口呆。数据很奇怪。

直到我们和客户分享了我们的困惑。我们解释了如何着手试图理清这些混乱的数据。然后美好的事情发生了。客户的州主管沉浸在他所在州的日常现实中,他提供了两个我们通常不会考虑的解释。我们后来在数据中发现了支持这两种观点的证据。

我们以一个既好又诚实的故事结束了那场演讲。这不是一个完美的故事,它有一些松散的结尾,但这就是好的数据故事的全部。

保管东西。秘密

原文:https://towardsdatascience.com/keeping-things-secret-d9060c73089b?source=collection_archive---------45-----------------------

向黑客大军隐藏 API 密钥的指南。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由卢瑟·博特里尔(https://unsplash.com/photos/EsBufnuK4NE)拍摄

作为一名数据科学家或软件工程师,在你的职业生涯和个人项目中,你将不可避免地与 API(应用编程接口)进行交互。虽然许多 API 提供“免费增值”服务,允许用户每天免费请求一定数量的请求,但一旦超过这一限制,就会产生巨额账单。北卡罗来纳州立大学(NCSU) 完成的一项研究扫描了 13%的 GitHub 存储库,发现每天都有数千个新的独特代码被泄露。黑客不仅可以为他们不幸的目标积累荒谬的账单,他们甚至可以对国家安全构成威胁。NCSU 能够在一个西欧国家的主要政府机构的网站上找到 AWS 凭证。正如您在本文中可能想到的,我们需要一种方法来保持这些 API 凭证的秘密,同时仍然能够在线呈现我们的代码。

与编程世界中的大多数事情一样,这可以通过几种方式来实现。我将在本文中概述我最喜欢的方法,主要是因为它简单易用。此外,本教程是为使用 Unix 命令行(macOS 或 Linux)的人编写的,但稍加修改也可以在 Windows 操作系统上复制。

制作一个秘密文件夹

我们要保存这些密钥的位置是在我们主目录的一个文件夹中。首先导航到主目录并编写以下代码:

mkdir .secret

这将在主目录中创建一个名为.secret的新目录。“.”意味着这个文件夹是隐藏的,它不会显示在主目录中。如果你想检查它是否确实存在,你可以在主目录中输入ls -a,它会在你所有的其他文件中列出。

创建 JSON 文件

使用您选择的文本编辑器,我的偏好是 Sublime Text ,您现在需要创建一个 JSON 字典,其中包含您给定项目所需的所有 API 键。该结构的一个示例如下所示:

{
      "API_Key" : "<YOUR API KEY GOES HERE>"
  }

记得把这个文件保存在你的主目录下,扩展名为.json。这就是将文件定义为 JSON 的原因,与代码的格式无关。

将 JSON 文件移动到。秘密目录

在您的主目录中,编写以下代码,将 JSON 文件从主目录移动到我们刚刚创建的.secret文件中。

mv ~/<JSON FILE NAME> ~/.secret/<JSON FLE NAME>

从文件中提取 API 密钥

现在,在你的 python 脚本中,你需要定义一个使用json库来解码 JSON 文件的函数。

剩下的就是使用上面的函数,将文件路径作为参数。要访问该字典中的特定 API 键,只需选择相应的字典键,在本例中为“API_Key”。瞧,API 键现在作为它自己的对象存储在您的脚本中。

这是所有的乡亲。我希望这对你有帮助,在你编码的时候保持安全!

跟上数据工程:数据工程师资源指南

原文:https://towardsdatascience.com/keeping-up-with-data-engineering-a-resource-guide-for-data-engineers-3e4383ddfde2?source=collection_archive---------27-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

凯文·Ku 在 Unsplash 上的照片

今年早些时候,在 LinkedIn 2020 年新兴工作报告中,数据工程师的角色在美国新兴工作排名中名列第八——年招聘增长率为 33%。最近,Robert Half Technology 的薪酬指南将数据工程列为 2021 年薪酬最高的 IT 工作。根据 Robert Half 的说法,数据工程人才将继续获得高于平均水平的薪酬,因为组织将严重依赖“能够将大量原始数据转化为可操作信息以进行战略制定、决策和创新的个人”

随着对数据工程师需求的增加,数据团队必须准备好合适的工具和资源,以确保该角色保持高效的工作流程,而不是像最近的调查所引用的那样,将大部分时间花在现有系统的维护上。

必须承认,数据工程领域正在快速发展,不断有新的开源项目和工具发布,“最佳实践”也在不断变化,随着企业数据需求的增加,数据工程师比以往任何时候都更加捉襟见肘。不仅如此,数据工程师最近亲眼目睹了数据湖和数据仓库的混合,现代仓库通过将计算与存储分开来模糊二者的区别。这种混合已经将过去的基本“ETL”管道发展成更复杂的编排,经常是从仓库中读取数据和向仓库中写入数据。此外,随着数据科学家和数据分析师同事开始实施他们的工作,数据工程师需要与这些职能部门进行更多协作,并进一步赋予这些角色更多权力。由于所有这些压力和不断的变化,很难跟上这个空间(甚至进入它!).

从播客到博客,以下是资源综述——包括新数据工程师和经验丰富的专业人士——希望了解数据工程领域的最新动态。

数据工程播客

在这个领域,很难有比数据工程播客更贴切的了。每周一集,节奏很容易跟上。然而,跟上主题是另一回事。话题从公司讨论他们的数据架构和技术挑战到深入研究开源和付费产品。

我在下面强调了一些有趣的情节:

Presto Distributed SQL Engine:我们已经如此习惯于聚合数据以备查询,从另一个角度考虑这种模式很有启发性——联合查询执行以查询/组合数据所在的位置。听到这种方法的利弊有助于为数据工程师的工具箱添加另一个工具。

Jepsen: 传奇的 Jepsen 项目一直在深入研究分布式系统是如何设计、构建的,或许最重要的是,它们如何处理故障情况。虽然在堆栈中比普通的日常堆栈更深,但是更熟悉我们所依赖的和正在构建的分布式系统(当我们将多个不同的系统联系在一起时)是一件好事。

非盈利数据专家:本期节目的嘉宾是一家非盈利机构的数据基础设施总监,她分享了在预算严格的情况下处理大量数据源和合规性变化是多么具有挑战性——这是当今初创公司(或本案例中的非盈利机构)的许多数据专家都可以理解和借鉴的。

订阅这个播客是每周花些时间保持领先的好方法。

InfoQ 的大数据

也许播客不是你的风格,或者也许你渴望一周不止一次。如果你正在寻找更多的资源(多种格式,包括播客、文章、演示文稿等等),InfoQ 的大数据部分是一个深入挖掘的好地方。该公司一直在通过创建一个由工程师和从业者组成的编辑社区来为自己命名,而不是记者,其内容反映了这一选择。大数据部分涵盖了广泛的主题,包括人工智能,机器学习和数据工程。虽然它并不完全专注于数据工程,但它是一种了解更多相邻空间的有益方式。

令人惊叹的大数据

我是软件社区创建 GitHub 库的趋势的忠实粉丝,GitHub 库拥有专注于特定领域的精选资源列表(又名牛逼列表)。在我的 Clojure 时代, awesome-clojure 是查找不同数据库适配器或 linters 的绝佳资源。幸运的是,大数据有一个“太棒了”,其中包括数据工程公共数据集的子部分(这些似乎总是会派上用场)。在该项目大受欢迎之前,监管似乎有点严格;别担心,即使拥有一个庞大的公共数据集列表——例如,在能源领域——也是非常有价值的。

无论是以招聘增长率、业务需求还是工具集的发展来衡量,数据工程领域都在快速发展。幸运的是,可用资源保持同步。虽然很容易迷失在日复一日的项目中,但我已经能够重新燃起热情,并通过后退一步了解更新的范式/技术来跟上这个领域。有了这个资源列表,我希望你们都能找到这样做的起点!

跟上 PyTorch 闪电和九头蛇

原文:https://towardsdatascience.com/keeping-up-with-pytorch-lightning-and-hydra-31e8ed70b2cc?source=collection_archive---------19-----------------------

我如何使用 PyTorch Lightning 1.1 和 Hydra 1.0 的新特性将我的训练脚本缩减了 50%

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

努力跟上!— 来源

2021 年 02 月 09 日更新:这个故事是关于 PyTorch Lightning 0.9.0 和 Hydra 1.0.0rc4 的。从那时起,他们已经发布了他们的正式生产就绪版本,我也发布了这个故事的第二版,其中包括了所有最新的变化。我把这个故事留给后人,但请查看第二版*!***

介绍

上周, PyTorch Lightning 0.9.0Hydra 的第四个候选版本 1.0.0 发布了,充满了新功能和大部分最终 API。我认为这是一个很好的机会让我重温我的边项目 Leela Zero PyTorch ,看看这些新版本是如何融入其中的。在这篇文章中,我将谈论这两个库的一些新特性,以及它们如何帮助 Leela Zero PyTorch。我不会在这里过多地谈论关于 Leela Zero PyTorch 的细节,所以如果你想更多地了解我的副业项目,你可以在这里阅读我以前关于它的博文

PyTorch 闪电 0.9.0

对于 PyTorch Lightning 团队来说,这是一个重要的里程碑,因为他们正在努力工作以发布 1.0.0 版本。它引入了许多新特性和一个更接近最终版本的 API。在我们开始之前,如果你想了解更多关于这个版本的信息,请查看官方的博客文章。如果你想了解更多关于 PyTorch Lightning 的知识,请查看 Github 页面和官方文档。

结果

你有没有发现自己重复地实现*_epoch_end方法,只是为了从你的*_step方法中聚集结果?您是否发现自己在如何正确记录您的*_step*_epoch_end方法中计算出的指标时被绊倒了?你并不孤单,PyTorch Lightning 0.9.0 引入了一个叫做Result的新抽象来解决这些问题。

ResultTrainResultEvalResult两种。顾名思义,TrainResult用于训练,EvalResult用于验证和测试。它们的接口很简单:您指定在实例化期间要操作的主要指标(对于TrainResult,要最小化的指标,对于EvalResult,要检查点或提前停止的指标),然后您指定要记录的附加指标。让我们看看它们是如何在我的项目中使用的:

TrainResult 和 EvalResult 有助于您不重复度量记录。

training_step()中,我指定了要最小化的总损耗,并记录了总损耗(也指定要在进度条中显示)、均方误差损耗、交叉熵损耗以及最后的精度(使用 PyTorch Lightning 的新度量包计算,稍后将讨论)。我不需要编写代码来在纪元级别聚合它们,因为TrainResult会处理这些。事实上,您可以使用TrainResult指定每个指标应该聚合和记录的级别(步骤、时期或两者都有),它会自动为您处理一切。

类似地,在validation_step()中,我指定了用于检查点的总损失,并记录总损失、均方误差损失、交叉熵损失和准确性。同样,我不需要编写validation_epoch_end(),因为聚合和日志记录是由EvalResult处理的。此外,我不需要为test_step()重复我自己,只需调用validation_step()并为要记录的指标重命名键。

得益于Result,您可以立即看到我的代码变得更加简单、可读性更好、可维护性更高。你可以在这里了解更多信息

韵律学

PyTorch Lightning 团队在 0.8 中继续他们的工作,在 0.9.0 中引入了更多的度量实现。PyTorch Lightning 中的每一个指标实现都是一个 PyTorch 模块,并且有其对应的功能,使用起来非常简单灵活。对于我的项目,我决定集成 accuracy 的功能实现,这只是导入它并在适当的*_step方法中调用它的问题。

函数精度在 training_step()方法中被调用。

PyTorch Lightning 现在包含许多其他指标实现,包括高级 NLP 指标,如 BLEU score。你可以在这里阅读更多关于它的

照明数据模块

PyTorch Lightning 的另一个棘手问题是处理各种数据集。直到 0.9.0,PyTorch Lightning 对如何组织你的数据处理代码保持沉默,除了你使用 PyTorch 的数据集数据加载器。这无疑给了您很大的自由,但也使您很难保持数据集实现的整洁、可维护和易于与他人共享。在 0.9.0 中,PyTorch Lightning 在LightningDataModule中引入了一种新的组织数据处理代码的方式,封装了数据处理中最常见的步骤。它有一个简单的接口,有五种方法:prepare_data()setup()train_dataloader()val_dataloader()test_dataloader()。让我们回顾一下它们在我的项目中是如何实现的,以理解它们的作用。

  • prepare_data():该方法适用于任何在主流程中必须完成的事情,然后再派生出分布式培训的子流程。下载、预处理或保存到磁盘等任务是这种方法的理想选择。需要注意的一点是,在这里设置的任何状态都不会被带到分布式培训中的子流程,因此您应该注意不要在这里设置任何状态。在我的项目中,我依赖 Leela Zero 来预处理 Go sgf 文件,所以我决定跳过实现这个方法,但是我可以在技术上实现这个方法中的预处理步骤。
  • setup():该方法用于分布式培训的每个子流程中必须完成的任何事情。您应该构造实际的 PyTorch Datasets并在这里设置任何必要的状态。在 Leela Zero PyTorch 中,我初始化了我的Datasets,它从磁盘读入数据,并将其转换为张量,并保存为状态。
  • *_dataloader():这是你初始化DataLoaders进行训练/验证/测试的地方。在我的例子中,我简单地使用在setup()中构建的数据集以及在LightningDataModule初始化期间传递的配置来初始化DataLoaders

现在,只需要将LightningDataModule转换成trainer.fit()trainer.test()就可以了。您也可以想象这样一个场景,我为不同类型的数据集(比如国际象棋游戏数据)实现了另一个LightningDataModule,培训师也会同样接受它。我可以更进一步,使用 Hydra 的对象实例化模式,在各种数据模块之间轻松切换。

九头蛇 1.0.0rc4

1.0.0rc4 使 Hydra 更加接近其 1.0.0 正式版本。它包含许多错误修复和一些重要的 API 更改,使该库更加成熟和易于使用。在我们开始之前,如果你想了解更多关于九头蛇的信息,请查看官方网站以及官方文档

@hydra.main()

您可以将这个装饰器添加到任何接受 OmegaConf 的DictConfig的函数中,Hydra 将自动处理您脚本的各个方面。这本身并不是一个新特性,但是我最初决定不使用这个特性,因为它接管了输出目录结构和工作目录。我实际上使用了 Hydra 的实验性 Compose API 来解决这个问题,我将在后面讨论。然而,在与 Hydra 的创建者 Omry 交谈后,我意识到这不仅不是推荐的方法,而且我还失去了 Hydra 提供的一些很酷的功能,如命令行界面的自动处理、自动帮助消息和 tab 补全。此外,在使用一段时间后,我发现 Hydra 的输出目录和工作目录管理非常有用,因为我不必在 PyTorch Lightning 端手动设置日志目录结构。你可以在 Hydra 的基础教程中读到更多关于这个装饰者的内容。

打包指令

在 Hydra 0.11 中,配置只有一个全局名称空间,但在 1.0.0 中,您可以使用 package 指令在不同的名称空间中组织配置。这允许您保持 yaml 配置文件的平整和整洁,没有不必要的嵌套。让我们来看看 Leela Zero PyTorch 的网络规模配置:

“@package group”表示此配置应在当前组下,在本例中为“网络”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网络大小配置已按规定添加到“网络”下。请注意“board_size”和“in_channels”来自数据配置(composition!)

如您所见,包指令使您的配置更易于管理。你可以在这里阅读更多关于包指令及其更高级的用例

实例化对象

Hydra 提供了一个特性,可以根据配置实例化一个对象或调用一个函数。当您希望您的脚本有一个简单的接口在各种实现之间切换时,这是非常有用的。这也不是一个新功能,但它的界面在 1.0.0rc4 中有了很大的改进。在我的情况下,我使用它在网络大小、训练记录器和数据集之间切换。我们以网络规模配置为例。

“大”、“巨型”和“小型”网络的配置

基于所选配置实例化网络。请注意,您可以传入额外的参数来实例化(),就像我在这里对 cfg.train 所做的那样。

NetworkLightningModule对其__init__()network_conftrain_conf接受两种说法。前者从配置中传入,后者在instantiate() ( cfg.train)中作为额外参数传入。你所要做的就是在命令行中输入+network={small,big,huge}来选择不同的网络大小。您甚至可以想象通过用不同的_target_创建一个新的配置,并在命令行中传递配置名,来选择一个完全不同的架构。不需要通过命令行传递所有的小细节!你可以在这里阅读更多关于这个模式的信息

撰写 API

尽管 Hydra 的 Compose API 不是编写脚本的推荐方式,但它仍然是编写单元测试的推荐方式,并且非常有用。我用它来为主培训脚本编写单元测试。同样,这并不是一个新特性,但是 Hydra 1.0.0rc4 确实为使用 Python 的上下文管理器的 Compose API 引入了一个更干净的接口(with语句)。

您可以使用 Hydra 的 Compose API 轻松编写配置字典。它有助于保持单元测试的整洁和易于调试。

你可以在这里阅读更多关于 Compose API 的内容,以及如何在这里使用它进行单元测试

未使用的功能:结构化配置和变量插值

Hydra 1.0.0rc4 中还有很多其他特性我没有利用,主要是因为我还没有足够的时间来集成它们。我将讨论本节中最大的一个问题——结构化配置。

结构化配置是 1.0.0 中引入的一个主要新特性,它利用 Python 的 dataclasses 来提供运行时和静态类型检查,这在应用程序变得复杂时非常有用。我可能会在将来有时间的时候整合它们,所以请继续关注另一篇博文!

结论

我的新旧训练脚本。行数从 56 增加到 28。五折优惠!

自从我写了关于 Leela Zero PyTorch 的第一篇博文以来,Hydra 和 PyTorch Lightning 都引入了许多新的特性和抽象,可以帮助您极大地简化 PyTorch 脚本。正如你在上面看到的,我的主要训练脚本现在只有 28 行,而以前是 56 行。此外,训练管道的每个部分,神经网络体系结构,数据集和记录器,都是模块化的,易于交换。这使得迭代更快,维护更容易,重现性更好,让您可以专注于项目中最有趣和最重要的部分。我希望这篇博文对你“跟上”这两个了不起的库有所帮助!你可以在这里找到 Leela Zero PyTorch 的代码。

跟上贝茨

原文:https://towardsdatascience.com/keeping-up-with-the-berts-5b7beb92766?source=collection_archive---------12-----------------------

NLP 镇最受欢迎的家庭

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

莱昂纳多·大久保俊郎在 Unsplash 上的照片

如果你稍微关注一下 NLP 世界,或者甚至是 ML 新闻,你最有可能遇到 Google 的 BERT 模型或者它的一个亲戚。如果您还没有并且仍然偶然看到这篇文章,那么让我荣幸地向您介绍 BERT——强大的 NLP 怪兽。

伯特是什么?

BERT 代表BI directionalEn coderR表示来自Ttransformers,是 Google 的一个语言表示模型。它使用两个步骤,预训练和微调,为广泛的任务创建最先进的模型。

它与众不同的特点是跨不同下游任务的统一架构——这些是什么,我们将很快讨论。这意味着相同的预训练模型可以针对各种最终任务进行微调,这些最终任务可能与训练的任务模型不相似,并给出接近最先进的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

BERT 由两个步骤组成。来源:论文

正如你所看到的,我们首先在预训练任务上同时训练模型。一旦预训练完成,同一模型可以针对各种下游任务进行微调。请注意,单独的模型针对特定的下游任务进行了微调。因此,单个预训练模型可以在微调后生成多个下游任务特定模型。

伯特建筑

简单来说就是 一堆变压器的编码器 。你可以在我之前的文章中详细了解变形金刚。或者,如果你已经对它有了一些模糊的想法,请查看这个绝对是 BERT 中使用的编码器块的 3D 炸弹图。说真的,你不能错过这个!

[## 变形金刚(电影名)

或者我喜欢称之为类固醇引起的注意。💉💊

towardsdatascience.com](/transformers-89034557de14)

现在让我们来看看一些我们都不会记得的数字,但是如果没有它们,我们的理解会感到不完整,所以这里什么都不说:

L =层数(即堆栈中的#个变换器编码器块)。
H =隐藏大小(即 q,kv 向量的大小)。
A =关注头数。

  • 伯特基 : L=12,H=768,A=12。
    总参数=110M!
  • 伯特大号 : L=24,H=1024,A=16。
    总参数=340M!!

什么使得它是双向的?

我们通常通过在一些不相关的任务上训练它来创建语言模型,但是这些任务有助于开发对模型中单词的上下文理解。这种任务通常包括预测下一个单词或彼此非常接近的单词。这种训练方法不能扩展并用于双向模型,因为它会让每个单词间接“看到自己”——当你从相反的方向再次接近同一个句子时,你已经知道会发生什么了。一个数据泄露的案例。

在这种情况下,模型可以轻易地预测目标单词。此外,我们不能保证该模型,如果经过完全训练,在某种程度上已经学会了单词的上下文含义,而不仅仅是专注于优化琐碎的预测。

那么伯特是如何做到双向预训练的呢?它通过使用一个称为屏蔽 LM 的过程来实现这一点。稍后会有更多的细节,所以请继续读下去,我的朋友。

预训练伯特

BERT 模型在以下两个无监督的任务上被训练。

1.掩蔽语言模型(MLM)

此任务启用模型的深度双向学习方面。在此任务中,随机屏蔽(用[MASKtoken 替换)一定百分比的输入标记,模型尝试预测这些屏蔽的标记,而不是整个输入序列。然后,来自该模型的预测标记被馈送到词汇表上的输出 softmax 中,以获得最终的输出单词。

然而,这在预训练和微调任务之间产生了不匹配,因为后者在大多数下游任务中不涉及预测屏蔽词。这通过我们如何屏蔽输入令牌的微妙变化得以缓解。

大约 15%的单词在训练时被屏蔽,但是所有被屏蔽的单词都不会被[MASKtoken 替换。

  • 80%的时间用[ 屏蔽令牌。
  • 10%的时候用随机代币。
  • 10%的时间,未改变的输入令牌被屏蔽。

2.下一句预测(NSP)

LM 并不直接捕捉两个句子之间的关系,而这种关系在许多下游任务中是相关的,例如问答(QA)和自然语言推理(NLI) 。通过对二值化 NSP 任务的训练,该模型被教导句子关系。

在这个任务中,选择了两个句子——A 和 B——进行预训练。

  • 50%的时间 B 实际上是 a 后面的下一句话。
  • 50%的时间 B 是从语料库中随机抽取的句子。

培训——输入和输出。

该模型同时在上述两个任务上被训练。这是通过输入和输出的巧妙运用而成为可能的。

输入

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

伯特的输入表示。来源:论文

该模型需要接受单个句子或明确打包在一个标记序列中的两个句子的输入。作者注意到“句子”可以是连续文本的任意跨度,而不是实际的语言句子。一个[SEP]标记用于分隔两个句子,以及使用一个学习过的片段嵌入来指示一个标记作为片段 A 或 b 的一部分。

问题#1: 所有的输入都是在一个步骤中馈入的——与顺序馈入输入的 RNNs 相反,模型 不能保持输入令牌的排序 。每种语言的词序都很重要,无论是语义上还是句法上。

问题#2: 为了正确执行下一个句子预测任务,我们需要能够 区分句子 A 和 B 。固定句子的长度可能限制太多,并且是各种下游任务的潜在瓶颈。

这两个问题都是通过向我们的原始令牌添加包含所需信息的嵌入并将结果用作我们的 BERT 模型的输入来解决的。以下嵌入被添加到令牌嵌入中:

  • 片段嵌入 :它们提供了特定标记所属句子的信息。
  • 位置嵌入 :它们提供了单词在输入中的顺序信息。

输出

如何同时预测两个不同任务的产出?答案是通过使用不同的 FFNN + Softmax 层,该层建立在来自最后一个编码器的输出的顶部,对应于期望的输入令牌。我们将最后一个编码器的输出称为最终状态。

第一个输入令牌总是一个特殊分类*【CLS】*令牌。对应于该令牌的最终状态被用作分类任务的聚集序列表示,并被用于下一句预测,其中它被馈送到预测标签“ IsNext 或“ NotNext ”的概率的 FFNN + Softmax 层。

对应于[ MASK ] tokens 的最终状态被输入 FFNN+Softmax,以从我们的词汇表中预测下一个单词。

微调伯特

通过交换适当的输入或输出,可以对各种下游任务进行微调。在一般情况下,为了训练特定于任务的模型,我们向现有的 BERT 添加一个额外的输出层,并对结果模型进行微调——所有参数,端到端。增加输入/输出层而不改变 BERT 模型的一个积极结果是,只需要从头开始学习最少数量的参数,使得过程快速、成本和资源高效。

为了让你了解它的速度和效率,作者声称,从完全相同的预训练模型开始,论文中的所有结果在单云 TPU 上最多可以在1 小时内复制,在 GPU 上最多可以在几小时内复制。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在各种下游任务上微调 BERT。来源:论文

在句子对分类和单句分类中,对应于[ CLS 记号的最终状态被用作进行预测的附加层的输入。

在 QA 任务中,在微调期间引入了开始(S)和结束(E)向量。问题作为句子 A 输入,答案作为句子 b 输入。单词 i 作为答案区间开始的概率计算为 T i (对应于第 i 个输入标记的最终状态)和 S(开始向量)之间的点积,后面是段落中所有单词的 softmax。端跨采用类似的方法。
从位置 i 到位置 j 的候选区间的得分定义为 S T i + E T j ,以 ji 的最大得分区间作为预测

GPT——远房表亲

伯特是产生这些突破性成果的唯一模型吗?不是。OpenAI 的另一个模型,叫做 GPT,已经在网上引起了轰动。

但是很多人没有意识到这两个模型有一个共同点,那就是这两个模型都重用了一个 Transformer 组件。如前所述 BERT 将变压器的编码器部分作为其构建模块。同时, GPT 使用变压器的解码器部分作为其构建模块。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:论文。

请注意,由于编码器的双向自我关注,BERT 中的双向连接。与此同时,GPT 的连接只是单向的,从左到右,由于解码器的设计防止查看未来的预测——更多信息请参考变形金刚

伯特一家

如果我们不把一些工作得很好的东西拿来,并试图重新创造或修改它,那就不是 21 世纪了。伯特建筑也不例外。以下是一些最流行的变体:

  • 阿尔伯特 由谷歌和更多-本文描述了参数减少技术,以降低内存减少和提高 BERT 模型的训练速度。
  • 罗伯塔 脸书——本文认为最初的 BERT 模型训练不足,并表明经过更多的训练/调整,它可以超越最初的结果。
  • ERNIE :百度通过知识整合增强表示——受 BERT 掩蔽策略启发,学习通过知识掩蔽策略增强的语言表示,包括实体级掩蔽和短语级掩蔽。
  • 蒸馏伯特——使用 Huggingface 模型蒸馏的较小伯特。

你可以在 the GLUE 排行榜查看更多伯特风格的模特。

结论

  1. BERT 是一个堆叠变压器的编码器模型。
  2. 它有两个阶段——预训练和微调。
  3. 预训练是计算和时间密集型的。
  4. 然而,它独立于它最终完成的任务,因此相同的预训练模型可以用于许多任务。
  5. GPT 和伯特没什么不同,是一个堆叠变压器的解码器模型。
  6. 伯特有许多变种。

参考文献+推荐阅读

我很高兴你坚持到这篇文章结束。🎉我希望你的阅读体验和我写这篇文章时一样丰富。💖

尽请查看我的其他文章 这里

如果你想联系我,我会选择Twitter

把你的孩子的自行车变成键盘,让他们在隔离期间保持活跃

原文:https://towardsdatascience.com/keeping-your-kids-in-shape-during-quarantine-by-turning-their-bicycle-into-a-keyboard-e1f8a34fac2e?source=collection_archive---------59-----------------------

在这个简短的教程中,我将带你了解将任何自行车变成类似街机的电脑游戏控制器的过程。

尽管没有树立健康生活方式的最佳榜样,我和妻子还是努力吃绿色食品,避免太多垃圾食品,保持身材。另一方面,我们 5 岁的孩子对任何体育活动都不感兴趣,除非这能让他更接近他的(我的……)iPad。

通常情况下,这个问题可以通过做一个孩子来部分解决,这包括大量的跑步,爬山,摔倒…还有最近的骑自行车!但不幸的是,随着新冠肺炎·疫情的出现,我们真的做不到这些。

所以,我把他的自行车变成了键盘。

现在,他可以一边玩他最喜欢的电脑游戏,一边锻炼身体!在这篇短文中,我将带你经历建造你自己的键盘自行车的过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基础:

第一步是通过让后轮自由旋转而不接触地面,并允许车把自由转动,将自行车变成室内训练自行车。如果你的孩子被向前推,你不应该进行以下步骤。

旋转踏板时,有许多方法可以让自行车保持在原位。将自行车悬挂在天花板上,在车轮下的地板上挖洞等等。我个人用训练轮来支撑车尾,但这是你发挥创造力的地方,更重要的是,要负责。我有意避免谈论我实现这一部分的太多细节(你可以从本文中的各种图片和 gif 中得到一个很好的想法……),因为确保自行车的安全使用是你的责任。

这个计划

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在将自行车固定好以便可以在适当的位置踩踏之后,我们将连接传感器来捕捉后轮的旋转、车把的位置和一些用于额外控制的按钮(这些可以在以后轻松编程以映射任何键盘按键)。来自所有传感器的数据将被 Arduino 捕获和转换,Arduino 将充当键盘,并将击键发送到运行(很可能是赛车)游戏的计算机。

五金器具

我们必须使用一块可以作为 USB 键盘的板。我用的是 Arduino 莱昂纳多

我使用了 3 种传感器:
1A1302 霍尔效应传感器 —该传感器在接近磁场时会改变其输出电压。我们将使用它将后轮的旋转转换为向前按键。
2。 10K 欧姆线性电位器 —用于将车把方向转换为左/右键。
3。 微动开关 —用作额外控制器。我强烈建议使用这种类型的开关(限位开关),不要使用一些花哨的按钮。这些开关非常灵敏,而按钮有时对孩子来说很难按下。

连接零件

霍尔效应传感器

通过将磁铁放在后轮上,并将传感器靠近(尽可能靠近而不接触车轮),我们可以在磁铁经过传感器时感应到车轮的旋转。我使用了两个等间距的磁铁(即大约 180 度)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用于连接磁体的尖端-

你可能会找到一个更优雅的解决方案,但我在这里使用的是用塑料包装缠绕磁铁,就像糖果包装一样(我指的是上面 gif 中看到的随着轮子旋转的白色斑点)。然后使用松散的两端将磁铁绑在车轮的辐条上,并用热胶固定。

为了让传感器(这是指 gif 中的红色垂直杆,传感器本身位于其末端)尽可能靠近车轮,我将它安装到一根软线上(我使用了一些重型电线,但任何可以弯曲并保持其位置的电线都可以)。

电位计

电位计需要连接到车把的枢轴点。在那一点上应该有一个内六角扳手的套筒。我使用了一个合适的驱动位,并将其连接到电位计。最初,我的计划是将两部分焊接在一起,但驱动位的可焊性很低(没有)。最后,我拿了一根短吸管(在下面的 gif 中以紫色显示),从两侧粘上电位计和驱动器位,并注入热胶,将所有东西粘在一起。它像岩石一样坚固。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

至于把电位计固定在自行车车架上,你还是需要发挥创意。我的解决方案(可以在预告片 gif 和文章结尾的图片中看到,显示了从自行车上拆下的所有组件)是基于制作一个有机玻璃“臂”来固定电位计,可能不适用于您孩子的自行车。这个想法很简单,电位计需要和把手一起旋转。尽管很简单,但实施起来可能相当具有挑战性…

开关

我把其中一个开关放在(冗余的)前轮刹车杆后面,另一个放在不冗余的后轮刹车杆后面,断开这个刹车,这样通过踩刹车来压开关,感觉很自然。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

再说一次,我不承担任何责任。如果你觉得你在第一部分做的支撑不够坚固,担心阻碍和/或断开刹车,你可以,也应该把按钮放在别的地方。没有对错,只要舒服安全就好。我把自己限制在两个按钮上,但是你显然可以在板子允许的范围内尽可能的高。

接线

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关于提前思考的一个注记。

在完成键盘自行车版本 1.0 后,我开始改进它,确保我可以轻松地断开所有组件,并尽快重新组装它们,这需要一些调整,如果我一开始就想到这一点,就可以避免。我花了将近 2 个小时升级到键盘自行车版本 2.0 ,但是现在用不到一分钟就可以组装/拆卸。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

另一点要记住的是什么是固定板的最佳位置。我不经意地把它放在了我儿子上下自行车的一侧。回想起来,我应该选择另一边…

密码

如硬件部分所述,键盘自行车设置由 3 种传感器组成:霍尔效应传感器、电位计和按钮。我将分别解释每个组件的代码。

霍尔效应传感器

当车轮旋转且磁铁经过传感器时,霍尔效应传感器被“激活”,导致输出电压发生变化。对于这一部分,每当电压的绝对变化超过某个阈值(hall_effect_threshold)时,我就打开键(在我的情况下是前进键)。然后我让它一直开着,开始倒数一定的时间(drive_decay_const)。这允许调整你从每个“尖峰”得到的运动量。增加drive_decay_const的值以获得更多的移动。

电位计

在这里,我们基本上试图将转动车把的“模拟”运动转换成“二进制”的左或右点击。如果你只是想做一些小的调整,连续的按压就太戏剧化了,所以转动的量需要用“点击量”来表示。我是通过在设定的时间间隔(interval)激活和取消旋转运动来做到这一点的,这样旋转车把超过某个阈值,就会开始间歇地按压。此外,当按钮被激活时,我增加了一个延迟,这个延迟随着转动的次数而增加。因此,对于更高角度的转弯,按钮将被按下更长的时间(即,每次点击将更长),从而导致更积极的转弯。另外,注意我在设置时初始化了pot_center。这对于电位计从插座上断开的情况很有帮助。不用重新定位中心,你只需重置棋盘,中心就会被重置。

开关

这里没什么特别的。只需更换激活的钥匙,以满足您的需求。这里的代码是为了玩 SuperTuxKart ,这是一个很棒的儿童赛车游戏,但这辆自行车显然可以适应许多其他游戏。我们也用它来演奏《我的世界》,效果很好。

/*
bicycle_keyboard
*/#include "Keyboard.h"
const int hall_effect_threshold = 1      // threshold for hall effect sensor.
const int turn_threshold = 35;           // threshold for detecting a left/right movement. 
const int drive_decay_const = 100;       // drive_decay speed of car (larger => slower decreasing / faster car)
const int turn_decay_const = 0.75;       // multiplier for intensity of turn. higher value => sharper turns. 
const char button_1_val = ' ';           // value for button 1
const char button_2_val = 'n';           // value for button 2
const long interval = 200;               // interval for allowing not allowing turns.
const int button_1 = 2;                  // button_1 pin 
const int button_2 = 3;                  // button_2 pin
int pot_center = 500;                    // center point for potentiometer. this value is being reset in setup.
int prev_hallSensorValue = 0;            // initial value for sensor
const int delay_time = 1;                // delay between reads
int drive_decay = 0;                     // initial drive_decay value
unsigned long previousMillis = 0;        // initial value
bool allow_turn = true;                  // allow turn is a boolean to only allow turns every passing interval 
int turn_amount = 0;                     // initial value
void setup() {
  Serial.begin(9600);
  Keyboard.begin();
  pinMode(button_1, INPUT);
  pinMode(button_2, INPUT);
  pot_center = analogRead(A1);
}
void loop() {
  unsigned long currentMillis = millis();
  int hallSensorValue = analogRead(A0);
  int pot = analogRead(A1);//  Serial.println(hallSensorValue);
//  Serial.println(pot);  delay(delay_time);        // delay in between reads for stability  // DRIVE:
  if (abs(hallSensorValue- prev_hallSensorValue) >1){
    prev_hallSensorValue = hallSensorValue;
    Keyboard.press(KEY_UP_ARROW);
    drive_decay = drive_decay_const;
  }
  else{
    if (drive_decay <= 0){
        Keyboard.release(KEY_UP_ARROW);
        }
    else{
      drive_decay -=1;
      }
    }if (allow_turn==true){//TURN RIGHT
  if (pot < pot_center - turn_threshold) {
    Keyboard.press(KEY_RIGHT_ARROW);
    } 
// TURN LEFT
  if (pot > pot_center + turn_threshold){
    Keyboard.press(KEY_LEFT_ARROW);
  }}
    if (currentMillis - previousMillis >= interval) { // interval has passed
    previousMillis = currentMillis;
    turn_amount = abs(pot-pot_center);    // switch allow turn state:
    if (allow_turn == true) {
      if (turn_amount > turn_threshold){
          delay(turn_amount); //keep button pressed longer the more aggressive the turn.
        }
      allow_turn = false;
    } else {
      allow_turn = true;
      }
     // release the turn keys:
      Keyboard.release(KEY_RIGHT_ARROW);
      Keyboard.release(KEY_LEFT_ARROW);
    }  //check buttons
  if (digitalRead(button_1) == HIGH){
    Keyboard.press(button_1_val);
    }
   else{
     Keyboard.release(button_1_val);
    }  if (digitalRead(button_2) == HIGH){
    Keyboard.press(button_2_val);
    }
   else{
     Keyboard.release(button_2_val);
    }
}

摘要

最后,我希望我很好地解释了如何制造键盘自行车。如果你选择建立一个,而发现我还没有,不要犹豫,发表评论或给我发消息。

保留您的 mLab MongoDB 实例:从 Heroku 分离。

原文:https://towardsdatascience.com/keeping-your-mlab-mongodbs-detaching-from-heroku-173b5c97c715?source=collection_archive---------32-----------------------

实践教程

如何在 2020 年 11 月 10 日通过从 Heroku 分离 mLab 来保存你的数据不被删除。

By: 爱德华克鲁格 道格拉斯富兰克林

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

约翰·巴克利普在 Unsplash 上拍摄的照片

如果您使用 mLab 托管的 MongoDB 实例作为 Heroku 附加组件,请花些时间通过分离它来迁移它。分离不需要迁移或对应用程序代码进行任何更改,您的环境变量将保持不变。你仍然需要在 2021 年 1 月 12 日之前迁移,那时 mLab 作为一个整体关闭并终止所有实例。

这是因为 MongoDB 在 2018 年收购了 mLab,并已决定结束服务,以让用户将数据库迁移到 MongoDB Atlas。

我们在 2020 年 7 月 10 日首次发送的电子邮件中收到了这一通知。邮件称,2020 年 11 月 10 日之后,Heroku 将不再支持 mLab 插件。这将导致任何依赖 mLab 的应用程序无法运行,并删除存储在 mLab 上的所有数据。

以下是这封邮件的副本:

亲爱的 Heroku 客户:

我们发现您是以下安装了 mLab MongoDB 附加组件的应用程序的所有者或合作者。请注意,这可能是部分列表。

弗拉斯克-皮蒙戈

mLab 团队已选择停止此附加组件。mLab MongoDB 插件将于 2020 年 11 月 10 日从所有 Heroku 应用中移除

有许多解决方案可以让我们的软件在云中运行。最简单的是我们将在本文中深入研究的,从 Heroku 附加组件中分离我们的 mLab 帐户。我们可以稍后处理迁移,并将写一篇包含一些选项的文章。我们会在准备好的时候更新文章。

指南:从 Heroku 分离 mLab

步骤 1:登录 mLab 并进入确认屏幕。先别确认!

在 Heroku 上,查看您想要修改的应用程序,然后单击 mLab 插件。这应该会带你到 mLab 的网站。单击帐户。在这里你可以找到右上角的“脱离 Heroku”按钮。这将使您进入下面看到的协议页面。先别确认!

请注意这里的三个要点:

  1. 你仍然需要在 2021 年 1 月 21 日之前迁移到 MongoDB atlas。换句话说,这种分离是暂时的,我们将在明年一月迁移。
  2. 您的电子邮件地址将成为 mLab 的管理员帐户,您可以通过电子邮件重置密码来重置您的密码。不幸的是,事实并非如此。在写这篇文章的时候,重置邮件里的 URL 把你带到了一个非功能性的页面。
  3. 其次,当您从 Heroku 分离时,您的 Heroku 配置变量可能会被自动删除。**在你完成这个过程之前,**确保你的应用不依赖于任何MONGO*_URI变量,如果需要的话保存它们。您可能需要重置它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

协议页面

第二步:从你的 Heroku 配置变量中获取 URI 并保存它

要检查您的应用程序对MONGO*_URI变量**,**的使用情况,请转到 Heroku.com,然后转到您的应用程序,再转到设置,然后检查您的配置变量。在我们的例子中,我们发现了MONGODB_URI。然后,我们将变量保存在一个文本文件中,以便与我们的 mLab 用户名和部署标识符(位于复选框页面的底部)一起妥善保管。将这些数据保存在文本文件中可以作为备份,以防在此过程中丢失。

这是加倍重要的,因为如果你有同样的问题,密码重置我做的,你也可能无法登录到您的 mLab 管理面板发现 URI。您将需要这些信息来保持您的应用程序正常工作,并在将来进行迁移。

步骤 3:完成 mLab 的分离,接受条款

回到我们在第一步中找到的确认页面。这一次,继续接受条款并确认。

现在,您的 mLab 帐户不再与 mLab Heroku 附加组件相关联。

单击分离后,可能需要一段时间才能在 Heroku 端删除配置变量。现在是从我们的 Heroku 项目中删除附加组件的好时机。

不要注销 mLab,因为您可能无法重新登录。

步骤 4:删除 Heroku 端的 mLab 插件

为此,请单击应用下的“配置加载项”。然后单击 mLab 横幅最右侧的下拉菜单,并选择删除附加组件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 Heroku 删除 mLab MongoDB

确认附加组件删除并将其移除。

步骤 5:仔细检查连接字符串在 mLab 端没有改变,如果它被删除,则获取保存的 URI 并将其设置为 Heroku 配置变量

现在我们回到 mLab,看看连接字符串在 mLab 方面是否仍然有效。这可以在 home →MongoDB Deployments 下看到。选择数据库并查看字符串。它与你保存在文本文件中的匹配吗?如果是这样的话,你不需要做任何事情。如果已经更改,请确保在 Heroku config vars 下更新该变量。

第六步:重新部署应用

接下来,您将在 Heroku 上重新部署应用程序。您应该能够通过应用程序访问您的数据库,并通过分离看到持久化的数据。

大功告成!

再一次,这是保持你的数据库在 2020 年 11 月 10 日之后的十分钟快速修复。我们将在后面的文章中进一步研究迁移选项。

Keras 101:一个简单的(和可解释的)用于房价回归的神经网络模型

原文:https://towardsdatascience.com/keras-101-a-simple-and-interpretable-neural-network-model-for-house-pricing-regression-31b1a77f05ae?source=collection_archive---------11-----------------------

TL;DR:使用带有神经网络的波士顿数据集预测房价,并采用 SHAP 值来解释我们的模型。完整的笔记本可以在这里找到

在本帖中,我们将介绍一些数据探索的基础知识,并使用 Keras 构建一个模型,以帮助我们预测波士顿(MA)地区某栋房屋的销售价格。作为这个模型在现实世界中的一个应用,你可以想象成为一个真正的国家代理人,寻找一个工具来帮助你履行日常职责,至少对我来说,与直觉相比,这听起来相当不错。

在这个练习中,我们将使用 Plotly 库,而不是老式的 matplotlib,因为它有更多的交互图,这无疑有助于理解数据。我们还将使用 Scikit-LearnKeras 来构建模型,使用 Pandas 库来操作我们的数据,使用 SHAP 库来为我们训练好的模型生成解释。

导入数据集

在本例中,我们将使用包含波士顿数据集的 sklearn.datasets 模块。您也可以使用 keras.datasets 模块,但是这个模块不包含要素的标签,所以我们决定使用 scikit 的模块。让我们也把它转换成熊猫的数据帧,并打印它的头部。

from sklearn.datasets import load_boston
import pandas as pdboston_dataset = load_boston()df = pd.DataFrame(boston_dataset.data, columns=boston_dataset.feature_names)
df['MEDV'] = boston_dataset.targetdf.head(n=10)

探索性数据分析

熟悉数据集是帮助您理解数据并从结果中得出更好的结论和解释的基本步骤。

首先,让我们绘制一些箱线图,这将有助于我们更好地可视化数据分布中的异常和/或异常值。如果你对什么是盒图以及它如何帮助我们更好地可视化我们的数据分布感到困惑,这里有一个来自 Ross (1977)的简短描述:

在描述统计学中,箱线图是一种通过四分位数以图形方式描绘数字数据组的方法。箱形图也可能有从箱形图(须状图)垂直延伸的线,表示上下四分位数之外的可变性,因此称为箱形图和箱形须状图。异常值可以绘制为单个点。

from plotly.subplots import make_subplots
import plotly.graph_objects as go
import mathtotal_items = len(df.columns)
items_per_row = 3
total_rows = math.ceil(total_items / items_per_row)fig = make_subplots(rows=total_rows, cols=items_per_row)cur_row = 1
cur_col = 1for index, column in enumerate(df.columns):
    fig.add_trace(go.Box(y=df[column], name=column), row=cur_row, col=cur_col)

    if cur_col % items_per_row == 0:
        cur_col = 1
        cur_row = cur_row + 1
    else:
        cur_col = cur_col + 1 fig.update_layout(height=1000, width=550,  showlegend=False)
fig.show() 

这将输出以下箱线图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些结果证实了我们最初的假设,即在一些列中有异常值。让我们为每个特征和目标变量绘制一些散点图,以及它们的截距线:

from plotly.subplots import make_subplots
import plotly.graph_objects as go
import math
import numpy as nptotal_items = len(df.columns)
items_per_row = 3
total_rows = math.ceil(total_items / items_per_row)fig = make_subplots(rows=total_rows, cols=items_per_row, subplot_titles=df.columns)cur_row = 1
cur_col = 1for index, column in enumerate(df.columns):
    fig.add_trace(go.Scattergl(x=df[column], 
                            y=df['MEDV'], 
                            mode="markers", 
                            marker=dict(size=3)), 
                  row=cur_row, 
                  col=cur_col)

    intercept = np.poly1d(np.polyfit(df[column], df['MEDV'], 1))(np.unique(df[column]))

    fig.add_trace(go.Scatter(x=np.unique(df[column]), 
                             y=intercept, 
                             line=dict(color='red', width=1)), 
                  row=cur_row, 
                  col=cur_col)

    if cur_col % items_per_row == 0:
        cur_col = 1
        cur_row = cur_row + 1
    else:
        cur_col = cur_col + 1 fig.update_layout(height=1000, width=550, showlegend=False)
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从这个初步的数据探索中,我们可以得出两个主要结论:

  • RM(每所住宅的平均房间数)和 LSTAT(人口的较低地位百分比)与目标变量之间有很强的线性相关性,RM 为正相关,LSTAT 为负相关。
  • 有一些包含异常值的记录,我们可以对其进行预处理,以便为我们的模型输入更多的规范化数据。

数据预处理

在我们进行任何数据预处理之前,将数据分成训练集和测试集是很重要的。我们不应该对我们的数据进行任何类型的预处理,而不应该忘记我们不应该将信息从我们的测试集泄露到其他的测试集中。对于这一步,我们可以使用 scikit-learn 中的 train_test_split 方法。在这种情况下,我们将使用 70%的数据用于训练,30%的数据用于测试。我们还设置了一个 random_state 种子,以保证方法的可重复性。

from sklearn.model_selection import train_test_splitX = df.loc[:, df.columns != 'MEDV']
y = df.loc[:, df.columns == 'MEDV']X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=123)

为了向我们的神经网络提供标准化的输入,我们需要执行数据集的规范化。这可以被看作是减少可能由现有特征引起的规模差异的一个步骤。我们通过从数据中减去平均值并除以标准偏差来执行标准化。再强调一次,为了避免测试集的任何信息泄露,这种标准化只能通过使用训练集的平均值和标准偏差来执行。

mean = X_train.mean(axis=0)
std = X_train.std(axis=0)X_train = (X_train - mean) / std
X_test = (X_test - mean) / std

建立我们的模型

由于该数据集中呈现的数据量很小,我们必须小心不要创建过于复杂的模型,否则会导致数据过度拟合。为此,我们将采用基于两个密集层的架构,第一个具有 128 个神经元,第二个具有 64 个神经元,两者都使用 ReLU(整流线性单元)激活功能。具有线性激活的密集层将被用作输出层。

为了让我们知道我们的模型是否正确学习,我们将使用均方误差损失函数,并且为了报告它的性能,我们将采用平均误差度量。

通过使用来自 Keras 的汇总方法,我们可以看到我们总共有 10,113 个参数,这对我们来说是可以接受的。

from keras.models import Sequential
from keras.layers import Densemodel = Sequential()model.add(Dense(128, input_shape=(13, ), activation='relu', name='dense_1'))
model.add(Dense(64, activation='relu', name='dense_2'))
model.add(Dense(1, activation='linear', name='dense_output'))model.compile(optimizer='adam', loss='mse', metrics=['mae'])
model.summary()

训练我们的模型

这一步非常简单:用我们的特征和它们的标签来拟合我们的模型,总共 100 个历元,保留 5%的样本(18 个记录)作为验证集。

history = model.fit(X_train, y_train, epochs=100, validation_split=0.05)

通过绘制损失和平均误差,我们可以看到我们的模型能够学习数据中的模式,而不会发生过度拟合(如验证集曲线所示):

fig = go.Figure()
fig.add_trace(go.Scattergl(y=history.history['loss'],
                    name='Train'))fig.add_trace(go.Scattergl(y=history.history['val_loss'],
                    name='Valid')) fig.update_layout(height=500, width=700,
                  xaxis_title='Epoch',
                  yaxis_title='Loss')fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们训练模型的训练和验证损失。我们可以看到,我们的模型显然能够学习我们的数据模式,而不存在数据过度拟合。

fig = go.Figure()
fig.add_trace(go.Scattergl(y=history.history['mean_absolute_error'],
                    name='Train'))fig.add_trace(go.Scattergl(y=history.history['val_mean_absolute_error'],
                    name='Valid')) fig.update_layout(height=500, width=700,
                  xaxis_title='Epoch',
                  yaxis_title='Mean Absolute Error')fig.show() 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练集和验证集的训练和验证平均绝对误差(MAE)。

评估我们的模型

为了正确评估我们的模型是否能够在现实世界中工作,我们必须使用我们的测试集来评估它。下面我们通过使用 evaluate 方法以及测试集中的特性和目标来实现。

mse_nn, mae_nn = model.evaluate(X_test, y_test)print('Mean squared error on test data: ', mse_nn)
print('Mean absolute error on test data: ', mae_nn)

这为我们提供了以下输出:

152/152 [==============================] - 0s 60us/step
**Mean squared error on test data:  17.429732523466413
Mean absolute error on test data:  2.6727954964888725**

与传统方法的比较

首先让我们尝试一个简单的算法,线性回归:

lr_model = LinearRegression()
lr_model.fit(X_train, y_train)y_pred_lr = lr_model.predict(X_test)
mse_lr = mean_squared_error(y_test, y_pred_lr)
mae_lr = mean_absolute_error(y_test, y_pred_lr)print('Mean squared error on test data: ', mse_lr)
print('Mean absolute error on test data: ', mae_lr)**Mean squared error on test data: 28.40585481050824
Mean absolute error on test data: 3.6913626771162575**

现在使用决策树回归器:

tree = DecisionTreeRegressor()
tree.fit(X_train, y_train)y_pred_tree = tree.predict(X_test)mse_dt = mean_squared_error(y_test, y_pred_tree)
mae_dt = mean_absolute_error(y_test, y_pred_tree)print('Mean squared error on test data: ', mse_dt)
print('Mean absolute error on test data: ', mae_dt)**Mean squared error on test data:  17.830657894736845
Mean absolute error on test data:  2.755263157894737**

打开黑匣子(也就是解释我们的模型)

有时候,对大多数人来说,一个好的结果就足够了,但是有些情况下,我们需要解释我们的模型用来执行预测的主要组件是什么。对于这个任务,我们可以依靠 SHAP 库,它允许我们很容易地创建我们的特性及其对模型输出的影响的概要。我不会深入 SHAP 的细节,但如果你有兴趣了解更多关于它是如何工作的,你可以查看他们的 github 页面或者甚至看一下他们的 T2 论文。

import shap
shap.initjs()explainer = shap.DeepExplainer(model, X_train[:100].values)
shap_values = explainer.shap_values(X_test[:100].values)shap.summary_plot(shap_values, X_test, plot_type='bar')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从这个简单的图中,我们可以看到对模型输出有影响的主要特征是:

  • LSTAT: %人口的较低地位
  • RM:每个住宅的平均房间数
  • RAD:放射状公路可达性指数
  • DIS:到五个波士顿就业中心的加权距离
  • 氮氧化物:氮氧化物浓度(百万分之一)——这可能与该地区的绿化程度有关
  • CRIM:城镇人均犯罪率

这清楚地证实了我们最初的 EDA 分析,其中我们指出 LSTAT 和 RM 特征与模型结果高度相关。

结论

在这篇文章中,我们已经表明,通过使用神经网络,我们可以轻松地超越传统的机器学习方法。我们还表明,即使在使用更复杂的模型时,与其他技术相比,我们仍然可以通过使用 SHAP 值来解释我们的模型的结果。

此外,我们需要记住,所探索的数据集可能已经过时,为了更好地反映当前的情况,可以执行一些功能工程(如校正通货膨胀的价格)。

参考

波士顿数据集:https://www . cs . Toronto . edu/~ delve/data/Boston/Boston detail . html

剧情:https://plot.ly/python/

scikit learn:https://scikit-learn.org/stable/

https://keras.io/

熊猫:【https://pandas.pydata.org/】T4

https://github.com/slundberg/shap SHAP 项目页面:

SHAP 论文:https://papers . nips . cc/Paper/7062-a-unified-approach-to-interpretation-model-predictions . pdf

工程师和科学家概率统计导论。https://www . Amazon . com . br/DP/b 007 zbzn 9 u/ref = DP-kindle-redirect?_encoding=UTF8 & btkr=1

Keras 的准确性指标

原文:https://towardsdatascience.com/keras-accuracy-metrics-8572eb479ec7?source=collection_archive---------6-----------------------

通过在 Python 中运行简单的实验来理解它们

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乌古尔·佩克尔在 Unsplash 上拍摄的照片

介绍

Keras 是 Python 的深度学习应用编程接口。它为评估分类器提供了五种不同的准确性指标。本文试图通过实验探索这些指标的组成和计算,从基础层面解释这些指标。

Keras 提供以下准确度指标

  1. 准确(性)
  2. 二元精度
  3. 分类准确性
  4. TopK 分类准确性
  5. 稀疏 TopK 分类准确度

对于每个指标,我们运行以下实验

  1. 从逻辑上定义和计算准确性——假设。
  2. 用 Keras 的方法计算精度。
  3. 如果(1)和(2)同时存在,将逻辑定义归于 Keras 的方法。

准确(性)

精确度计算预测值(yPred)与实际值(yTrue)相匹配的百分比。

对于一个记录,如果预测值等于实际值,则认为它是准确的。

然后我们通过将准确预测的记录数除以记录总数来计算准确率。

密码

输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二元精度

二进制精度计算二进制标签的预测值(yPred)与实际值(yTrue)相匹配的百分比。

由于标签是二进制的,yPred 由等于 1 的预测概率值组成。

郑重声明:

  1. 如果概率高于阈值,则分配 1,否则分配的值为 0。
  2. 如果赋值等于实际值,则认为是准确的。

然后,我们通过将准确预测的记录数除以记录总数来计算二进制精度。

可以调整阈值(默认值= 0.5)来提高二进制精度。

密码

输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题 1。在上面的实验中,当我们将阈值改为(i) 0.4 和(ii) 0.49 时,二进制精度的值是多少?

分类准确性

分类精度计算一次性标注的预测值(yPred)与实际值(yTrue)相匹配的百分比。

郑重声明:

  1. 我们使用 argmax()确定最大值出现的索引。
  2. 如果 yPred 和 yTrue 相同,则认为是准确的。

然后,我们通过将准确预测的记录数除以记录总数来计算分类准确性。

由于分类准确性寻找最大值的索引,yPred 可以是 logit 或预测的概率。

密码

输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题 2。以下数据的分类准确度值是多少?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

提示。上面的 yPred 可能看起来不寻常,因为它有多个 1。但是,Keras 处理它们时不会抛出任何错误,因为当最大值出现多次时,argmax()会返回第一个匹配项的索引。

TopK 分类准确性

TopK 分类准确性计算目标(非零 yTrue)在前 K 个预测(yPred)中的记录的百分比。

郑重声明:

  1. 我们按照概率值的降序排列 yPred 预测。
  2. 如果非零 yTrue 的索引中存在的 yPred 的秩小于或等于 K,则认为它是准确的。

然后,我们通过将准确预测的记录数除以记录总数来计算 TopK 分类准确性。

密码

输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**问题 3。**上述实验输出 100%作为 TopK 分类准确率的最小 K 是多少?

稀疏 TopK 分类准确度

稀疏前 K 分类准确性计算整数目标(yTrue)在前 K 个预测(yPred)中的记录的百分比。

yTrue 由非零目标的索引(0 到 n-1)组成,而不是像 TopK 分类精度中那样的一次性目标。

郑重声明:

  1. 我们按照概率值的降序排列 yPred 预测。
  2. 如果非零 yTrue 的索引中存在的 yPred 的秩小于或等于 K,则认为它是准确的。

然后,我们通过将准确预测的记录数除以记录总数来计算稀疏 TopK 分类准确度。

密码

输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**问题 4。**上述实验输出 100%作为稀疏 TopK 分类准确率的最小 K 是多少?

结论

概括地说,Keras 提供了五种不同的度量来衡量分类器的预测准确性。根据数据的性质,特定的方法可能比其他方法更有帮助,也更相关。看完这篇文章,我希望你能明智地选择一个指标,并准确地解读它。

答案

  1. ㈠100%,㈡80%
  2. 100%
  3. K = 4
  4. K = 2

更多信息,请参考 Keras 的文档

最后,我想感谢我的朋友萨姆校对了这篇文章。

Keras 和 R:用序列模型预测血糖水平

原文:https://towardsdatascience.com/keras-and-r-predicting-blood-glucose-levels-with-the-sequential-model-596efe89a6b8?source=collection_archive---------22-----------------------

这个例子说明了如何使用 Keras 和 R 来实现基于回归的神经网络。

随着 TensorFlow 2.0 的出现, Keras 现在是这个版本的默认 API。Keras 用于构建深度学习目的的神经网络。因此,Keras 是对大型数据集进行分析的非常有用的工具。

然而,你知道 Keras API 也可以在 R 中运行吗?

在这个例子中, Keras 用于生成一个神经网络,目的是解决 r 中的一个回归问题

具体来说,皮马印第安人糖尿病数据集用于使用相关特征预测患者的血糖水平。

在这方面,本文概述了:

  • R 中的特征选择方法
  • 如何在 Keras 中定义顺序模型
  • 验证和测试模型预测的方法

数据集

在这个例子中,Pima Indians 糖尿病数据集被分成三个独立的数据集。

训练和验证: 皮马-印度人-diabetes1.csv 。原始数据集的 80%是从完整数据集分割出来的。反过来,该数据集的 70%用于训练模型,剩余的 30%用于验证预测。

测试: 皮马-印第安人-糖尿病 2.csv皮马-印第安人-糖尿病 3.csv 。原始数据集的剩余 20%用作看不见的数据,以确定模型生成的预测在处理全新数据时是否表现良好。 pima-indians-diabetes2 包含特征(或自变量),而 pima-indians-diabetes3 包含因变量(血糖水平)。

特征选择

特征选择的目的是确定那些对因变量影响最大的特征。

在我们的例子中,有八个特征,其中一些在确定血糖水平时比其他的更重要。

这里使用的两种特征选择方法是:

  • 相关图
  • 多元线性回归

相关图

相关图使我们能够直观地确定:

  1. 与因变量高度相关的特征
  2. 彼此高度相关的特征

如果某些特征与血糖水平高度相关,那么这表明这些特征在预测血糖水平方面是重要的。具有低相关性的特征被指示为不重要。

然而,彼此高度相关的特征将表明这些特征中的一些是多余的(因为它们实际上试图解释相同的事情)。

这是第一个相关图:

M <- cor(diabetes1)
corrplot(M, method = "circle")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到胰岛素结果变量与葡萄糖变量特别相关,而年龄怀孕胰岛素皮肤厚度之间也有相关性。

但是,我们可以更详细地了解并获得每个特征的特定相关系数:

corrplot(M, method = "number")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多元线性回归

多元线性回归的目的是:

  1. 在解释因变量时,确定每个特征的系数的大小和性质。
  2. 确定每个特征的重要性或不重要性。

以下是线性回归的结果:

Call:
lm(formula = Glucose ~ Pregnancies + Outcome + Age + DiabetesPedigreeFunction + 
    BMI + Insulin + SkinThickness + BloodPressure, data = diabetes1)Residuals:
    Min      1Q  Median      3Q     Max 
-68.709 -18.148  -2.212  15.176  80.950 Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)              78.401064   6.363612  12.320  < 2e-16 ***
Pregnancies              -0.481865   0.363730  -1.325  0.18575    
Outcome                  25.590805   2.384153  10.734  < 2e-16 ***
Age                       0.527262   0.106097   4.970  8.8e-07 ***
DiabetesPedigreeFunction  0.052534   3.198192   0.016  0.98690    
BMI                       0.318452   0.167106   1.906  0.05718 .  
Insulin                   0.082208   0.009843   8.352  4.8e-16 ***
SkinThickness            -0.202236   0.077372  -2.614  0.00918 ** 
BloodPressure             0.083865   0.058081   1.444  0.14929    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1Residual standard error: 24.94 on 590 degrees of freedom
Multiple R-squared:  0.362,	Adjusted R-squared:  0.3533 
F-statistic: 41.84 on 8 and 590 DF,  p-value: < 2.2e-16

在 5%的水平上,结果年龄胰岛素皮肤厚度被认为是显著的。其他特征在 5%的水平上被认为是不重要的。

使用 Breusch-Pagan 进行异方差检验

在这种情况下,没有必要对多重共线性进行正式测试,因为相关图显示了彼此高度相关的要素。

然而,可能存在异方差(跨标准误差的不均匀方差),例如,由于患者的年龄不同。为了检验这一点,进行了 Breusch-Pagan 检验——p 值低于 0.05 表示存在异方差。

> bptest(fit) studentized Breusch-Pagan testdata:  fit
BP = 36.585, df = 8, p-value = 1.372e-05

由于表明存在异方差,所以运行稳健的回归——特别是使用 Huber 权重。这样做的目的是降低数据集中异常值的价值。

> # Huber Weights (Robust Regression)
> summary(rr.huber <- rlm(Glucose ~ Pregnancies + Outcome + Age + DiabetesPedigreeFunction + BMI + Insulin + SkinThickness + BloodPressure, data=diabetes1))Call: rlm(formula = Glucose ~ Pregnancies + Outcome + Age + DiabetesPedigreeFunction + 
    BMI + Insulin + SkinThickness + BloodPressure, data = diabetes1)
Residuals:
    Min      1Q  Median      3Q     Max 
-68.627 -16.842  -1.543  15.576  83.793 Coefficients:
                         Value   Std. Error t value
(Intercept)              78.3319  6.2990    12.4357
Pregnancies              -0.4675  0.3600    -1.2984
Outcome                  25.0513  2.3599    10.6152
Age                       0.5448  0.1050     5.1881
DiabetesPedigreeFunction -0.5482  3.1657    -0.1732
BMI                       0.3297  0.1654     1.9935
Insulin                   0.0925  0.0097     9.4912
SkinThickness            -0.2530  0.0766    -3.3032
BloodPressure             0.0673  0.0575     1.1706Residual standard error: 24.53 on 590 degrees of freedom

590 自由度上,双尾 t 临界值如下:

> abs(qt(0.05/2, 590))
[1] 1.963993

当 t 统计量> t 临界值时,零假设被拒绝。对此,结局年龄身体质量指数胰岛素皮肤厚度的 t 值绝对值大于临界值。

考虑到相关图和多元线性回归的发现,选择结果年龄胰岛素皮肤厚度作为分析的相关特征。

数据准备

既然已经选择了相关特征,就可以构建神经网络了。在此之前:

  1. 最大-最小归一化用于在 0 和 1 之间缩放每个变量。这是为了确保变量之间有一个共同的范围,以便神经网络可以正确地解释它们。
normalize <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}maxmindf <- as.data.frame(lapply(df, normalize))
attach(maxmindf)
maxmindf<-as.matrix(maxmindf)
  1. 训练验证集被分成 70/30。
ind <- sample(2, nrow(maxmindf), replace=TRUE, prob = c(0.7,0.3))X_train <- maxmindf[ind==1, 1:4]
X_val <- maxmindf[ind==2, 1:4]
y_train <- maxmindf[ind==1, 5]
y_val <- maxmindf[ind==2, 5]

顺序模型

现在,定义了顺序模型。四个输入特征(结果、年龄、胰岛素、皮肤厚度)包括在输入层中,该层中定义了 9 个神经元。定义一个具有 60 个神经元的隐藏层,并且定义一个具有 1 个神经元的线性输出层。

正如法尔哈德·马利克在本文中所解释的,每层神经元的数量配置如下:

  • **输入层:**输入层的神经元数量计算如下:

Number of features in the training set + 1

在这种情况下,由于训练集中有 8 个特征,因此相应地定义了 9 个输入神经元。

  • **隐藏层:**定义一个隐藏层,因为单个层适用于处理大多数数据集。隐藏层中神经元的数量确定如下:
Training Data Samples/Factor * (Input Neurons + Output Neurons)

在这种情况下,将因子设置为 1,该因子的目的是防止过度拟合。因子可以取 1 到 10 之间的值。输入层中有 9 个神经元,输出层中有 1 个神经元,训练集中有 599 个观察值,隐藏层被分配了 60 个神经元。

  • **输出层:**由于这是结果层,输出层默认取值 1。
model <- keras_model_sequential() 
model %>% 
  layer_dense(units = 9, activation = 'relu', kernel_initializer='RandomNormal', input_shape = c(4)) %>% 
  layer_dense(units = 60, activation = 'relu') %>%
  layer_dense(units = 1, activation = 'linear')summary(model)

以下是输出:

Model: "sequential"
________________________________________________________________________________
Layer (type)                        Output Shape                    Param #     
================================================================================
dense (Dense)                       (None, 9)                       45          
________________________________________________________________________________
dense_1 (Dense)                     (None, 60)                      600         
________________________________________________________________________________
dense_2 (Dense)                     (None, 1)                       61          
================================================================================
Total params: 706
Trainable params: 706
Non-trainable params: 0
________________________________________________________________________________

该模型现在经过 30 个时期的训练,并基于其损失和平均绝对误差进行评估。假设因变量为区间,则使用均方差来确定预测值和实际值之间的偏差。

model %>% compile(
  loss = 'mean_squared_error',
  optimizer = 'adam',
  metrics = c('mae')
)history <- model %>% fit(
  X_train, y_train, 
  epochs = 30, batch_size = 50, 
  validation_split = 0.2
)

模型评估

预测值和实际值会按比例还原为其原始格式:

model %>% evaluate(X_val, y_val)
model
pred <- data.frame(y = predict(model, as.matrix(X_val)))
predicted=pred$y * abs(diff(range(df$Glucose))) + min(df$Glucose)
actual=y_val * abs(diff(range(df$Glucose))) + min(df$Glucose)
df<-data.frame(predicted,actual)
attach(df)

以下是输出:

$loss
    0.0266957393988254
$mae
    0.132186755537987Model
Model: "sequential"
________________________________________________________________________________
Layer (type)                        Output Shape                    Param #     
================================================================================
dense (Dense)                       (None, 9)                       45          
________________________________________________________________________________
dense_1 (Dense)                     (None, 60)                      600         
________________________________________________________________________________
dense_2 (Dense)                     (None, 1)                       61          
================================================================================
Total params: 706
Trainable params: 706
Non-trainable params: 0
________________________________________________________________________________

这是损失和平均绝对误差的曲线图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该模型产生的损失略高于 2%,平均绝对误差略高于 13%。

MLmetrics 库也可用于计算 MAPE(平均绝对百分比误差)。

install.packages("MLmetrics")
library(MLmetrics)
MAPE(predicted, actual)

验证集的 MAPE 为 18% 。增加模型中隐藏层的数量并不能提高 MAPE,因此决定在模型配置中保留一个隐藏层。

预测和测试数据

尽管这个模型显示了强大的预测能力,我们的工作还没有完成。

虽然该模型在验证数据上表现良好,但我们现在需要评估该模型在完全看不见的数据上是否也表现良好。

从 pima-indians-diabetes2 加载特征变量,并再次调用 max0min 归一化:

normalize <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}maxmindf2 <- as.data.frame(lapply(df2, normalize))
attach(maxmindf2)

使用 R 中的预测函数,为葡萄糖变量生成预测:

pred_test <- data.frame(y = predict(model, as.matrix(maxmindf2)))
predicted_test = pred_test$y * abs(diff(range(diabetes1$Glucose))) + min(diabetes1$Glucose)
predicted_test

然后将预测值与 pima-indians-diabetes 中的实际值进行比较 3:

actual_test = diabetes3$Glucose
df2<-data.frame(predicted_test,actual_test)
attach(df2)
df2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,使用测试值计算平均绝对百分比误差:

MAPE(predicted_test, actual_test)

计算出 17%的平均百分比误差:

0.177895157636775

观察到,虽然平均百分比误差略高于使用训练和验证数据计算的误差,但是该模型在测试集上的所有未观察到的观察中预测血糖水平仍然表现良好。

结论

在本例中,我们看到:

  • 如何在 R 中实现特征选择方法
  • 使用 Keras API 构建一个神经网络来分析回归数据
  • 使用测试数据的仪表预测精度

非常感谢你的时间!你也可以在 michael-grogan.com 找到更多我的数据科学内容。

免责声明:本文是在“原样”的基础上编写的,没有担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。

Keras,告诉我我的书的类型

原文:https://towardsdatascience.com/keras-tell-me-the-genre-of-my-book-a417d213e3a1?source=collection_archive---------25-----------------------

利用递归神经网络的能力进行分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Shutterstock 上的十亿张照片

对于任何希望深入了解递归神经网络(RNN)如何工作的人来说,我希望这个简单的教程是一个很好的读物!

我们使用的数据集包括我从 GoodReads 收集的书籍描述和流派分类,这是一个使用 RNN 的分类方法解决典型分类问题的很好的例子。对于这个项目,我们将把我们的问题简化为一个二元分类问题,我们将使用 RNN 对全文图书描述进行情感分析!

想想这有多神奇。我们将训练一个人工神经网络如何“阅读”书籍描述并猜测其流派。

由于理解书面语言需要跟踪一个句子中的所有单词,我们需要一个递归神经网络来保存以前出现过的单词的“记忆”,因为它随着时间的推移“阅读”句子。

特别是,我们将使用 LSTM(长短期记忆)细胞,因为我们真的不想太快“忘记”单词——句子中早期的单词会显著影响句子的意思。

GitHub 资源库这里

这里是我们这个项目需要的一些重要的库——Numpy、Pandas、matplotlib、Plotly 和 Tensorflow。我们将使用 Keras——运行在 TensorFlow(或 CNTK 或 Theano)之上的东西。Keras 允许我们更少地考虑原始模型拓扑,直接投入到简单快速的原型开发中。实验越快,结果越好:)

import numpy as np
import pandas as pdimport matplotlib.pyplot as plt
import tensorflow
import plotly.offline as pyoff
import plotly.graph_objs as go
pyoff.init_notebook_mode()

让我们首先导入我从好的阅读中收集的数据,看看我们需要处理什么。

bookdata_path = 'book_data.csv'
testdata_path = 'book_data18.csv'
book = pd.read_csv(bookdata_path)
test = pd.read_csv(testdata_path)book.columns

’ book_authors ':作者姓名字符串

’ desc 图书’:描述字符串

’ book_edition ':图书字符串的不同版本

’ book_format ':精装/平装字符串

’ book_pages ':一本书的页数

“图书评级”:图书评级浮动

’ book_rating_count ':评分数 int

’ book_review_count ':评论数 int

’ book_title ':图书字符串标题

“体裁”:书串的体裁

’ image_url ':图书图像 url 字符串

数据清理和数据探索

这一步占用了每个数据科学家的大部分时间。我们查看数据框中的每一列,找出我们可能面临的任何潜在问题。

一些常见问题包括:

  1. 缺少值
  2. 涉及不同的语言
  3. 非 Ascii 字符
  4. 无效描述
  5. 描述中缺少空格,例如 HelloILike toEat

我建议写下你的数据清理步骤的所有发现,这样你就可以不断地查阅你的笔记,确保你不会错过任何一个步骤!

事不宜迟,以下是我的发现。

  1. 语料库中存在多种语言——我是想保留所有语言还是只保留英语描述?我的数据集中的整体语言分布如何?
  2. 每本书至少有一个用户定义的流派,我的数据集中有多少流派?流派分布是怎样的?有多少独特的流派?

1。删除无效格式的描述

因为我们在预测类型,类型将是我们的标签,而特征将来自每本书的描述。我发现在一些条目中有格式错误——这就是 langdetect 出现的地方。我们将实现一个函数来删除任何具有无效描述格式的行。

from langdetect import detectdef remove_invalid_lang(df):
    invalid_desc_idxs=[]
    for i in df.index:
        try:
            a=detect(df.at[i,'book_desc'])
        except:
            invalid_desc_idxs.append(i)

    df=df.drop(index=invalid_desc_idxs)
    return dfbook = remove_invalid_lang(book)
test = remove_invalid_lang(test)

2.仅获取英文描述

我注意到在我的数据集中涉及到许多语言。为了简单起见,我只想要英文的书籍描述。

book[‘lang’]=book[‘book_desc’].map(lambda desc: detect(desc))
test['lang']=test['book_desc'].map(lambda desc: detect(desc))

langdetect 允许我们将每个描述映射到一个 ISO 639-1 值,以使我们在过滤掉英文描述时更加轻松。用它!然后,我将从维基百科中检索语言列表及其各自的 ISO 值。

lang_lookup = pd.read_html('[https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes')[1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes')[1)]
langpd = lang_lookup[['ISO language name','639-1']]
langpd.columns = ['language','iso']def desc_lang(x):
    if x in list(langpd['iso']):
        return langpd[langpd['iso'] == x]['language'].values[0]
    else:
        return 'nil'book['language'] = book['lang'].apply(desc_lang)
test['language'] = test['lang'].apply(desc_lang)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从图中可以清楚地看到,绝大多数描述都是英文的。让我们仔细看看其他语言的分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用这些一行程序从我们的测试和训练数据集中检索所有的英语书!

book = book[book['language']=='English']
test = test[test['language']=='English']

3.查看所有可用的流派

这就是我们的流派专栏的样子。我们有许多由“|”分隔的用户定义的风格,所以我们必须清理它。

幻想|年轻人|小说

在每个数据科学项目中,了解数据的分布非常重要,最好的方法是绘制图表!我真的很喜欢使用 Plotly 进行数据可视化,但是 matplotlib 和 seaborn 也可以完成这项工作。

这是我的功能,获取每本书的所有流派,并绘制成图表。

def genre_count(x):
    try:
        return len(x.split('|'))
    except:
        return 0book['genre_count'] = book['genres'].map(lambda x: genre_count(x))plot_data = [
    go.Histogram(
        x=book['genre_count']
    )
]
plot_layout = go.Layout(
        title='Genre distribution',
        yaxis= {'title': "Frequency"},
        xaxis= {'title': "Number of Genres"}
    )
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我必须说,大多数书都有大约 5-6 种类型,而且分布非常均匀。

def genre_listing(x):
    try:
        lst = [genre for genre in x.split("|")]
        return lst
    except: 
        return []book['genre_list'] = book['genres'].map(lambda x: genre_listing(x))genre_dict = defaultdict(int)
for idx in book.index:
    g = book.at[idx, 'genre_list']
    if type(g) == list:
        for genre in g:
            genre_dict[genre] += 1genre_pd = pd.DataFrame.from_records(sorted(genre_dict.items(), key=lambda x:x[1], reverse=True), columns=['genre', 'count'])

上面的代码给了我一个所有流派的字典,以及它们在整个语料库中的总数。让我们进入剧情。

plot_data = [
 go.Bar(
 x=genre_pd[‘genre’],
 y=genre_pd[‘count’]
 )
]
plot_layout = go.Layout(
 title=’Distribution for all Genres’,
 yaxis= {‘title’: “Count”},
 xaxis= {‘title’: “Genre”}
 )
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看那些数量很少的类型是不实际的,因为它对我们来说没有什么价值。我们只想查看代表数据集的顶级独特流派,因此让我们挑选 50 个顶级流派来查看!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果我们看一下 genre_list 这一栏,如果“小说”至少被列为一种类型,那么这本书就被归类为小说。通过观察,如果一本书在其流派列表中至少有小说,同一列表中的所有其他流派也将与小说密切相关。由此,我可以比较我的数据集中小说和非小说书籍的数量,并将其转化为二元分类问题!

def determine_fiction(x):
    lower_list = [genre.lower() for genre in x]
    if 'fiction' in lower_list:
        return 'fiction'
    elif 'nonfiction' in lower_list:
        return 'nonfiction'
    else:
        return 'others'
book['label'] = book['genre_list'].apply(determine_fiction)
test['label'] = test['genre_list'].apply(determine_fiction)

4.清理文本

下面是我的函数,可以删除任何非 Ascii 字符和标点符号。

def _removeNonAscii(s): 
    return "".join(i for i in s if ord(i)<128)def clean_text(text):
    text = text.lower()
    text = re.sub(r"what's", "what is ", text)
    text = text.replace('(ap)', '')
    text = re.sub(r"\'s", " is ", text)
    text = re.sub(r"\'ve", " have ", text)
    text = re.sub(r"can't", "cannot ", text)
    text = re.sub(r"n't", " not ", text)
    text = re.sub(r"i'm", "i am ", text)
    text = re.sub(r"\'re", " are ", text)
    text = re.sub(r"\'d", " would ", text)
    text = re.sub(r"\'ll", " will ", text)
    text = re.sub(r'\W+', ' ', text)
    text = re.sub(r'\s+', ' ', text)
    text = re.sub(r"\\", "", text)
    text = re.sub(r"\'", "", text)    
    text = re.sub(r"\"", "", text)
    text = re.sub('[^a-zA-Z ?!]+', '', text)
    text = _removeNonAscii(text)
    text = text.strip()
    return textdef cleaner(df):
    df = df[df['label'] != 'others']
    df = df[df['language'] != 'nil']
    df['clean_desc'] = df['book_desc'].apply(clean_text)return df

只需拨打:

clean_book = cleaner(book)
clean_test = cleaner(test)

我们现在对每本书都有了一个“干净”的描述!

“胜利会让你出名。输了就意味着必死无疑。”成为

“胜利会让你出名,失败意味着死亡”

为模型准备我们的数据

现在有趣的部分来了。书籍描述是我们的预测器,所以我们必须特别注意!我们需要确保每个描述都是相同的格式和长度。

使用固定输入长度可以提高模型训练期间的性能,因为这样可以创建固定形状的张量,从而获得更稳定的权重。因此,我们将进行裁剪和填充——如果原始描述长度比最佳长度短,则将描述裁剪到最佳长度并用空值填充的过程。

我们如何确定最佳长度?

绘制描述长度的分布图,并观察最“常见”的描述长度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

哇,这是一个非常倾斜的分布!但是我们知道大多数书的描述长度小于 500。我想画出累积分布函数(CDF ),来观察每一级描述长度的“图书数量”。

len_df_bins=clean_book.desc_len.value_counts(bins=100, normalize=True).reset_index().sort_values(by=['index'])len_df_bins['cumulative']=len_df_bins.desc_len.cumsum()len_df_bins['index']=len_df_bins['index'].astype('str')len_df_bins.iplot(kind='bar', x='index', y='cumulative')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

约 92.7%的记录字数在 277 字以下。因此,我决定将我的最大阈值设置为 250 个单词。我们还需要一个最小阈值,我将把它设置为 6,因为任何少于 5 个单词的描述都不太可能足以确定类型。

1.剪辑和填充

对于描述少于 250 个单词的记录,我们将用空值填充它们,而对于描述多于 250 个单词的记录,我们将对它们进行裁剪,只包含前 250 个单词。

RNN 从左到右读取令牌序列,并输出一个预测书是小说还是非小说。这些标记的内存被一个接一个地传递给最终的标记,因此,对序列进行预填充而不是后填充是很重要的。这意味着零添加在令牌序列之前,而不是之后。存在后填充可能更有效的情况,例如在双向网络中。

min_desc_length=6
max_desc_length=250clean_book=clean_book[(clean_book.clean_desc.str.split().apply(len)>min_desc_length)].reset_index(drop=True)

上面的代码过滤掉了所有少于 6 个单词的描述。

vocabulary=set() #unique list of all words from all descriptiondef add_to_vocab(df, vocabulary):
    for i in df.clean_desc:
        for word in i.split():
            vocabulary.add(word)
    return vocabularyvocabulary=add_to_vocab(clean_book, vocabulary)#This dictionary represents the mapping from word to token. Using token+1 to skip 0, since 0 will be used for padding descriptions with less than 200 words
vocab_dict={word: token+1 for token, word in enumerate(list(vocabulary))}#This dictionary represents the mapping from token to word
token_dict={token+1: word for token, word in enumerate(list(vocabulary))}assert token_dict[1]==token_dict[vocab_dict[token_dict[1]]]def tokenizer(desc, vocab_dict, max_desc_length):
    '''
    Function to tokenize descriptions
    Inputs:
    - desc, description
    - vocab_dict, dictionary mapping words to their corresponding tokens
    - max_desc_length, used for pre-padding the descriptions where the no. of words is less than this number
    Returns:
    List of length max_desc_length, pre-padded with zeroes if the desc length was less than max_desc_length
    '''
    a=[vocab_dict[i] if i in vocab_dict else 0 for i in desc.split()]
    b=[0] * max_desc_length
    if len(a)<max_desc_length:
        return np.asarray(b[:max_desc_length-len(a)]+a).squeeze()
    else:
        return np.asarray(a[:max_desc_length]).squeeze()len(vocabulary)
85616

我们有 85616 个独特的单词。最后,裁剪和填充步骤的最后一步,标记每个描述。

clean_test['desc_tokens']=clean_test['clean_desc'].apply(tokenizer, args=(vocab_dict, max_desc_length))

2.列车测试分离

当数据集不平衡时,即目标变量(虚构/非虚构)的分布不均匀时,我们应该确保训练-验证分裂是分层的。这确保了目标变量的分布在训练和验证数据集中得到保留。

我们也可以尝试随机欠采样来减少虚构样本的数量,但是,在这种情况下,我将使用分层采样。原因如下。

分层随机样本用于可以很容易分成不同的子群或子集的人群,在我们的例子中,是虚构或非虚构的。我将从每个标签中随机选择与群体规模和人口数量成比例的记录。每个记录必须只属于一个阶层(标签),我确信每个记录是互斥的,因为一本书只能是小说或非小说。重叠的地层会增加某些数据被包括在内的可能性,从而扭曲样本。

分层抽样优于随机欠抽样的一个优点是,因为它使用特定的特征,所以它可以根据用于划分不同子集的内容来提供更准确的图书表示,而且我们不必删除任何可能对我们的模型有用的记录。

def stratified_split(df, target, val_percent=0.2):
    '''
    Function to split a dataframe into train and validation sets, while preserving the ratio of the labels in the target variable
    Inputs:
    - df, the dataframe
    - target, the target variable
    - val_percent, the percentage of validation samples, default 0.2
    Outputs:
    - train_idxs, the indices of the training dataset
    - val_idxs, the indices of the validation dataset
    '''
    classes=list(df[target].unique())
    train_idxs, val_idxs = [], []
    for c in classes:
        idx=list(df[df[target]==c].index)
        np.random.shuffle(idx)
        val_size=int(len(idx)*val_percent)
        val_idxs+=idx[:val_size]
        train_idxs+=idx[val_size:]
    return train_idxs, val_idxs_, sample_idxs = stratified_split(clean_book, 'label', 0.1)train_idxs, val_idxs = stratified_split(clean_book, 'label', val_percent=0.2)
sample_train_idxs, sample_val_idxs = stratified_split(clean_book[clean_book.index.isin(sample_idxs)], 'label', val_percent=0.2)classes=list(clean_book.label.unique())sampling=Falsex_train=np.stack(clean_book[clean_book.index.isin(sample_train_idxs if sampling else train_idxs)]['desc_tokens'])
y_train=clean_book[clean_book.index.isin(sample_train_idxs if sampling else train_idxs)]['label'].apply(lambda x:classes.index(x))x_val=np.stack(clean_book[clean_book.index.isin(sample_val_idxs if sampling else val_idxs)]['desc_tokens'])
y_val=clean_book[clean_book.index.isin(sample_val_idxs if sampling else val_idxs)]['label'].apply(lambda x:classes.index(x))x_test=np.stack(clean_test['desc_tokens'])
y_test=clean_test['label'].apply(lambda x:classes.index(x))

x_train 和 y_train 将用于训练我们的模型,而 x_val 和 y_val 用于检查我们模型的验证准确性。我们的目标是适度的高训练精度和高验证精度,以确保我们不会过度拟合。

过度拟合是指我们的模型在对他们接受训练的数据进行预测时表现良好,但未能对看不见的数据(验证数据)进行归纳。另一方面,当我们的模型甚至在训练数据上表现糟糕时,就会出现欠拟合。

过度拟合的模型将具有高方差和低偏差,而欠拟合的模型将具有高偏差和低方差。

误差=偏差+方差

我们的主要目标是减少误差,而不是偏差或方差,因此最佳复杂度是中间值。

模型结构

在这一步,我们的训练数据只是一个数字矩阵,它是我们模型的必要输入。至于我们的 y 标签,现在是 1(虚构)或者 0(非虚构)。

重述

概括一下,我们有一堆书籍描述,它们被转换成由整数表示的单词向量,还有一个二元情感分类可供学习。RNN 病会很快发作,为了让事情在我们的小电脑上易于管理,我们将描述限制在它们的前 250 个单词。不要忘记,这也有助于提高我们的模型训练的性能!

初始化我们的模型

现在让我们建立我们的神经网络模型!考虑到 LSTM 递归神经网络是多么复杂,用 Keras 做到这一点是多么容易,这真的令人惊讶。

我们将从嵌入层开始,这只是将输入数据转换为更适合神经网络的固定大小的密集向量的一个步骤。您通常会看到这与我们这里的基于索引的文本数据结合在一起。嵌入层帮助我们降低问题的维度。如果我们对词汇表中的单词进行一次性编码,每个单词将由一个向量表示,这个向量的大小等于词汇表本身的大小,在本例中是 85643。由于每个样本将是一个大小为(词汇×记号数量)的张量,即(85643×250),该层的大小对于 LSTM 来说将太大而不能消耗,并且对于训练过程来说将是非常资源密集和耗时的。如果我使用嵌入,我的张量大小将只有 250 x 250!WAYYY 小一点!

一个热编码将导致一个巨大的稀疏矩阵,而嵌入给我们一个密集的矩阵表示。嵌入长度越高,我们的模型可以学习的表示就越复杂,因为我们的嵌入层学习每个单词的固定长度的“表示”。

接下来,我们只需为 RNN 本身设置一个 LSTM 图层。就这么简单。我们指定 200 来匹配嵌入层的输出大小,并删除项以避免过度拟合,这是 RNN 特别容易发生的。顺便说一下,你可以选择 200 以外的任何数字,它只指定了该层中隐藏神经元的数量。

最后,我们只需要用 sigmoid 激活函数将它归结为最后一层中的单个神经元,以选择我们的二元情感分类 0 或 1。

我将解释为什么我为模型选择了超参数,但是如果你想跳到代码,请随意跳过这一部分!

我们应该使用多少 LSTM 层?速度-复杂度权衡

通常 1 层足以发现简单问题的趋势,2 层足以发现相当复杂的特征。对于一系列选择(层数),我们可以在固定的时期数之后比较我们的模型的准确性,如果我们发现即使在添加更多层之后验证准确性也没有显著变化,我们可以选择最小层数。

我们应该添加多少隐藏节点到我们的 LSTM 层?
Ns:训练数据中样本的数量
Ni:输入神经元的数量
No:输出神经元的数量
alpha:缩放因子(指示您希望您的模型有多通用,或者您希望防止过度拟合的程度)

通式:Ns / [alpha * (Ni + No)]

添加脱落层。精度-防过拟合权衡

通过在训练期间忽略随机选择的神经元来防止过度拟合,并降低对单个神经元的特定权重的敏感性。这迫使我们的模型展开 it 学习。

添加密集层

因为我们有 1 个输出标签来表示小说或非小说,所以我们将有 1 维输出。

添加激活层

有许多激活功能可供选择,所以这取决于我们的目标。

在这种情况下,我们希望输出是虚构/非虚构的,因此 Softmax 或 Sigmoid 函数会很好。

Sigmoid 函数基本上输出概率,我们通常会使用 sigmoid 进行二值分类。

Softmax 函数输出介于 0 和 1 之间的值,使得所有输出值的总和等于 1。基本上你得到了每一类的概率,它们的和必然是 1。这使得 Softmax 非常适合多类问题。

您可以使用 Sigmoid 和 Softmax 函数调整激活层超参数,并比较验证精度!我们也可以尝试 reLU,它被广泛使用,因为它计算速度快,效果好。

选择损失函数、优化器和判断标准

由于我们面临的是二进制分类问题,二进制交叉熵将与 sigmoid 一起很好地工作,因为交叉熵函数抵消了 Sigmoid 函数两端的平稳段,因此加快了学习过程。

对于优化器,自适应矩估计(adam)已被证明在大多数实际应用中工作良好,并且仅在超参数变化很小的情况下工作良好。r

从整体准确性的角度来判断模型的性能是最容易解释最终模型性能的。

现在我们将实际训练我们的模型。像 CNN 一样,RNN 的资源非常丰富。保持相对较小的批量是让它在你的电脑上运行的关键。当然,在现实世界中,您可以利用安装在集群上的许多计算机上的 GPU 来大大提高这一规模。

代号

parameters = {'vocab': vocabulary,
              'eval_batch_size': 30,
              'batch_size': 20,
              'epochs': 5,
              'dropout': 0.2,
              'optimizer': 'Adam',
              'loss': 'binary_crossentropy',
              'activation':'sigmoid'}

**def** bookLSTM(x_train, y_train, x_val, y_val, params):
    model = Sequential()
    model.name="Book Model"
    model.add(Embedding(len(params['vocab'])+1, output_dim=x_train.shape[1], input_length=x_train.shape[1]))
    model.add(LSTM(200, return_sequences=**True**))
    model.add(Dropout(params['dropout']))
    model.add(LSTM(200))
    model.add(Dense(1, activation=params['activation']))
    model.compile(loss=params['loss'],
              optimizer=params['optimizer'],
              metrics=['accuracy'])
    print(model.summary())
    model.fit(x_train, 
          y_train,
          validation_data=(x_val, y_val),
          batch_size=params['batch_size'], 
          epochs=params['epochs'])
    results = model.evaluate(x_test, y_test, batch_size=params['eval_batch_size'])
    **return** model

BookModel1 = bookLSTM(x_train, y_train, x_val, y_val, parameters)------ Model Summary ------Model: "Book Model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_2 (Embedding)      (None, 200, 200)          17123400  
_________________________________________________________________
lstm_1 (LSTM)                (None, 200, 200)          320800    
_________________________________________________________________
dropout_1 (Dropout)          (None, 200, 200)          0         
_________________________________________________________________
lstm_2 (LSTM)                (None, 200)               320800    
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 201       
=================================================================
Total params: 17,765,201
Trainable params: 17,765,201
Non-trainable params: 0
_________________________________________________________________Train on 23387 samples, validate on 5845 samples
Epoch 1/5
23387/23387 [==============================] - 270s 12ms/step - loss: 0.3686 - accuracy: 0.8447 - val_loss: 0.2129 - val_accuracy: 0.9129
Epoch 2/5
23387/23387 [==============================] - 282s 12ms/step - loss: 0.1535 - accuracy: 0.9476 - val_loss: 0.2410 - val_accuracy: 0.9013
Epoch 3/5
23387/23387 [==============================] - 279s 12ms/step - loss: 0.0735 - accuracy: 0.9771 - val_loss: 0.2077 - val_accuracy: 0.9357
Epoch 4/5
23387/23387 [==============================] - 280s 12ms/step - loss: 0.0284 - accuracy: 0.9924 - val_loss: 0.2512 - val_accuracy: 0.9334
Epoch 5/5
23387/23387 [==============================] - 293s 13ms/step - loss: 0.0161 - accuracy: 0.9957 - val_loss: 0.2815 - val_accuracy: 0.9290
657/657 [==============================] - 3s 5ms/step

我注意到,随着我的纪元从 3 增加到 5,测试精度增加了,但是验证精度降低了。这意味着模型更好地拟合了训练集,但它失去了对新数据进行预测的能力,这表明我的模型开始拟合噪声,并开始过度拟合。让我们改变参数!

parameters = {'vocab': vocabulary,
              'eval_batch_size': 30,
              'batch_size': 20,
              'epochs': 2,
              'dropout': 0.2,
              'optimizer': 'Adam',
              'loss': 'binary_crossentropy',
              'activation':'sigmoid'}

**def** bookLSTM(x_train, y_train, x_val, y_val, params):
    model = Sequential()
    model.name="Book Model2"
    model.add(Embedding(len(params['vocab'])+1, output_dim=x_train.shape[1], input_length=x_train.shape[1]))
    model.add(LSTM(200, return_sequences=**True**))
    model.add(Dropout(params['dropout']))
    model.add(LSTM(200))
    model.add(Dense(1, activation=params['activation']))
    model.compile(loss=params['loss'],
              optimizer=params['optimizer'],
              metrics=['accuracy'])
    print(model.summary())
    model.fit(x_train, 
          y_train,
          validation_data=(x_val, y_val),
          batch_size=params['batch_size'], 
          epochs=params['epochs'])
    results = model.evaluate(x_test, y_test, batch_size=params['eval_batch_size'])
    **return** model

BookModel2 = bookLSTM(x_train, y_train, x_val, y_val, parameters)------ Model Summary ------Model: "Book Model2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_3 (Embedding)      (None, 200, 200)          17123400  
_________________________________________________________________
lstm_3 (LSTM)                (None, 200, 200)          320800    
_________________________________________________________________
dropout_2 (Dropout)          (None, 200, 200)          0         
_________________________________________________________________
lstm_4 (LSTM)                (None, 200)               320800    
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 201       
=================================================================
Total params: 17,765,201
Trainable params: 17,765,201
Non-trainable params: 0
_________________________________________________________________Train on 23387 samples, validate on 5845 samples
Epoch 1/2
23387/23387 [==============================] - 337s 14ms/step - loss: 0.3136 - accuracy: 0.8690 - val_loss: 0.1937 - val_accuracy: 0.9305
Epoch 2/2
23387/23387 [==============================] - 332s 14ms/step - loss: 0.1099 - accuracy: 0.9636 - val_loss: 0.1774 - val_accuracy: 0.9341
657/657 [==============================] - 3s 4ms/step

我们仅在两个时期内就实现了 96.4%的训练准确率和 93.4%的验证准确率!对于 2 次运行来说,这已经很不错了。我尝试了许多不同的超参数,仅运行 10 个纪元就花了我一个小时,所以…

你自己试试,让我知道你选择了什么参数来获得比我更高的验证准确率!

测试我们模型的简单函数

在这部引人入胜的《纽约时报》畅销书《纸与火的女孩》续集中,雷和雷恩逃离了他们在隐秘宫殿中的压抑生活,但很快发现自由需要付出可怕的代价。雷,一个天真的乡下女孩,成为了一名皇家妓女,现在被称为月嫂,一个成功地做了别人做不到的事情的平民。但是杀死残忍的魔王并不是计划的结束——这只是开始。现在,雷和她的战士爱人雷恩必须前往王国,以获得来自遥远的反叛氏族的支持。旅途变得更加险恶,因为有人悬赏要得到雷的人头,还有阴险的怀疑威胁着要把雷和雷恩从内部分开。与此同时,在黑魔法和复仇的推动下,一个消灭叛军起义的邪恶阴谋正在成形。雷会成功地推翻帝制并保护她对任的爱情吗?还是她会成为邪恶魔法的牺牲品?

def reviewBook(model,text):
  labels = [‘fiction’, ‘nonfiction’]
  a = clean_text(fantasy)
  a = tokenizer(a, vocab_dict, max_desc_length)
  a = np.reshape(a, (1,max_desc_length))
  output = model.predict(a, batch_size=1)
  score = (output>0.5)*1
  pred = score.item()
  return labels[pred]

让我们传入我们的最终模型和样本文本,看看我们的模型是否能够仅仅根据一本书的描述准确地预测它的类型。你觉得上面这本书属于哪种流派?

reviewBook(BookModel2,fantasy)

小说

很明显在描述中有“恶魔之王”和“黑魔法”!

结论

请注意,在第二或第三个时期之后,我们训练时的验证准确性从未真正提高;我们可能只是过度适应了。在这种情况下,早期停止将是有益的。

但是再一次——停下来想想我们刚刚在这里做了什么!一种神经网络,可以“阅读”描述,并根据文本推断这本书是否是小说。它考虑了每个单词的上下文及其在评论中的位置——建立模型本身只需要几行代码!你可以用 Keras 做的事情真是不可思议。

伙计们,希望你们在阅读我的第一篇媒体文章时过得愉快。cheerrrrrrssssssssssssss

Python 中从头开始的内核回归

原文:https://towardsdatascience.com/kernel-regression-from-scratch-in-python-ea0615b23918?source=collection_archive---------11-----------------------

大家都知道线性回归,但是你知道核回归吗?

机器学习的初学者都是从学习回归的含义和线性回归算法的工作原理开始的。事实上,线性回归的易理解性、可解释性和大量有效的真实世界用例是该算法如此著名的原因。然而,有些情况下线性回归并不适合。在本文中,我们将看到这些情况是什么,什么是内核回归算法,以及它如何适应这种情况。最后,我们将从头开始编写带有高斯核的核回归算法。要阅读本文,需要具备 Python 和 numpy 的基础知识。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Clarisse CrosetUnsplash 上拍摄的照片

简单回顾一下线性回归

给定形式为 N 特征向量 x =[ x ₁、 x ₂、… ,x ₙ]的数据,该数据由 n 个特征和相应的标签向量 y 组成,线性回归试图拟合出最佳描述数据的直线。为此,它试图找到直线方程的最优系数 c ᵢ, i ∈{0,…,n}y=c₀*+cx₁+cx₂+…+cx*ₙ然后,获得的方程被用于预测新的看不见的输入向量 x ₜ.的目标 y

线性回归是一种简单的算法,不能模拟特征之间非常复杂的关系。从数学上来说,这是因为它是线性的,方程的次数为 1,这意味着线性回归总是模拟一条直线。事实上,这种线性是线性回归算法的弱点。为什么?

好吧,让我们考虑一种情况,其中我们的数据不具有直线的形式:让我们使用函数 *f(x) = x 生成数据。*如果我们使用线性回归来拟合该数据的模型,我们将永远不会接近真正的三次函数,因为我们正在寻找系数的方程没有三次项!因此,对于任何不是使用线性函数生成的数据,线性回归都很可能不足。那么,我们该怎么办?

我们可以使用另一种类型的回归,称为多项式回归,它试图找到(顾名思义)多项式方程的最佳系数,该方程的次数为 nn ⪈1.然而,多项式回归带来了另一个问题:作为一名数据分析师,您无法知道方程的次数应该是多少,才能使结果方程最适合数据。这只能通过反复试验来确定,由于在 3 阶以上,使用多项式回归建立的模型难以可视化,所以这变得更加困难。

这就是内核回归的用武之地!

什么是内核回归?

看到名字,你可能会问,如果线性回归中的‘linear’是指线性函数,多项式回归中的‘多项式’是指多项式函数,那么‘核’是什么意思?原来,它的意思是一个内核函数!那么,是什么内核函数呢?简单地说,它是一个相似性函数,接受两个输入,并指出它们有多相似。我们将很快看到在核回归中如何使用核函数。

现在谈谈内核回归。与需要学习最优参数向量 c =[ c ₁、 c ₂、… ,c ₙ]的线性和多项式回归不同,核回归是非参数回归,这意味着它通过直接对输入 x ₜ.执行计算来计算目标 y

怎么会?

给定数据点( x ᵢ, y ᵢ),内核回归通过首先为每个数据点 x ᵢ.构建一个内核 k 来进行预测然后,对于给定的新输入 x ₜ,它使用内核计算每个 x ᵢ(由 x ᵢ- x ₜ给出)的相似性得分;相似性分数充当权重 w ᵢ,其表示在预测目标 y ₜ.时该内核(以及相应的标签 y ᵢ)的重要性然后通过将权重向量 w= [ w ₁、 w ₂、… 、w ₙ]乘以标签向量 y= [ y ₁、 y ₂、… ,yₙ】来获得预测。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片:方程中的核回归

现在,可以有不同的核函数,它们产生不同类型的核回归。一种这样的类型是高斯核回归,其中构造的核的形状是高斯曲线,也称为钟形曲线。在高斯核回归的背景下,每个构建的核也可以被视为具有平均值 x ᵢ和标准偏差b的正态分布。这里,b 是控制曲线形状(特别是高斯核中的高斯曲线的宽度)的超参数。高斯内核 k 的方程式如下所示。注意这个等式和高斯(也称为正态)分布的等式之间的相似性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者:高斯核方程

接下来我们将编写这种类型的内核回归。

编码高斯核回归

我们将首先查看一维特征向量的情况,然后将其扩展到 n 维。

from scipy.stats import norm
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt
import mathclass GKR:

    def __init__(self, x, y, b):
        self.x = x
        self.y = y
        self.b = b

    '''Implement the Gaussian Kernel'''
    def gaussian_kernel(self, z):
        return (1/math.sqrt(2*math.pi))*math.exp(-0.5*z**2)

    '''Calculate weights and return prediction'''
    def predict(self, X):
        kernels = [self.gaussian_kernel((xi-X)/self.b) for xi in self.x]
        weights = [len(self.x) * (kernel/np.sum(kernels)) for kernel in kernels]
        return np.dot(weights, self.y)/len(self.x)

我们为高斯核回归定义了一个类,它在初始化期间接受特征向量 *x、*标签向量 y 和超参数 b 。在类内部,我们定义了一个函数 gaussian_kernel() 来实现高斯内核。你可以看到我们只是把数学方程写成代码。接下来,我们定义函数 predict() ,该函数接收目标值需要预测的特征向量 x ₜ(代码中称为 X )。在函数内部,我们为每个 x ᵢ构造内核,计算权重并返回预测,同样通过将数学方程插入代码中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片:可视化不同的构造内核

现在,让我们传入一些虚拟数据,并查看输出的预测。我们预测 x ₜ = 50 的值(出于演示目的,忽略它已经存在于训练数据中)

gkr = GKR([10,20,30,40,50,60,70,80,90,100,110,120], [2337,2750,2301,2500,1700,2100,1100,1750,1000,1642, 2000,1932], 10)
gkr.predict(50)

这给了我们 1995.285 的输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片:从图形上,我们可以观察到 x_t = 50 的权重 w_i 是不同核之间的交点的垂线和虚线与 y 轴相交的点

现在,让我们针对 n 维特征向量的情况扩展代码。我们需要做的唯一修改是相似性得分计算。我们不是获得 x ᵢ和 x ₜ之间的差异,而是将 n 维情况下的相似性得分计算为它们之间的欧氏距离|| x ᵢ- x ₜ||。注意,为了处理 n 维向量,我们在需要的地方使用 numpy。

from scipy.stats import multivariate_normal

'''Class for Gaussian Kernel Regression'''
class GKR:

    def __init__(self, x, y, b):
        self.x = np.array(x)
        self.y = np.array(y)
        self.b = b

    '''Implement the Gaussian Kernel'''
    def gaussian_kernel(self, z):
        return (1/np.sqrt(2*np.pi))*np.exp(-0.5*z**2)

    '''Calculate weights and return prediction'''
    def predict(self, X):
        kernels = np.array([self.gaussian_kernel((np.linalg.norm(xi-X))/self.b) for xi in self.x])
        weights = np.array([len(self.x) * (kernel/np.sum(kernels)) for kernel in kernels])
        return np.dot(weights.T, self.y)/len(self.x)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片:可视化构建的 3D 高斯核

同样,让我们传入一些 2D 虚拟数据并预测 x ₜ = [20,40]。

gkr = GKR([[11,15],[22,30],[33,45],[44,60],[50,52],[67,92],[78,107],[89,123],[100,137]], [2337,2750,2301,2500,1700,1100,1000,1642, 1932], 10)
gkr.predict([20,40])

我们得到 y ₜ = 2563.086。

本文的扩展代码(包括可视化代码)可以在 GitHubKaggle 上找到。

结论

我们看到了线性回归和多项式回归不能使用的地方和原因,并在此背景下理解了内核回归背后的直觉和工作原理,以及如何将其用作替代方法。我们研究了高斯核回归的细节,并通过简单地插入数学方程来编码,用 Python 从头开始编码。

我很乐意在 Linkedin 上与你联系!

参考

[1] A .布尔科夫,《百页机器学习书》 (2019),安德烈·布尔科夫出版。

现代强化学习的关键概念

原文:https://towardsdatascience.com/key-concepts-of-modern-reinforcement-learning-f420f6603045?source=collection_archive---------32-----------------------

强化学习导论

强化学习设置的基本层次包括一个在反馈回路中与环境交互的主体。代理基于在时间s_{t-1}从处于前一状态的环境接收的响应,在时间s_t为环境的每个状态选择动作。从这个基本设置中,我们已经可以确定强化学习设置中的两个主要组件,即代理环境

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**代理-环境接口的递归表示。**在时间步骤t,代理接收环境状态的实例s_t。然后,代理从状态s_t的可用动作集中选择一个动作a_t。在下一次迭代中,代理收到一个新的状态实例s_{t+1}和一个基于在前一个状态s_t中采取的动作a_t的即时奖励r_{t+1}

当代理与环境交互时,它学习一个策略。策略是一种“习得的策略”,它控制着代理在环境的特定时间t选择动作的行为。策略可以被视为从环境的状态到在那些状态中采取的动作的映射。

强化代理的目标是在反馈配置中与环境交互时最大化其长期回报。代理从每个状态-动作循环中得到的响应(代理在环境的每个状态下从一组动作中选择一个动作)被称为奖励函数。奖励函数(或简称奖励)是基于代理所做的动作的该状态的合意性的信号。

“有利的”奖励可以指示代理的良好的即时事件(即,状态-动作对)。另一方面,“不利的”奖励可能对代理人来说是一个坏事件。奖励函数对于强化代理所面临的问题是唯一的,并且影响代理所做的最优策略的选择。奖励函数在很大程度上定义了强化学习任务。

另一个关键的部分是一个值函数(或者简单的值)的概念。当代理在环境的特定状态下采取行动时,奖励函数向代理传达该状态的直接和内在的合意性。然而,结果可能是,一个有即时高回报的状态可能会导致其他非常不受欢迎的状态。这并不好,因为 RL 代理的目标是最大化长期回报。状态的价值函数是通过考虑可能的未来状态和它们的回报函数而得到的当前状态的预期长期合意性。

归根结底,虽然 RL 代理的目标是最大化价值,但奖励是代理在与环境交互时收到的主要信号。评估价值的想法是在代理人与环境相互作用的每个状态下提高奖励的质量。因此,当代理在一个状态中采取行动时,它基于价值估计来这样做,以便它可以转移到具有高价值的新状态,从而导致长期回报。

奖励很容易获得,因为它们本质上是直接从环境中获得的反馈。另一方面,当主体反复与环境交互并收集更多信息时,必须不断地评估值。寻找一种有效的技术来估计值的任务是设计现代强化学习算法的核心。

然而,重要的是要注意,虽然估计值函数已经影响了现代 RL 文献中的许多想法,但强化学习问题仍然可以在不估计值的情况下解决。但是当然,这种方法的效率、适用性和可伸缩性是另外一个讨论。

最后,我们需要一个环境模型来学习强化学习代理的最优策略。环境模型必须以某种方式表示环境的随机性质,并在采取行动时向代理返回下一个状态和响应。拥有一个环境模型在计划中是有用的,在计划中,代理在采取行动之前考虑可能的未来结果。在任何情况下,强化学习系统也可以是初步的试错学习器,正如在学习自动机理论中看到的那样。通过反复试验学习的代理也可以学习环境的模型,并在以后使用它进行计算规划。

文献学

  • 纳伦德拉,K. S .,& Thathachar,硕士(2012)。学习自动机:导论。快递公司。
  • 萨顿和巴尔托(1998 年)。强化学习:导论。麻省理工出版社。

【https://ekababisong.org】最初发表于

用 python 做按键驱动分析

原文:https://towardsdatascience.com/key-driver-analysis-in-python-788beb9b8a7d?source=collection_archive---------4-----------------------

实践教程

回答“是什么驱动了糖果的魅力?”这个问题的指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者提供,仪表盘可用此处

在几年前的一次数据科学采访中,我被要求使用来自我们在 FiveThirtyEight 的朋友的一个小数据集来建议如何最好地设计一款畅销的糖果。“根据你在这里看到的‘市场调查’,”提示示意道,“建议产品设计团队一种糖果的最佳特性,我们可以和品牌糖果一起销售。”

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

FiveThirtyEight 的原始数据集,可在线获取点击

作为应用和商业领域的数据科学家,最好的这个词总是用来测试你的商业意识。更环保的数据科学家的一个典型标志是,他们是否在考虑最佳业务成果与最佳机器学习模型。“最佳”是“哪些糖果元素带来最高的满意度/享受度?”的平衡以及“哪些糖果元素推动了最高价格?”我们基本上是在努力寻找平衡

  1. 一种保证能取悦消费者的糖果
  2. 占据足够的空间,不仅仅是“山寨、打折 M & Ms”
  3. 还经过成本优化,比 M & M 更便宜,从而提高了利润率。

我们在 FiveThirtyEight 的朋友在试图解决#1 时犯了一个严重的统计错误(或两个)。

本教程通过使用适当的统计工具在 python 中进行“关键驱动因素”分析,脱离了 FiveThirtyEight 方法。在这个过程中,我解释了 1)为什么数据科学家和产品策略师应该更加信任我的数据,以及 2)如何以一种赢得信任的方式传达这些结果(参见我的糖果仪表盘)。

下面是本文的路线图:

  • 方法:线性回归不正确,使用相对权重分析
  • 实现:在 python 中进行 RWA 以获得糖果的味道和价格
  • 三角测量:为什么企业应该通过三角测量信任 RWA

统计第一,ML 第二

让我们通过理解为什么线性回归不是正确的答案…或者至少不是正确的答案来开始我们的统计方法。

FiveThirtyEight 建立了一个多元回归,包括在他们的数据集中捕获的糖果的所有可能特征。重要性是从线性回归的系数中提取的,使用维度的 P 值来定义我们是否可以将其视为可靠。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多元回归方程

然而,看看线性回归的方程,我们看到一个非常重要的问题。记住:OLS 回归系数告诉我们自变量的增加是否与因变量均值的增加相关(反之亦然,为负)。但这是衡量数量的标准。

在我们的糖果问题中,如果我们建立一个 OLS 回归来预测糖果棒的中奖情况,并将单位从克改为磅,我们将得到一个高得多的系数。体积除了单位没有改变任何东西。这里的论点可能是你可以标准化你的变量,比如标准化到 0 均值和单位方差。然而,通过归一化,您可能会遇到共线性问题-如果预测值是线性相关的或高度相关的,则 OLS 会变得不稳定,即使对自变量进行了标准化也是如此。

所以我们的工具箱里还需要一个工具。一些可以帮助我们避开关于系数的坏假设的东西。虽然 ML 可能没有答案,但统计学有。

相对权重分析和为什么 R2 是真正的绝地

这里,我们将实现代码,告诉我们每个特征/自变量对标准方差(R2)的贡献有多大。在其原始形式中,相对权重分析返回原始重要性分数,其总和等于模型的总体 R2;它的规范化形式允许我们说“特征 X 解释了目标变量 Y 中方差的 Z% 或者,更具体地说,

“假设让一种糖果受欢迎的关键驱动力在这里被抓住,坚果巧克力是最受欢迎的口味组合。”

相对权重分析依赖于 R2 分解来分配每个预测值的重要性。当独立变量之间的相互关联使得几乎不可能将标准化回归权重作为重要性的衡量标准时,RWA 通过创建相互正交的预测值并在没有多重共线性影响的情况下对其进行回归来解决这一问题。然后,它们被转换回原始预测值的度量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

的三特征 RWA 示例

我们涉及到多个步骤,我将通过一个 python 脚本来演示,而不是用符号来演示,您将能够使用这个脚本(如果您一字不差地使用这个脚本或派生出您自己的版本,请相信这篇文章!)我假设此时您已经完成了构建逻辑和数学上合理的模型所需的 EDA 和操作。

用 Python 实现 RWA

第一步:获得所有因变量和自变量之间的相关性。

步骤 2 :使用相关矩阵上的特征向量和特征值创建正交预测器,创建特征值平方根的对角矩阵。这避免了多重共线性的问题。注意获取对角线索引的 python 技巧。

第三步:乘特征向量矩阵及其转置(在 python 中,我们可以用@作为运算符,称为 matmul)。这允许我们将 X 视为因变量的集合,将 X 回归到矩阵 Z 上——矩阵 Z 本身是 X 的正交对应物,具有最小的平方误差。为了得到每个独立变量的部分效应,我们将矩阵乘法应用于逆矩阵和相关矩阵。

:如前所述,上面 coef_yz 的平方和应该加起来就是总 R2!这在下一步中将很重要!

步骤 4 :然后我们计算相对权重,作为步骤 2 和步骤 3 中矩阵的乘积。标准化后的版本就是 r2 的百分比!

现在,您只需拉上您的功能和这两个列表的拉链,就可以获得每个功能的相对权重,因为它“驱动”(或者,更数学地说,解释与增加相关的差异)了糖果对偶的获胜百分比。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

预测胜率的原始和正常相对权重

巧克力和坚果赢得了口味,但我们还没完。我们还必须通过从我们创造的糖果中创造价值来赚钱,以收入减去成本来衡量。使用表中的价格百分位数,我们还可以看看是什么推动了糖果的价格。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

价格百分位预测的原始和正常相对权重

向企业传达结果

我们在这里有两个责任:提出建议,并让利益相关者乐于执行这些建议。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

plt 总是一个很好的动态可视化事物的科学方法。对许多利益相关者来说,好的科学和坏的可视化是密切相关的。

建议 1 :巧克力占了糖果成功的 38%,而花生占了 15%。我们可以通过观察样本中巧克力和花生糖的相对成功率来证实这一点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

建议 2 :相对于其他形式,Pluribus 糖果的成本最低(相对重量 3%),这有助于抵消我们的巧克力和坚果成本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

推荐 3: 根据我们的数据,美味的 pluribus chocolatey peanutyalmondy 糖果尽管性能更高,但并不容易获得。在我们的数据集中只有 2 个被捕获。

通过分析亚马逊杂货数据中的糖果,这些建议可以得到进一步的支持,该数据有 290 万条糖果评论。

虽然在亚马逊的数据中,水果在味道/评论得分上超过了坚果,但它仍然排在前三名。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

风味在预测亚马逊杂货店约 3M 糖果评论分数中的重要性的相对权重分析

此外,从高销量(>第 50 个百分点)、高排名(> 3 星)的亚马逊巧克力和坚果评论构建的主题模型进一步表明,我们的风味特征是一个成功的决定。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是洞察交付幻灯片的一个示例。我的幻灯片的最终版本有 5 张幻灯片:首先是建议,然后是 RWA 可视化,然后是确认结果,最后是“这是风险/不确定性所在,以及我们如何试图将其最小化”的幻灯片。

结论

事实上,我们可能会认为关键驱动因素分析有点像寻找圣杯,因为有如此多不同的可能混淆的变量,以至于知道拉什么杠杆来驱动我们的目标变量是一种无中生有的尝试。但是我们可以通过放弃传统的(尽管是错误的)线性回归系数的使用,并转向相对权重分析来获得更好的信心。而且,可以肯定的是,这个版本有一些改进——如果你关注 R2 风味概况,并亲自尝试定价,你会发现通过各种数学优化还有改进的空间。

隐藏在这里的是一个关于数据科学家必须精通降低商业风险和最大化决策潜力背后的统计学和数学的故事。否则,我们只是解决输入/输出问题的廉价软件开发人员/工程师,这些问题会给企业带来巨大的成本。

对于那些 DS 领导者和经理读者来说,这个例子表明,建立一个不同时拥有面向统计和技术的数据科学家的数据科学团队是一个灾难。为什么?那么,在这种情况下,“我们如何做出最好的糖果?”,是一个统计问题和业务领域问题,不容易用 sklearn API 调用解决。

通过使用 Tidyverse 挖掘性别平等数据获得的关键见解

原文:https://towardsdatascience.com/key-insights-obtained-by-mining-gender-equality-data-using-tidyverse-e9b80a557c72?source=collection_archive---------48-----------------------

确定关键绩效指标,以评估妇女对国家和全球一级决策的贡献。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

萨曼莎·索菲亚在 Unsplash 上的照片

性别不平等是最受关注的领域之一,这促使联合国(UN)制定了可持续发展目标 5实现性别平等并赋予所有妇女和女童权力。为了评估当今世界的情况,最好的评估标准之一是根据妇女在领导和决策方面的贡献对国家进行排名。为了在同样的基础上形成见解,可以将*妇女在国家议会中所占席位的比例(%)* 视为基石(数据)。其背后的原因是,在当今世界,许多地区的歧视性法律和制度性做法限制了妇女竞选政治职务的能力。系统性不平等、缺乏受教育机会、参与劳动力的选择有限以及对个人自由的许多其他限制,限制了妇女获得追求政治生涯的资源和机会。

关于数据

数据来源是改头换面星期一挑战(2020W30) 的一部分,并由世界银行整理,作为其世界发展指标数据库的一部分。数据揭示了全国妇女在国家议会中所占席位的比例(%)(1997-2019 年)。这些数据是可视化性别平等——viz 5计划的一部分。

探索性数据分析

从数据中获得洞察力的第一步是探索数据。对于这个场景,使用了 Tidyverse ,这是在 r 中进行 EDA 的一个关键要素。以下是数据的一瞥:

head(Female_Political_Representation)Country.Name  Country.Code Year Proportion of Seats
1      Albania          ALB 1997                  NA
2      Albania          ALB 1998                  NA
3      Albania          ALB 1999          0.05161290
4      Albania          ALB 2000          0.05161290
5      Albania          ALB 2001          0.05714286
6      Albania          ALB 2002          0.05714286

对于每个国家,我们都有国家代码、年份(1997-2019)和女性所占席位的比例。从这一瞥中,我们可以看到我们缺少价值观。

让我们检查缺失值是在初始年份还是在随机地点。

missing_data <- filter(Female_Political_Representation, is.na(`Proportion of Seats`)) %>% # Filter only Missing values
  group_by(Year) %>%        # Group by Year
  summarise(count = n()) %>% # Count the instances
  arrange(desc(count))       # Arrange in descending order> missing_data
# A tibble: 20 x 2
    Year count
   <int> <int>
 1  1999    31
 2  1997    23
 3  2000    22
 4  2002    17
 5  1998    16
 6  2001    13
 7  2003     6
 8  2013     4
 9  2014     4
10  2017     3

正如我们所见,缺失数据是随机分布的。因此,对于插补,我们可以遵循两种方法:

  1. 捕捉单个国家的趋势,并将其用于估算该国的缺失年份。
  2. 在国家级别使用向前填充|向后填充方法估算缺失值。

我更喜欢后一种方法,并通过给出方向“向下来使用填充的方法

Imputed_data <- Female_Political_Representation %>% 
                group_by(Country.Name,Country.Code) %>% # Group at Country Level
                fill(`Proportion of Seats`, .direction = "downup") # Missing Value Imputation  

colSums(is.na(Imputed_data)) # check for completeness

识别 KPI

插补后的关键目标是确定特定国家如何有效实施对性别问题有敏感认识的议会——妇女有充分参与的自由。可以通过计算妇女席位比例的年同比变化来评估。以下是可以创建的两个指标:

  1. 与前一年的差异:通过用前一年的值减去特定年份的值来获得。它可以被视为计算提升的一种手段——差异越积极,国家就越有可能提高对性别平等的认识。此外,我们可以根据这一指标对国家进行排名。
  2. **差额 vs 起点:**计算当年比例与起点的差额。*公式:比例(2019)-比例(1997)。*差异越大,国家在权力结构和组织中实现性别均衡代表性的进展就越好。

生成见解

使用 tidyverse ,我们可以通过使用 lag 函数轻松计算出 ***与一年前*** * 的差异。*

# difference vs Year Ago Compute (At Country Level) ----KPI1 <- Imputed_data %>% 
         group_by(Country.Name,Country.Code) %>%mutate(Diff_vs_Year_Ago = 
`Proportion of Seats` - lag(`Proportion of Seats`)) %>% 
#Difference vs Year Ago fill(Diff_vs_Year_Ago, .direction = "downup") %>%  
# Missing Value Imputation mutate(`Proportion of Seats` = round(`Proportion of Seats` *100,2),
Diff_vs_Year_Ago = round(Diff_vs_Year_Ago*100,2) 
# Data Standardization)

我们可以对国家进行排名,以查看年度水平,哪个国家的进步最大/最小。

# Rank the differences at Yearly level ----
Rank1 <- KPI1 %>% 
         group_by(Year) %>% 
         mutate(rank = rank( - Diff_vs_Year_Ago, ties.method = "first"))

对于 2019 年(最近一年),我们可以借助 ggplot2 和 plotly 来可视化前 10/后 10 个国家:

library(plotly)
# Plot Top 10 Countries
top_10_plot <- Rank1 %>% 
  filter(Year == 2019 & rank < 11) %>% 
  ggplot(aes(x=reorder(Country.Name, `Proportion of Seats`), y= `Proportion of Seats`,fill = Country.Code)) +
  geom_bar(stat="identity")+
  theme_minimal() +
  theme(legend.position = "none")  +
  xlab("Country") + 
  coord_flip()# Convert to plotly
ggplotly(top_10_plot)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

十大国家(2019 年与 2018 年)

故事要点:与去年相比,排名前 3 位的国家表明他们增加了超过 10%的席位比例,其中阿联酋以 27.50%的比例高居榜首

# Plot Bottom 10 Countries ----
bottom_10_plot <- Rank1 %>% 
  filter(Year == 2019 & rank >205) %>% 
  ggplot(aes(x=reorder(Country.Name, - Diff_vs_Year_Ago), y= Diff_vs_Year_Ago,fill = Country.Code)) +
  geom_bar(stat="identity")+
  theme_minimal() +
  theme(legend.position = "none")  +
  xlab("Country") + 
  coord_flip()# Convert to plotly
ggplotly(bottom_10_plot)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

垫底的 10 个国家(2019 年与 2018 年)

故事要点:与去年相比,所有排名后 10 位的国家都显示出了不到 0%的下降率,其中突尼斯以-8.7 %的下降率高居榜首

我们可以执行顶部/底部分析的另一个角度是查看国家如何随着时间的推移而演变(‘差异 _ 对比 _ 起点’)。我们可以在国家级别进行分组,并使用*‘first’*方法计算每个实例与起始年份的差异。

# Difference vs start Compute ----
KPI1 <- KPI1 %>%
  arrange(Year) %>%
  group_by(Country.Name) %>%
  mutate(Diff_vs_Start = `Proportion of Seats` - first(`Proportion of Seats`))# Compute Rank basis at Yearly Basis
Rank2 <- KPI1 %>% 
         group_by(Year) %>% 
         mutate(rank_Diff_vs_Start = rank( - Diff_vs_Start, ties.method = "first"))

对于 2019 年(最近一年),我们可以将前 10 名/后 10 名国家定位如下:

top_10_plot1 <- Rank2 %>% 
  filter(Year == 2019 & rank_Diff_vs_Start < 11) %>% 
  ggplot(aes(x=reorder(Country.Name, Diff_vs_Start), y= Diff_vs_Start,fill = Country.Code)) +
  geom_bar(stat="identity")+
  theme_minimal() +
  theme(legend.position = "none")  +
  xlab("Country") + 
  coord_flip()# Convert to plotly
ggplotly(top_10_plot1)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

前 10 个国家(历史上有所改善)

故事要点:历史上排名前三的国家,如阿联酋、卢旺达和玻利维亚,表明它们的席位比例增加了 40 %以上,其中阿联酋以 50%的比例高居榜首。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

垫底的 10 个国家(历史上有所改善)

故事要点:与起点相比,历史上所有垫底的 10 个国家的增长率都呈下降趋势,低于 0%,其中塞舌尔以-6.2 %的增长率高居榜首

**全球层面的见解—

从全球绩效的角度来看,我们可以忽略国家/地区,按年度对数据进行分组,并获得修订的 KPI,如下所示:

# Global Level Trend ----
Global_Insight <- KPI1 %>% 
                  group_by(Year) %>% 
                  summarize(
                    Avg_Proportion_of_Seats = mean(`Proportion of Seats`),
                    Avg_Diff_vs_Year_Ago = mean(Diff_vs_Year_Ago),
                    Avg_Diff_vs_Start = mean(Diff_vs_Start)

                  )> mean(Global_Insight$Avg_Diff_vs_Year_Ago) # Compute Average Lift
0.5781982##########################################################Year Avg_Proportion_of_Seats
   <int>                   <dbl>
 1  1997                    10.4
 2  1998                    10.8
 3  1999                    11.2 
 ----------------------------------
 4  2017                    21.9  
 5  2018                    22.5  
 6  2019                    23.3
##########################################################

此外,我们可以使用这些数据来创建一个面积图,它将展示全球趋势,如下面的代码所示:

# Area plot ----# Plot 1
Avg_Proportion_of_Seats_plot <-  ggplot(Global_Insight, aes(x = Year, y = Avg_Proportion_of_Seats)) + 
  geom_area(fill ="#00AFBB", alpha = 0.5, position = position_dodge(0.8))ggplotly(Avg_Proportion_of_Seats_plot)# Plot 2
Avg_Diff_vs_Year_Ago_plot <-  ggplot(Global_Insight, aes(x = Year, y = Avg_Diff_vs_Year_Ago)) + 
  geom_area(fill ="#ff7a00", alpha = 0.5, position = position_dodge(0.8))ggplotly(Avg_Diff_vs_Year_Ago_plot)# Plot 3
Avg_Diff_vs_Start_plot <-  ggplot(Global_Insight, aes(x = Year, y = Avg_Diff_vs_Start)) + 
  geom_area(fill ="#0dff00", alpha = 0.5, position = position_dodge(0.8))ggplotly(Avg_Diff_vs_Start_plot)# Combine 3 plots ----
library(ggpubr)
theme_set(theme_pubr())figure <- ggarrange(Avg_Proportion_of_Seats_plot, Avg_Diff_vs_Year_Ago_plot, Avg_Diff_vs_Start_plot,
                    labels = c("Avg_Proportion_of_Seats", "Avg_Diff_vs_Year_Ago", "Avg_Diff_vs_Start"),
                    ncol = 1, nrow = 3)figure

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

环球之旅(1997- 2019)

故事要点:我们可以看到,全球级别的席位比例从 10.4% (1997 年)上升到 23.3% (2019 年),平均每年增长 0.57%此外,Diff_vs_Year_Ago 经历了高峰和低谷,2007 年涨幅最大,2010 年跌幅最小

结束注释

这一洞察生成之旅始于探索和清理数据、标准化数据(缺失值插补)、识别 KPI、创建有效的视觉效果来回答问题,并从中讲述一个有意义的故事。此外, tidyverse 可以快速方便地从数据中挖掘见解,这些数据稍后可以集成到 tableau public 中,以创建快速的视觉效果和故事线,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tableau 公共仪表板(https://public.tableau.com/profile/gaurav.chavan#!/viz home/改头换面 Monday 2020 w30 _ 15961721926540/global journey?发布=是

上图预测表明,到 2030 年**,妇女在政治角色中的比例仅为27.67%,而不是联合国达到 50%的目标。**

链接到Tableau 公共仪表盘: 链接****

链接到代码和视觉效果:链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值