TowardsDataScience 博客中文翻译 2020(六百六十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

新手学习强化学习指南

原文:https://towardsdatascience.com/newbies-guide-to-study-reinforcement-learning-8b9002eff643?source=collection_archive---------4-----------------------

在强化学习领域迈出小步

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

本指南中介绍的入门资源包

如果计量付费墙困扰着你,请点击此链接

如果你想知道我的深度学习之路,可以看看我在新手深度学习指南上的文章。

我在这里要谈的不是强化学习,而是如何研究强化学习,我采取了哪些步骤,以及我在学习过程中发现哪些是有帮助的。如果你发现一些有用的东西,请在评论中告诉我。如果您有其他想要推荐的路径,请将它们留在评论中让其他人看到(我会在适当的地方编辑、添加和更新文本)。和平的人们!

阻止信息泛滥

强化学习有相当多的概念需要你去思考。看到 RL 技术的完整分类后,你的脑袋会转得更快。一旦你开始阅读所有最酷和最新的研究,以及它们让事情运转的技巧和细节,事情就会变得更加复杂。但是看那些 OpenAI 机器人玩 DoTA 太酷了,你可能会想学习它的所有技术和技巧,并建立自己的机器人。首先,停在那里。暂时忘记如何实现自己版本的 OpenAI Five。你最终可能会回到起点;也就是说,永远离开 RL,却发现自己在三个月后试图重新学习。

你需要从大量的教程(我的两分钱教程)和 YouTube 视频中抽身出来,告诉他们你可以用 20 行代码在 5 分钟内编写出“一些棒极了的 RL 东西”之类的东西。因为他们都没教你什么!是的,什么都没有(除了 git 克隆和/或复制代码)。一旦你足够努力地去弄清楚价值迭代是如何工作的,并意识到这个想法如此简单,但对于一个简单的玩具例子来说却非常有效,你就会知道知识的真正味道。这就是你学习的方式,也是你在这条学习道路上前进的方式。

在线课程

所以,让我们理清思路,重新开始,让自己保持冷静,参加 Coursera 的实用强化学习课程。本课程将不是在公园散步,但挑战是锻炼你的大脑和质疑自己是否完全掌握了核心概念。它从非常基本的交叉熵方法开始,逐渐发展到策略迭代、值迭代、Q 学习和 SARSA。课程的后半部分包括:深度 Q 网络和演员-评论家算法。这门课程的一个好处是,你不需要担心繁重的计算资源,因为你可以在 Coursera 或 Google Colab 的 Jupyter 笔记本上完成作业(他们有在 Colab 上设置的说明),甚至可以在你自己的机器上用你最喜欢的 IDE 完成。就个人而言,我更喜欢在我的本地 IDE 中编写代码,因为我拥有所有的调试工具。事实上,我甚至会引导您在 IDE 中运行和调试代码,因为您需要理解 OpenAI gym 对象实际包含的内容(使用 print 语句并不理想)。否则,你会觉得事情是在黑盒子里,尽管它们不是。查看 OpenAI 文档,感受特定的环境并愉快地开始调试(是的,当我进行调试会话时,我非常高兴;不确定你会有什么感觉)。

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

当你学习新概念时,你的 IDE 和调试器是你最好的朋友。我发现 Jupyter 笔记本在跳跃、查找文档和调试时非常笨重。但那是我个人的看法。

但是课程视频会变得非常乏味,你不想吸收任何东西。如果是这种情况,停止视频,直接开始编程作业。我有时发现这真的很有帮助,因为它给了我一个更好的动机,为什么我应该学习课程视频喋喋不休的内容。结合阅读我将在下面提到的教科书。

身边放一本教科书(这会给你很大帮助!)

课本很无聊。我明白了。但有时,他们是那些能在网上文章的海洋中给你一些安慰的人。我在强化学习方面的首选教材是萨顿和巴尔托的《强化学习:导论。如果你曾经搜索过强化学习教材,这不会让你感到惊讶,而且它是大多数大学课程的首选教材。

萨顿和巴尔托写了这么好的教科书,真是太棒了。我发现阅读和查找我想知道的东西是一件非常愉快的事情。事实上,我甚至强烈推荐你阅读教材的第一章,对强化学习有一个非常温和的介绍。我发现它比任何其他在线教程或媒体帖子都好。

这本教材的另一个真正的好处是,即使在学习 Coursera 课程时,我有时也会发现阅读教材比课程视频本身对我的帮助更大。这有点奇怪,因为大多数时候情况正好相反。所以,我所做的就是在课本和课程视频之间来来回回,填补我的知识空白。然后,我尝试编程作业,以真正检查我是否理解算法的技术细节。

通过编码学习,而不仅仅是通过阅读

当我开始进入强化学习的世界时,我总是对“价值函数”、“Q 值”、“最优策略”和“策略”之间的联系感到困惑。相信我,在你实施并使用这些概念来训练你的特工之后,这些概念就会变得一清二楚。阅读文本,观看课程视频,实现功能,运行,调试,重复。

四处玩耍

当你在学习 Coursera 课程的时候(最好是在你完成了课程的第三周,并且对 Q-Learning 有所了解之后),看看 Lex Fridman 关于深度强化学习的讲座。这不是技术性的,但现在,你会对幻灯片中的 Q-learning 部分有更好的理解。关于强化学习的事情是,如果你在需要知道某些概念的时候用谷歌搜索它们,你会暂时记住这些知识,但如果你对这些概念背后的作用没有深刻的理解,你将永远感到困惑。这也是我建议你在充分理解基本概念后去查阅那些讲座的原因之一。然后,试用深度流量。有几个参数可供选择,如果你不确定这些参数的含义,查看的文档并阅读论文以更好地了解为什么某些参数会有帮助。然后,去试试 Karpathy 的深度 Q-Learning Demo 。现在,您应该非常熟悉各种超参数。

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

尝试几个随机参数并得到好的结果是有趣的,但是不要忘记了解你的变化背后的“为什么”。[ 深度交通

参数是脆弱的,但首先检查错别字!

当你开始研究强化学习问题时,你会开始意识到这些参数是多么脆弱。将您的 epsilon 调整到一个特定的数字,以便在您的代理开始开发之前进行足够的探索,这与为您的 DQN 网络建立一个具有确切参数的确切架构一样重要。所有这些都会让您认为,如果您的代理没有做好工作,您就没有将所有这些讨厌的超参数调得足够好。但是通常情况下,您可能在代码中的某个地方有一个打字错误。在更新 Q 值时,您可能错误地传递了当前状态,而不是下一个状态。所以,在你花一整天的时间调优一个参数而没有得到任何好的结果之前,一定要先检查你的代码。

走向广阔

一旦你很好地掌握了基本的强化学习概念,就开始跟随加州大学伯克利分校深度强化学习课程的讲座和大卫·西尔弗关于强化学习的讲座。这有助于重申你所学到的东西,并确保你仍然可以跟上,尽管在符号等方面有细微的变化(我们在机器学习文献中也看到了很多;人们使用稍微不同的符号只是为了让你更困惑!).

既然你对强化学习的基础有了很好的理解,你应该开始阅读关于 DQN 的开创性论文。如果你只理解深度学习部分而不理解强化学习部分,直接进入深度强化学习是不可取的。这是那些非常精通深度学习但不知道强化学习是什么的人的一个主要谬误。

配备了基本的强化学习知识,就可以开始阅读各种深度强化学习论文(并开始实施)。你在某些概念上会有一些知识缺口,但你应该已经有了工具箱中的核心概念,学习额外的技术不再那么困难。我个人的技巧是使用思维导图软件来绘制概念和论文(描述于新手深度学习指南)。

其他资源

不管怎样,伙计们,我希望这篇指南能给你足够的动力,让你真正认真对待强化学习,让你从永无止境的 YouTubing 和在线阅读教程的循环中解脱出来。

两周内的新闻聚合

原文:https://towardsdatascience.com/news-aggregator-in-2-weeks-5b38783b95e3?source=collection_archive---------24-----------------------

一种简单快速的新闻分类和聚类方法

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

图片由 Pezibear 通过 Pixabay 拍摄

数据聚类大赛是 Telegram 在 11 月举办的为期两周的数据科学竞赛。Telegram 团队希望参与者建立一个简单而有效的新闻聚合器,将来自不同出版商和网站的数以千计的文章整合到一个页面中,显示最新的新闻和头条新闻,就像谷歌新闻必应新闻所做的那样。

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

提交的公共测试屏幕。作者图片

尽管比赛于 2019 年 12 月 2 日结束,主办方还是在一周前宣布了结果。我们的团队“用心松鼠”在最终排行榜中排名第三。在本帖中,我们简要概述了我们的方法,并讨论了我们遇到的主要挑战。完整的解决方案可从 GitHub 上获得。所有培训脚本都在 Colab 中共享。

任务概述

竞赛包括五个子任务,预计将在原始数据上按顺序运行:

  1. 语言识别(只需要英文和俄文文本)
  2. 从其他文本中过滤新闻
  3. 分为 7 个主题
  4. 将与同一事件相关的文章聚类成线索
  5. 对每个主题中的线索进行排名

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

竞赛任务。图片由作者提供,使用 draw.io 创建

语言检测这一步似乎非常清楚,而其他任务却提出了一堆问题。没有给出“新闻”的严格定义;主题标准没有很好地定义;聚类粒度和排名标准也缺失。

竞赛规则要求最终提交的作品在 Debian 的一台机器上本地运行,该机器有 8 个内核和 16 GB 内存,不使用网络。此外,该应用程序必须能够在 60 秒内处理任何一批 1000 篇文章。限制的第一个版本还包括对超过 200 MB 磁盘空间的解决方案的惩罚。

解决办法

这些限制严重影响了适用仪器和算法的范围。第一个想法是采用 SOTA 模式,如埃尔莫伯特。但是,它们太大,不符合要求。因此,我们广泛依赖于一个快速文本库。这是脸书人工智能研究所在 2016 年创建的用于学习单词嵌入和训练文本分类模型的库。整个提交代码都是用 C++写的,唯一复杂的模型是用 Python 和 Keras 训练的。

语言检测

这项任务的解决方案很简单。我们利用了由 fastText 团队提供的预训练模型。

过滤和主题分类

我们将这两个任务合并成一个单一的多类分类问题。由于任何监督分类器都需要一定数量的训练数据,我们利用了 Yandex。Toloka 标注一小部分输入数据。Toloka 是俄罗斯的众包平台,就像亚马逊机械土耳其人一样。我们花了 60 美元购买了俄文和英文的标签。我们使用机器翻译英语文本到俄语,以方便说俄语的工作人员。

我们还扩展了数据集,增加了俄语新闻数据集 Lenta.ru 和英语新闻数据集 BBCHuffPost 的公开数据。

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

分类数据集。图片由作者提供,使用 draw.io 创建

利用所有标记的数据,我们训练了一个简单的快速文本分类器。默认的快速文本分类算法学习所提供数据上的单词嵌入,然后使用它们的平均值进行预测。在缺少标记数据的情况下,利用丰富的预训练单词表示通常会提高分类器的性能。我们在由 RIA“所有新闻”语料库扩展的先前数据集上训练这样的无监督向量。

新闻聚类

新闻聚类的两个基本要素是丰富的文本嵌入和健壮的聚类算法。

对单词向量进行简单的平均不会产生好的结果,这迫使我们应用一些更复杂的东西。我们使用结合了点积、密集层和顶部的 sigmoid 的连体神经网络架构来训练最终的文本嵌入。该分支的体系结构由单词向量上的平均池、最小池和最大池串联而成的单一密集层组成。我们将每个文本分成两半,然后训练网络来预测这些部分是否属于同一篇文章。我们从同一出版商的其他文本中挖掘与正面例子时间接近的负面例子。作为一个目标函数,我们使用了对数损失函数,尽管我们应该使用三重损失

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

用于构建文本嵌入的暹罗网络。图片由作者提供,使用 draw.io 创建

该模型仍然尽可能简单,只有一个可训练矩阵。在实际的 C++应用中,我们没有使用任何神经网络框架,只使用了矩阵乘法的特征库。

我们利用了 SLINK 凝聚聚类。该算法有一些缺点,其中最重要的是传递链接。第一个点可能与第二个点相关联,第二个点可能与第三个点相关联,因此,第一个点和第三个点属于同一群,尽管它们可能相距甚远。

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

凝聚集群。图片由作者提供,使用 draw.io 创建

SLINK 的时间复杂度为 O(n ),这使得我们无法一次聚类所有的文档。为了克服这个问题,我们假设时间上遥远的文档不应该属于同一个集群。这个假设允许我们将整个时间线分成 10,000 个文档的块,其中有 2,000 个项目重叠。然后,我们在每个块中应用聚类,最后,通过重叠的文档链接连续的块。

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

按块聚类。图片由作者提供,使用 draw.io 创建

线程命名和排序

我们在一个线程中的标题选择基于三个部分的乘积:新鲜度、与其他线程文档的相似性以及文档的源权重。

我们将新鲜度定义为文档日期和线程中最新鲜文档的日期之差的缩放后的 sigmoid。

使用与聚类中相同的文本向量来计算线程的文档之间的内部相似性。对于每个线程中的每个文档,我们计算了到线程中其他文档的平均距离。

对文档内部链接的 PageRank 算法允许我们评估来源的重要性。

新鲜度、源权重和簇大小形成了线程排序的基础。所有发布日期的第 99 百分位被选为整个数据集的“当前时刻”,因为可能存在异常值。线程的年龄是从这个“当前时刻”计算的。

结论

挑战的最重要部分是建立一个满足严格硬件限制的系统。这些限制以及短暂的比赛持续时间迫使我们依赖简单的机器学习算法(如 fastText),而不是复杂的 SOTA 模型。我们没有足够的时间进行所有我们想要的实验或完善最终的解决方案。尽管我们在比赛结束后做了一些修改,但仍有改进的空间。我们还构建了一个 CI 工作流,将每个版本的代码输出与规范结果相匹配,并发布到 GitHub 页面

最后,我们要感谢竞赛组织者给了我们一个挑战性的任务。这绝对是一个需要解决的大问题。

链接:

  1. GitHub 库:用心松鼠的电报数据聚类竞赛解决方案
  2. 这篇俄语文章:новостнойагиегатораадвенедедели
  3. 另一篇关于数据聚类大赛的文章:从零开始构建新闻聚合器:新闻过滤、分类、按线索分组和排名

牛顿 vs 神经网络:人工智能如何腐蚀科学的基本价值观。

原文:https://towardsdatascience.com/newton-vs-neural-networks-how-ai-is-corroding-the-fundamental-values-of-science-368c93e01906?source=collection_archive---------31-----------------------

科学就是寻找解释。随着数据的丰富,我们已经不再要求解释,并满足于仅仅是相关性。我们很高兴知道某事会发生,但不知道它为什么会发生。这从根本上破坏了科学探究的精神,我们可能永远无法恢复。

在过去的四百年里,科学方法引导了科学的发展。该方法包括仔细记录观察结果,制定一个有意义的假设,并严格检验该假设。基于这些测试的结果,假设被一次又一次地改进,直到它能解释观察结果。科学家被告知相关性并不意味着因果关系。对他们来说,知道有事发生是不够的;他们需要了解导致这种情况发生的潜在机制。

人工智能(AI)是一种非常不同的野兽。大多数人工智能系统使用一种称为机器学习的技术,通过向计算机提供与任务相关的大型数据集,来教会计算机如何执行任务。构建这种数据驱动的人工智能系统的开发人员被称为数据科学家。与真正的科学家不同,数据科学家满足于相关性,而不关心因果关系。他们蔑视科学方法的原则,嘲笑其严谨性。他们公然忽略了理论的必要性,只对经验模型感兴趣,这些模型在不理解他们预测什么的情况下给出预测。

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

地球、太阳和月亮的运动是三体的经典例子(图片来自 Pixabay)

三体问题是物理学中一个被充分研究的问题,由艾萨克·牛顿爵士在他不朽的论文数学原理中首次描述。假设给你空间中的三个大物体——比如太阳、月亮和地球——以及它们的初始位置和速度,你能在固定的时间间隔后确定它们的最终位置吗?为了解决这个问题,你需要计算每个物体对另外两个物体的引力的影响。这听起来可能很简单,但这个问题已经困扰了物理学家几个世纪。没有人能够推导出提供最终解的方程。计算机模拟用于使用运动定律费力地计算每个物体在每个时间步的运动。这些计算是混沌的,即对微小的变化非常敏感。由于这种混乱的性质和涉及的大量计算,模拟需要几周或几个月的时间来运行,可能会被卡住,有时根本不可行。

2019 年 10 月,人工智能研究人员宣布他们开发了一种神经网络,可以在几秒钟内找到三体的精确解。这比今天最好的模拟快了惊人的 1 亿倍。神经网络是最受欢迎的机器学习算法,它们构成了大多数人工智能解决方案的核心。让他们无比强大的是他们成为通用近似者的能力。给定足够的训练数据,他们可以学习近似描述输入和输出之间关系的任何方程。在上述案例中,研究人员在 10,000 个三体构型的数据集上训练了他们的神经网络。神经网络处理了这个数据集,并学习了描述初始配置和最终配置之间关系的方程,而不知道任何运动规律

人工智能正在物理、化学、生物、金融、经济甚至社会科学领域迅速取得进展,它正在迅速取代传统的理论方法。1973 年,费希尔·布莱克、迈伦·斯克尔斯和罗伯特·默顿为股票期权定价开发了著名且极具影响力的布莱克-斯科尔斯公式,为此默顿和斯科尔斯获得了诺贝尔经济学奖。他们的方法受到了物理学家用来模拟流体中悬浮粒子运动的布朗运动的启发。在过去十年中,理论驱动的布莱克-斯科尔斯模型实际上已经被数据驱动的神经网络淘汰,这些神经网络学习仅从过去的价格预测未来的价格,而对金融市场一无所知。

在医疗保健领域,人工智能模型通常用于从医学图像中诊断结核病和各种癌症等疾病。再说一次,人工智能对放射学、医学或人体解剖学一无所知。

科学就是寻找解释。随着数据的丰富,我们已经不再要求解释,并满足于仅仅是相关性。我们很高兴知道某事会发生,但不知道它为什么会发生。这从根本上破坏了科学探究的精神,我们可能永远无法恢复。

科学思维是一种技能,我们作为一个社会,不是一夜之间发展起来的。我们花了几千年才发现它,然后又花了几个世纪才采纳并开始实践它。就像我们正在失去记忆、心算或在没有全球定位系统的情况下在街上导航的能力一样,我们还需要多久才能在这个快速增长的能力列表中加入科学思想呢?

如果你喜欢这篇文章,可以在 Medium 上查看我的其他作品,在LinkedInTwitter,查看我的 个人网页 ,或者发邮件给我viraj@berkeley.edu

牛顿方法:视觉直觉。

原文:https://towardsdatascience.com/newtons-method-the-visual-intuition-12a346f4d89?source=collection_archive---------30-----------------------

如果第一次你没有成功,试着再试一次。

牛顿和拉夫森

我们将在这个博客中讨论的方法也被称为牛顿-拉夫森方法。从名字上看,你可能会想象牛顿和拉夫森作为一个团队一起工作,像好朋友一样提出这个想法。但实际上,他们是独立发现的。这似乎是与牛顿共同的主题。还记得他是如何和莱布尼茨一起发现微积分的吗?为什么这个人在别人也有想法的时候有这么多想法?这实际上是很有可能的,因为,对于处于一个领域研究前沿的许多人来说,接下来的步骤通常不会太模糊。

和多个独立团队发现英特尔芯片的安全漏洞(Spectere 和 Meltdown)是一个道理。

不管怎样,历史已经足够了,让我们进入它是如何工作的。

x 是什么?

代数的本质(源自阿拉伯语,Al-Jabr——把零件拼在一起)是求解未知量的方程。例如,找到 x,其中:

2x + 5 = 7 __(1)

很容易看出上面方程的解是 x=1。另一种方法是将所有东西放在一起,称表达式为 y,我们得到:y = 2x 2。然后,我们可以试着找到 y=0 的 x。

但是当 y 用 x 来表示变得越来越复杂时会发生什么呢?我们能找到满足 y=0 的 x 的所有(或至少一个)值吗?例如,下面的图 1 显示了 y 与 x 之间的一些更复杂的关系。但是,在所有情况下,x=1 时满足 y=0(尽管可能不是唯一的)。

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

图 1:在 x=1 时都有零点的不同函数。

如果线性函数是最简单的,那么下一个可能是二次函数(涉及 x)。现在,如果我们有一个二次方程,我们将如何着手解决它?嗯,我们可以简单地使用二次公式(当只有一个变量 x 时)。但是让我们假设我们不知道这个公式。我们只知道如何求解像方程(1)这样的线性系统。我们可以用解一个线性方程的知识来解一个非线性(这里是二次)方程吗?

y=x __(2)

我们可以,但前提是我们要坚持不懈。让我们从任意一个点 x 开始,现在,计算我们的二次函数在这个 x 上的值,称之为 y,我们的工具是一个线性方程求解器,但我们有一个二次方程。所以,让我们把二次方程转换成一次方程。为此,只需用一个线性方程近似二次方程(使用泰勒级数)。当我们解这个线性方程时,我们会得到 x 的“答案”,但我们不能指望这个答案是“正确的”,因为我们“作弊了”——解了一个线性方程,而不是一个二次方程。既然疯狂的定义是做同样的事情却期待不同的结果,我们就不断重复这个过程。唯一的不同是,这一次我们使用线性方程的前一个解作为我们的起点。最终,这个过程将我们引向二次方程的解,如下图所示。

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

图 2:抛物线的牛顿拉夫森迭代使我们越来越接近抛物线与 x 轴相交的一个点。

加大尺寸

现在,你可能已经看到了和上面非常相似的东西。但是,这如何扩展到多维度呢?例如,代替一个变量 x,假设我们现在有两个变量 x 和 y。将等式(2)扩展到二维的最自然的方式是:

z=x +y __(3)

这是它的情节:

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

图 3:抛物面,一种多维的二次方程。这里,x 和 y 是尺寸,z 轴代表抛物面的功能形式。

像以前一样,我们想求解 z=0。这发生在上图中的绿色圆圈处,也就是我们的抛物面方程与 x-y 平面相交的地方。但绿圈是无穷多个点,而不是一个、两个或三个。这很自然,因为我们将变量的数量增加到两个,但保持方程的数量不变。为了得到有限个解,我们需要有和变量一样多的方程,也就是两个。

对于我们的第二个方程,有许多候选项可供选择。为了简单起见,让我们复制现有的等式,并稍微移动一下。就这样,我们现在有了两个方程。

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

图 3:我们想演示求解多个方程。为了简单起见,我们只需将抛物面的现有方程复制,形成第二个方程。

此外,第二个方程也与 x-y 平面相交于一个圆,这两个圆相交于两个不同的点,这两个点是方程组的解。这是下图中的两个黄色点。

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

图 4:两个抛物线代表我们的两个二次方程相交于两点。

现在,我们如何使用之前的方法得到这些解中的一个?

我们将从 x-y 平面上的任意点开始(下图 5 中的粉红色点)。这可以投影到绿色抛物面上的绿点(这是我们的第一个二次方程)和黄色抛物面上的黄点(我们的第二个二次方程)。然后我们可以在绿色和黄色点画出绿色和黄色抛物面的最佳线性近似。因为线性方程是平面,这给了我们绿色和黄色的平面。这些平面将在紫色线处相交,然后在某点与 x-y 平面相交。这一点是两个线性方程组的解(两个抛物线的近似)。

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

图 5: NR 次迭代——我们重复求解通过逼近两个二次方程得到的线性方程组。这使我们越来越接近原二次系统的一个真实解。

这一点当然不是二次方程组的解,因为我们“作弊”,用线性方程组近似。但是我们从这个新的点开始重复整个过程。而这样反复做,就把我们带到了两个二次方程的其中一个解(两个黄点)。

如果我们想要第二种解决方案呢?那么,我们从一个不同的随机起点开始,重复这个过程,直到找到一个我们以前没有见过的解。

**注意:**你会在最优化的背景下看到牛顿拉夫森,而在这里,我们把它描述为一种解方程的方法。但是当我们注意到最优化通常涉及到寻找梯度(导数的向量)并将其设置为零时,它就变成了求解方程组的问题。

看看这个博客的视频版本:

利用天气数据用 SARIMAX 预测纽约出租车需求

原文:https://towardsdatascience.com/newyork-taxi-demand-forecasting-with-sarimax-using-weather-data-d46c041f3f9c?source=collection_archive---------12-----------------------

如何使用 SARIMAX 统计模型预测纽约市未来的出租车需求?

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

卢克·斯塔克波尔Unsplash 上拍摄的照片

axi 需求预测对于打车(和电子打车)公司来说已经变得极其重要,这是了解他们需求和优化车队管理的一种方式。

在这篇文章中,我们将介绍一种预测纽约某一地区出租车载客数量的方法。我们将执行时空时间序列分析,然后对包括天气数据在内的聚合数据应用众所周知的统计方法 SARIMAX。

数据描述:

出租车数据:

我们将使用纽约出租车需求公共数据,这些数据可从以下网址获得:https://www1 . NYC . gov/site/TLC/about/TLC-trip-record-data . page

这里我们就用 2015 年 1 月到 2016 年 2 月的数据。

要从纽约数据集网站快速下载大量数据到你的 google drive 账户,你可以通过这个链接跟随我的教程。

天气数据:

对于天气数据,我们将使用来自视觉交叉提供者的数据:https://visualcrossing.com/weather-data

理论的味道:

时间序列预测是数据科学领域的研究热点之一。从统计方法到深度神经网络,已经测试了各种模型和方法。

当涉及到时间序列预测时,机器学习爱好者往往会直接跳到机器学习模型,避免使用统计方法,在许多情况下,统计方法在精度测量和所有预测范围内都表现出更好的性能,尤其是对于单变量时间序列,让我们首先了解一下如何对时间序列问题进行分类。Jason Brownlee 博士在他的书中介绍了一个框架来对时间序列问题进行分类,如下所示:

输入与输出:

输入:提供给模型的历史数据,以便进行单一预测。

输出:除了作为输入提供的数据之外,对未来时间步长的预测或预报。

内源与外源:

内生变量:受系统中其他变量影响的输入变量,也是输出变量所依赖的变量。

外生变量:不受系统中其他变量影响的输入变量,也是输出变量所依赖的变量

回归与分类:

预测一个数字量 vs 分类为两个或多个标签中的一个。

单变量与多变量:

一段时间内测量的一个或多个变量

单步与多步:

预测下一个时间步与预测多个未来时间步。

静态与动态:

拟合模型一次并用于进行预测 vs 在每次预测之前根据新获得的数据拟合模型

连续与不连续:

观测值一致与观测值不一致。

现在你对我们面临的时间序列问题有了一个概念,让我们介绍一下 SARIMAX 模型:

SARIMAX 有点解释:

SARIMA 代表(季节性自回归综合移动平均)ARIMA 模型的一种变体,该模型用于单变量时间序列预测,支持序列的季节性成分。它支持自回归和移动平均组件。它有三个超参数来指定序列的季节性成分的自回归(AR)、差分(I)和移动平均(MA ),以及季节性周期的附加参数。

x 代表包含外生变量。

萨里玛(P,D,Q)

p :趋势自回归顺序。

d :趋势差序。

q :趋势移动平均订单。

  • P :季节性自回归顺序。
  • D :季节性差异订单。
  • Q :季节性移动平均订单。
  • m :单个季节周期的时间步数。

理论到此为止,现在让我们进入代码吧!

数据预处理:

在任何机器学习项目中,数据清理/预处理都是非常重要的步骤,但这是一个很累的步骤,如果你对应用 SARIMAX 方法比对清理数据更感兴趣,你可以跳过这一部分,因为我将通过驱动器文件夹为你提供结构化和清理的数据,但理解数据是如何清理和结构化的很重要。

PS:在“已清理数据文件夹链接”部分找到指向已清理数据文件夹的链接

data_Jan_2015 = dd.read_csv("/content/drive/My Drive/nyc-data/yellow_tripdata_2015-01.csv")data_Jan_2015.head()

我们在 dask dataframe 上导入 2015 年第一个月的数据,该数据包含许多特征,但我将仅展示我们清理数据所需的特征:

tpep_pickup_datetime: 提货的日期和时间

tpep_dropoff_datetime: 下车的日期和时间

**皮卡 _ 经度:**皮卡的经度

**皮卡 _ 纬度:**皮卡的纬度

**落客 _ 经度:**落客的经度

**落客 _ 纬度:**落客的纬度

**行程 _ 距离:**行程距离

首先,我们只选择需要的列,计算 trip_duration 和 speed,以便在数据清理过程中使用它。

准备好数据框后,我们将通过分析每列的四分位图来移除错误值:

行程距离四分位数:

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

出行距离四分位数

在打印四分位数和百分位数后,我们观察到第 99.9 个百分位数的出行距离为 22.58 英里,然而,第 100 个百分位数的值为 15420004.9 英里,这非常高。因此,我们将删除行程距离大于 23 英里的所有数据点。

在其他量化变量上做同样的过程(ِCheck 的 GitHub 回购代码)

速度四分位数:

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

速度四分位数

总票价:

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

总票价

取货/卸货清洁:

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

纽约的皮卡

通过可视化纽约外的一些接送,一些在海洋中,这些将从数据中删除。

使用 KMeans 对需求进行聚类:

在清除了纽约的需求之后,我们继续使用 Kmeans 算法对皮卡的需求进行聚类。我们把它分成 30 组。

#Clustering pickupscoord = new_data_Jan_2015[["pickup_latitude", "pickup_longitude"]].valuesregions = MiniBatchKMeans(n_clusters = 30, batch_size = 10000).fit(coord)cluster_column = regions.predict(new_data_Jan_2015[["pickup_latitude", "pickup_longitude"]])new_data_Jan_2015["pickup_cluster"] = cluster_column

接下来,我们将按地区对需求进行分组。

#Grouping the mounthly data by regionnew_data_Jan_2015.rename(columns={'tpep_pickup_datetime':'time','trip_distance':'demand',},inplace=True)new_data_Jan_2015['time'] = pd.to_datetime(new_data_Jan_2015['time'])grouped_new_data_Jan_2015 = new_data_Jan_2015[["pickup_cluster", "time", "demand"]].groupby(by = ["pickup_cluster", "time"]).count()

让我们想象一下这些区域:

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

最后,我们通过添加天气成分并以 1 小时为时间步长对需求进行重新采样来准备每个聚类的需求。

#Cluster example
cluster = grouped_new_data_Jan_2015.loc[0]
#Resampling Data in region j into one hour step
cluster = cluster.resample('1h').sum()
#Feature Engineering
cluster['Date time'] = cluster.index
cluster['Date time'] = pd.to_datetime(cluster['Date time'].dt.strftime('%Y-%m-%d %H'))
cluster['hour']=cluster['Date time'].dt.hour
cluster['day']=cluster['Date time'].dt.day
cluster['dayofweek']=cluster['Date time'].dt.dayofweek
#Merging with weather data
df_merge_col = pd.merge(cluster, weather_data, on='Date time')
cluster['temperature'] = df_merge_col.Temperature.values
cluster['wind_speed'] = df_merge_col['Wind Speed'].values
cluster = cluster[['hour','day','dayofweek','temperature','wind_speed',
'demand']]

群集(区域)将如下所示:

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

集群中的需求

这代表了一个地区的需求时间序列,我们将把它输入到我们的模型中以预测未来的需求。

每个区域将保存在一个单独的文件中。

我们将采取同样的步骤来准备其他月份的数据(预处理、清理、聚类、添加天气特征、将每个地区的需求保存在相应的文件中)

导入清理的数据:

我正在通过这个驱动器文件夹提供清理后的数据,

在这里,您可以找到所有按区域清理和聚类的 NewYork 数据,我们将使用这些文件来训练和测试我们的模型。

现在我们将开始研究预测问题,我们提醒你这是一个时间序列预测问题,我们需要测试时间序列的平稳性以便能够应用 SARIMAX 模型。

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

季节性/趋势分解(一月一月)

从分解图中,我们可以发现一天(24 小时)的季节性

平稳性检验

#Stationarity test
import statsmodels.tsa.stattools as sts
dftest = sts.adfuller(train.iloc[:,:].demand)
print('ADF Statistic: %f' % dftest[0])
print('p-value: %f' % dftest[1])
print('Critical Values:')
for key, value in dftest[4].items():
  print('\t%s: %.3f' % (key, value))

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

平稳性测试结果

增强迪基-富勒测验:

零假设(H0):这表明时间序列有一个单位根,这意味着它是非平稳的。它有一些依赖于时间的结构。

替代假设(H1):它表明时间序列没有单位根,这意味着它是平稳的。它没有依赖于时间的结构。

p 值> 0.05:接受零假设(H0),数据有单位根,非平稳。

p 值<= 0.05: Reject the null hypothesis (H0), the data does not have a unit root and is stationary.

Here we have the p-value <= 0.05 which means that the data is stationarity.

Reading cleaned data:

Now we confirmed that our process is stationary, we will read each regions demands and split the available data between train and test sets. We have data from January 2015 to February 2016.

Train / Test split:

**列车数据:**2015 年 1 月至 2015 年 12 月

测试数据:【2016 年 1 月和 2 月

split_date = pd.Timestamp('2016-01-01')
train = cluster_cleaned.loc[:split_date]
test =  cluster_cleaned.loc[split_date:]

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

测试数据时间序列

可视化测试集,我们可以观察到 1 月 24 日左右数据的奇怪行为。这几点会影响模型的结果,所以我们会考虑到这一点。快速谷歌研究我们可以发现,这种行为是由于 2016 年 1 月 24 日那一周发生在美国和纽约市的怪异事件,特别是封闭道路影响了出租车需求。

来源:https://www . NBC NewYork . com/news/local/NYC-new York-city-blizzard-biggest-ever-23-2016 年 1 月/831660/

使用 SARIMAX

现在我们的数据已经准备好,让我们开始吧,SARIMAX 有订单和季节性订单超参数需要调整,首先将对每个地区需求数据应用超参数调整,以使用 get_sarima_params 函数找到最佳模型设置:

接下来,我们使用此函数应用 sarimax 模型,请注意,在 get_sarima_params 中,我们在季节顺序 24 中指定了季节性,因为我们在时间序列中检测到了 24 小时的季节性。

不要忘记忽略 2016 年 1 月 23 日至 26 日之间的测试数据,因为纽约市的暴风雪导致了需求的错误行为。

让我们在第一个集群上测试该模型,并使用 MAE 和 RMSE 指标评估结果。

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

群集 1 上的梅& RMSE 训练和测试错误

可视化预测

现在我们将使用 Plotly 库来可视化测试集上的预测结果:

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

测试集上的预测可视化

我们可以看到,SARIMAX 模型在检测时间序列的季节性方面表现良好,而在检测变化(趋势)值方面表现不佳,这可以解释为某些问题中的异常检测。

我们还可以看到 1 月 23 日至 26 日期间,模型无法检测到奇怪的行为。

清理的数据文件链接:

在这个 drive 文件夹 中找到,纽约出租车需求数据被清理并整理成 30 个簇,你可以将这个文件夹复制到你的 drive 中,导入到你的笔记本中,直接应用。黑客快乐!

笔记本代码:

[## k4der 2r g/new York-Taxi-需求预测-带 SARIMAX

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/k4der2rg/NewYork-Taxi-demand-prediction-with-SARIMAX)

结论:

我希望这个教程对你有用。你从打车和电子呼叫公司需要预测未来需求的动机开始。接下来是时间序列预测和 SARIMAX 模型背后的理论。然后你开始下载、探索和清理需求数据。这使我们能够更好地理解时空预测问题的行为和结构,并检验时间序列的季节性和平稳性。最后,我们实现了在纽约数据上应用 SARIMAX 模型的方法。

需要改进的领域:

要改善 SARIMAX 模型在数据上的结果,您可以尝试多种方法,例如:

  • 将数据重新排列成更小的时间步长(比如 10 分钟),以便模型更好地检测波动。
  • 根据更多值调整 SARIMAX 模型。
  • 将需求聚集到更多的区域。

参考

  • Python 时间序列预测简介 Jason Brownlee 博士
  • 关于数据清理的一些步骤,我参考了这个 repo

新一代企业数据科学家

原文:https://towardsdatascience.com/next-gen-enterprise-data-scientists-d9ef729d80b9?source=collection_archive---------47-----------------------

EY 和 IBM 是如何改变现状的

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

自 2016 年以来,我们都听说过由 Gartner 使“公民数据科学家”成为主流的术语。如果你没有,那么你可能处在一个不断缩小的行业圈子里,这个圈子还没有被数字颠覆所颠覆。

公民数据科学家是能够使用或生成模型的人,这些模型利用了高级诊断分析或预测和规定功能。他们经常使用自动化工具,如 Alteryx 或 Power BI,通常可以生成比一般数据分析师稍微复杂一些的见解。

公民数据科学家是一种组织的数字化方式,通过提高现有员工的技能来挖掘通常可以在组织中找到的未使用的数据。安永(EY)通过向员工提供在 Udemy 上免费学习大量技术课程的机会来做到这一点。

但这就够了吗?公民数据科学家能否发掘组织内部潜在的洞察力宝库,同时必须能够在日常运营和实践之间周旋,同时努力更加以数据为中心?

进入企业数据科学家。

W 什么是企业数据科学家?

公民数据科学家和企业数据科学家的主要区别在于工作重点和范围。企业数据科学家通常来自组织或行业背景,已经完全转变为数据科学家的角色。他们曾经面向行业或客户的角色已经不复存在,使他们能够主要专注于数据科学和数据工程研究和任务。

企业数据科学家是一个成熟的数据科学家,但来自一个有着不同视角的组织或行业背景。

面对现实吧,一个数据科学家的工作并不容易。大量的时间花在研究和理解数据的本质上,然后测试和开发初始模型,然后进一步完善和重新完善。这还不是全部,还必须仔细实施该方法,记录流程和结果。

这样做了一遍又一遍。

毕竟,科学的脊梁是建立在细致的文献记录和复制结果的能力之上的。我们对昙花一现的奇迹不感兴趣。

这不是公民数据科学家拥有的奢侈品。他们的底线仍然是交付产品或服务,技术只是一个使能器。会有捷径和半生不熟的努力,但它肯定比没有任何技术支持的传统方式要好。

然而,企业数据科学家通过内部定位来填补这一空白,例如在组织的 R&D 或创新部门。在那里,他们可以与公民数据科学家合作,将他们最初的想法或原型开发成成熟的应用程序甚至软件。

我本人是作为一名企业数据科学家写这篇文章的。我之前是一个面向客户的角色,后来在内部转向专注于研究和开发供内部和商业使用的数据软件和平台。

不断增长的联盟

组织和企业不断增长的数据需求并没有被忽视。一些组织已经开始采取战略步骤,进一步加快工作的数字化。就在最近,EY 和 IBM 宣布了一项全球多年联盟,这无疑将使双方咨询业务的客户受益。

除了商业附加值,这种性质的合作为研究和开发提供了难得的机会,从长远来看,这可能会推动更大比例的价值。

EY 是一家跨国企业集团,业务遍及全球所有行业;而 IBM Watson Group 专注于商业和企业人工智能和自然语言。

Watson 拥有自己的服务套件,可以轻松实现日常数据科学活动。值得注意的是 IBM Cloud Pak for Data 采用完全集成的数据平台,允许存储资产、管理笔记本电脑和提供人工智能服务。最重要的是,这提供了一个环境,企业数据科学家可以直接与 IBM 的数据专家合作构建模型、应用程序和产品。

随着商业数据和智能的结合,我们可能会看到下一代智能“人工顾问”在行业中取得突破。这是成为企业数据科学家的大好时机!

角色和职能

连接点

在过去的几年里,作为 EY 公司的企业数据科学家,我与 IBM 密切合作。两个组织有两种传统上不同的商业模式,你可以想象在工作方式上会有很大的差异。

然而,我在这里要分享的是,这种体验在某种程度上是无缝的,因为每个团队的角色都是为联合交付技术产品而设置的。下图是我们在此类产品开发过程中典型的数据科学团队结构。

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

合作技术开发环境中各种角色的说明

行业本地的企业数据科学家(内部数据专家)应该与技术本地公司的外部数据科学同行保持第一接触点。能够执行简单数据处理和分析的数据分析师或公民数据科学家可以进一步帮助他们。与完全专注的企业数据科学家不同,公民数据科学家通常轮流担任角色,并提供从其特定领域获得的特定见解。

鉴于企业数据科学家对领域知识的理解和对数据架构的熟悉,他们负责为组织设计整体数据策略。这是传达给外部技术-本地数据科学家,建筑师和工程师,他们可以处理开发的纯技术方面。

结合工业和技术专业知识

因为企业数据科学家专注于行业/领域,所以他们通常对其领域中的数据如何表达以及在建模中使用它们的细微差别有最完整的理解。

作为一名在医疗保健领域拥有 10 年经验的从业者,并不意味着你是医疗保健数据方面的专家。相反的情况通常是正确的,有经验的领域从业者可能基于他们的经验怀有偏见,并且不能客观地查看数据。

另一方面,非特定领域的数据科学家具备处理数据的统计专业知识和技能。然而,需要大量的时间将他们的专业知识调整到他们没有经验的新的复杂领域。

通过拥有内部的企业数据科学家,组织可以节省宝贵的时间和资源来弥合这一巨大差距。

可解释性的层次

最后,产业本地组织和技术本地组织之间的成功合作取决于达到的沟通水平。

任何经历过数字重组的组织都明白,技术素养是一个现实问题。

除非你从事技术工作,否则你不太可能理解数据处理。同样,技术原住民也很难理解需要几十年经验才能掌握的领域。

回到弥合差距的功能,企业数据科学家充当沟通者,或者作为可解释性的附加层。对产业本地人和技术本地人都有利。

从行业的角度来看,他们可以以对业务有意义的方式捕获和解释数据,从而推动更容易的采用。从技术的角度来看,他们理解架构和工程的局限性,并且能够将令人痛苦的不合理的要求保持在最低限度。

为了证明对企业数据科学家的需求,请考虑以下示例:

在会计领域,我们有外部审计师和内部会计师。组织永远不会雇佣一个对会计有所了解的人来担任内部职务;他们将雇用一名真正的会计师。

同样,在技术和数据占据中心舞台的未来,组织需要具备内部数据科学能力。

组织需要有自己的企业数据科学家

感谢阅读!

如果你觉得这篇文章有用就分享吧!如果你想看我以上任何一个实验的更多故事,请告诉我。

使用 PySpark 进行新一代测序数据分析

原文:https://towardsdatascience.com/next-generation-sequencing-data-analysis-with-pyspark-888a1e0a079?source=collection_archive---------14-----------------------

了解如何使用 PySpark 在 Google Colab 中分析基因组数据

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

信用:pix abay/monar

有了 DNA,你必须能够分辨哪些基因是打开的,哪些是关闭的。目前的 DNA 测序无法做到这一点。下一代 DNA 测序需要能够做到这一点。如果有人发明了这个,那么我们就可以开始非常精确地确定疾病的治疗方法。——埃隆·马斯克

单细胞 RNA 测序(scRNA-seq)等下一代测序技术的快速发展要求对大数据进行高效的并行处理和分析。Hadoop 和 Spark 是存储和处理海量数据集的首选开源框架。Spark 最显著的优势是其迭代分析能力与内存计算架构相结合。打电话。弹性分布式数据集(RDD)上的 cache()有效地将其保存在内存中,并使其立即可用于计算;因此,后续的过滤、映射和归约任务变得即时。Spark 有自己的查询语言 Spark SQL,它的 MLlib 库非常适合机器学习任务。

scRNA-seq 可以做马斯克指出的事情——以细胞分辨率计算出哪些基因被打开/关闭。但这不是灵丹妙药——因为基因表达的调控并不停留在转录水平。mRNA 或信使 RNA——基因蛋白质编码区的代表——也包含决定核糖体蛋白质合成水平的序列。表达的蛋白质也可以经历一系列激活或抑制它的翻译后修饰。

设置笔记本

这些年来,我一直喜欢用 Google Colab 做我所有快速而肮脏的项目原型。使用 Colab,您可以跳过启动项目所需的所有初始步骤。无需设置虚拟环境、依赖关系等。此外,Colab 还配有免费的 GPU/TPU,可满足您所有的机器学习需求。

首先,去 Colab,按照上面的链接,启动一个新的 Python 3 笔记本。PySpark 需要 Java (Java 1.8)和 Scala,所以我们接下来会安装它们。

现在安装 PySpark。这里我们将安装 pyspark[sql],因为它将允许我们处理。gtf,。床,。砰然后。萨姆稍后归档。

!pip install pyspark[sql]

从 NCBI 序列读取档案(SRA)下载数据

已发表研究的序列数据可从 SRA 下载。在使用 sra 文件之前,您需要解压缩。使用 SRA 工具包中的 fastq-dump 工具。除了 SRA 文件,你可以直接下载。fastq、. fa.gz 或. fastq.gz 格式;现在可以用 PySpark 读取这些文件。

注意文件的大小。

PySpark 的魔力

PySpark 的核心是弹性分布式数据集(RDD);它代表了可以并行操作的不可变的数据分区集合。

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

信用:【https://cwiki.apache.org/】T4

首先初始化一个 spark 上下文。

import pyspark as spark
from pyspark import SparkConfsc = spark.SparkContext.getOrCreate(conf=set_conf())

为您的工作流传递一个最佳配置也是一个很好的实践。

数据分析

现在是时候读取我们的数据并将其转换到 RDD 了。

data = sc.textFile(path/to/fastq)
# in case you have a list of sequences
data = sc.parallelize(your_list)# lets take a look at the first read
# each read in fastq is represented by 4lines 
data.take(4)

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

让我们只从数据中提取序列。

sequences = data.filter(lambda x: x.isalpha())
sequences.count() # outputs the size of RDD - the number of reads
# => 1843156

看一下前四段。

sequences.take(4)

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

找出读数的长度怎么样?这只需要一行代码!

read_lengths = sequences.map(lambda seq: len(seq))
read_lengths.take(10)

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

接下来,计算读取的平均长度。

len_sum = read_lengths.reduce(lambda a, b: a+b)
len_sum//read_lengths.count()
# => 564

最后,我们来算一下基数。首先,我们将使用 list() 将序列分成单独的碱基,然后使用 flatMap 将列表合并成一个。然后,我们遍历各自的基并创建一个元组,将第一个元素作为基,将“1”作为其值。然后我们使用 reduceByKey 通过元组的第一个元素——key——来聚合值。

base_count = sequences.flatMap(lambda seq: list(seq))\
                       .map(lambda c: (c, 1)) \
                       .reduceByKey(lambda a, b: a+b)
base_count

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

我们这里只关心 A,T,G,C;剩下的都是神器。如你所见,这些序列富含 GC。

我在这里只触及了皮毛,您可以使用 PySpark 做许多很酷的事情。我会带着另一篇文章回来。

你可以在 这个 colab 笔记本 里找到代码示例。

我希望我已经介绍了足够的基础知识,可以帮助您开始学习。下次再聊。

下一级数据可视化

原文:https://towardsdatascience.com/next-level-data-visualization-f00cb31f466e?source=collection_archive---------28-----------------------

完整的 Plotly 手册

制作能激发灵感的图表/第 1 部分:制作定制图表

在古印度文献中,对哲学概念的解释通常以否定这个概念是关于什么而不是关于什么开始。利用一个反复出现的短语 neti neti (意思是既不是这个也不是那个),这个想法是,告诉什么东西不是,至少和解释那个概念/想法的实际意义一样重要。跟随这些古代哲学家的脚步,让我首先列举出本文不涉及的内容:

  • 这篇文章不是关于如何在 plotly 中快速制作图表,通常只有一行代码,就像 plotly express 一样。如果这是你感兴趣的,请跟随威尔·科尔森的这篇惊人的媒体文章
  • 本文也不是要列出所有可用于数据可视化的不同图表类型。如果这是你正在寻找的,看看这篇非常翔实的文章萨曼莎·李乐。事实上,本文只讨论了两种不同的图表类型:折线图和散点图。

介绍

任何数据分析项目都有两个基本目标。首先,以易于理解的形式整理数据,揭示隐藏的模式,并确定关键趋势。第二,也许更重要的是,通过深思熟虑的数据可视化将这些发现有效地传达给读者。这是一篇介绍性文章,讲述了如何开始考虑定制可视化,以方便地将关键数据特性传播给查看者。我们通过超越使 plotly 在数据分析师中如此受欢迎的单线图,并专注于个性化的图表布局和美学来实现这一目标。

本文中使用的所有代码都可以在 Github 上获得。这里展示的所有图表都是交互式的,并且是使用 jovian 渲染的,这是一个用于共享和管理 jupyter 笔记本的不可思议的工具。Usha Rengaraju 的这篇文章包含了如何使用这个工具的所有细节。

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

来源

Plotly

Plotly 是数据可视化的自然选择库,因为它易于使用,文档记录良好,并允许定制图表。在接下来的章节中,我们先简要总结一下 plotly 架构,然后再进行可视化。

虽然大多数人更喜欢使用高级的plotly.express模块,但在本文中,我们将关注使用plotly.graph_objects.Figure类来呈现图表。虽然 plotly 网站上有大量的文档,但这些材料对于那些不熟悉可视化的人来说可能有点难以接受。因此,我努力提供一个清晰和简洁的语法解释。

我们将使用的 plotly graph_objects由以下三个高级属性组成,绘制一个图表主要涉及指定这些属性:

  • data属性包括从超过 40 种不同类型的轨迹中选择图表类型,如[scatter](https://plotly.com/python/line-and-scatter/)[bar](https://plotly.com/python/bar-charts/)[pie](https://plotly.com/python/pie-charts/)[surface](https://plotly.com/python/3d-surface-plots/)[choropleth](https://plotly.com/python/choropleth-maps/)等,并将数据传递给这些函数。
  • layout属性控制图表的所有非数据相关方面,如文本字体、背景颜色、轴&标记、边距、标题、图例等。在处理大型数据集时,我们将花费相当多的时间操纵这个属性来进行更改,如添加一个额外的 y 轴或在一个图形中绘制多个图表。
  • frames用于指定制作动画图表时帧的顺序。本系列的后续文章将广泛利用这一属性。

对于本文中制作的大多数图表,下面三个是我们将使用的标准库:

对于 python 的新手,可以看看我之前的一篇关于使用 pandas 进行数据争论的文章。其中一些工具将用于提取和转换可视化数据。

[## 使用熊猫进行数据争论的备忘单

将原始数据转换成函数形式

towardsdatascience.com](/data-wrangling-in-pandas-a-downloadable-cheatsheet-84326d255a7b)

本文的其余部分分为以下几个部分:

  1. 折线图
  • 基本折线图
  • 定制的折线图
  • 何时不使用折线图

2。散点图

  • 基本散点图
  • 带有下拉菜单的图表
  • 散点图矩阵

折线图

基本折线图

还有什么比折线图更常规的呢?当考虑数据可视化时,这是首先想到的事情之一。我们首先利用 gapminder 数据集来呈现一个包含许多数据点的折线图。对于那些不熟悉这种数据可视化经典的人,请查看他们的网站这种基于 gapminder 数据的动画,它在不到 5 分钟的时间内捕捉了近 200 年的世界历史。

首先,我们使用一行plotly.express代码制作折线图,其中绘图主要涉及指定xy变量,绘图title和用于color编码数据的变量。

现在让我们使用 plotly graph_objects制作同样的图表。

我们首先使用fig1 = go.Figure()初始化一个空的 go figure 对象。然后,我们通过使用来自plotly.graph_objectsgo.Scatter类为每个国家添加一个轨迹fig1.add_trace来绘制一个折线图。这个类可以通过改变mode参数来制作折线图和散点图,该参数可以采用由+连接的"lines""markers"、&、"text"的任意组合。add_trace用于向go.Figure()对象提供数据参数。此外,我们还可以为后续图表提供布局参数框架参数,我们将在后续文章中介绍。

这是基本的折线图。我们使用只有一行代码的plotly.express和使用基本上做同样事情的graph_objects的稍长代码来绘制它。

这张图表看起来不可怕吗?只有我有这种感觉吗?到处看到这样的图表,你不烦吗?

我们使用 plotly graph_objects类制作这个图表的原因是,在决定定制图表的数据布局 ( & 框架)属性时,它提供了很大的灵活性。

定制的折线图

就数据可视化美学而言,我一直很欣赏 Nathan Yau 在流动数据中的作品。例如,检查这张图表,然后将其与我们刚刚制作的图表进行比较。从默认设置的世界走向这种定制的图表是我在这篇文章和本系列后续文章中的最终目标。

让我们开始定制那个可怕的图表。

**配色方案。**所有数据要么是离散的,要么是连续的。离散数据集是指各个数据点相互独立的数据集。在我们刚刚可视化的 GDP 数据集中,每个国家的 GDP 独立于其他国家的 GDP,因此我们必须使用来自px.colors.qualitative模块的颜色来可视化它。查看 plotly 文档以获得该模块中所有可用颜色集的完整列表。这种颜色集中的所有颜色具有不同的色调但是具有相似的饱和度和值

连续数据集的值在一个范围内变化,就像这张失业率图一样。我们使用顺序配色方案来可视化这种数据,其中数据的大小映射到颜色的强度。本系列的后续文章将更多地关注颜色以及如何有效地将颜色用于数据可视化。

回到定制我们的折线图。我们使用cycle工具通过palette = cycle(px.colors.qualitative.Pastel)交互**Pastel**调色板中的不同颜色。每次我们绘制数据点时,我们使用marker_color=next(palette).选择其中一种颜色

**轴。**让我们开始格式化轴,去掉矩形网格,在长度为 10 的轴外添加记号(showgrid=False, ticks="outside", tickson="boundaries", ticklen=10)。我们也去掉了 y 轴,有了粗黑的 x 轴(showline=True, linewidth=2.5, linecolor='black')。所有这些参数都被传递给update_xaxesupdate_yaxes.

**布局。**最后,我们指定几个参数来定义图表的整体布局,即:

将所有这些放在一起,这就是我们得到的结果:

这张图表可能与 Nathan Yau 的一些作品不一样,但它仍然比默认的 plotly 图表好得多。这个图表代码也代表了我们将在本文中绘制的所有其他图表的基本框架。上图中的几个关键变量可以很容易地改变,以改变剧情的整体美感。这些包括绘图和纸张背景颜色(plot_bgcolor='#ffffff' & paper_bgcolor = '#ffffff'))、渲染数据的颜色集(px.colors.qualitative.Pastel)和轴布局(update_xaxes & update_yaxes))。一定要花时间玩这些。

何时不使用折线图

现在让我们制作一个类似于上图的图表,但是是针对不同的数据集。

随着变量数量的增加,折线图变得难以解释,因为不同变量对应的颜色可能难以区分,或者至少需要一些努力。一种选择是使用堆积面积图。只需将stackgroup=’one’添加到上面代码中的 add_trace 函数中,就可以获得相同数据集的堆积面积图。

为了比较不同的数据点,面积图优于线图。在上面的图表中,我们不仅可以看到一个国家排放了多少二氧化碳,还可以比较它与其他国家的表现。中国排放的二氧化碳略多于美国和 EU-28 国的总和,这是我们可以从堆积面积图中立即得出的结论,而同样的结论很难从简单的线图中得到解释。

散点图

基本散点图

虽然折线图呈现了一个变量随时间变化的过程,但散点图也可用于绘制两个或多个相关或不相关变量之间的变化。我们从绘制一个公开可用的数据集开始,该数据集包含两个指数( GCAGGISTEMP ),测量自 1880 年以来的平均地表温度变化。这次我们使用plotly.graph_objectsgo.Scatter类和mode='markers' 。代码的整体主干与以前基本相同。

现在让我们添加另一个变量,在(大致)相同的持续时间内的平均海平面数据。结果是,当两个温度指数从-0.5 到 1.5 变化时,海平面从 0 到 10 变化。如果我们在同一条轴上绘制这两条曲线,温度曲线基本上会变平。所以我们用fig = make_subplots(specs=[[{"secondary_y": True}]])初始化一个有两个不同 y 轴的图形。现在,当使用fig.add_trace将每个轨迹添加到该图时,我们必须通过提供一个额外的参数secondary_y.来指定绘制它的 y 轴

带有下拉菜单的图表

如果我们想在同一张图表上绘制更多的变量呢?我们可以利用下拉菜单来选择单个变量,一次显示一个。为此,我们需要做到以下几点:

  1. layout添加一个新的参数,它包括所有要添加的按钮的列表buttons= list_updatemenus和这个下拉菜单的位置x=1.2,y=0.8updatemenus=list([dict(buttons= list_updatemenus, x=1.2,y=0.8)])
  2. 使用list_updatemenus. 指定下拉菜单的选项列表,包括:
  • label:出现在下拉菜单中的变量名称
  • method:决定当从下拉菜单中选择特定选项时如何修改图表。它可以是下列之一:restyle(修改数据)、relayout(修改布局)、update(修改两个数据的&布局)和animate(开始或结束动画)。
  • args:这包括(1) visible一个列表,指定哪个数据集将以布尔列表的形式绘制。该列表的大小与添加到该图中的迹线数量相同。'visible': [True, False]表示将显示两个go.Scatter图中的第一个。(2) 'title'绘制变量时显示在顶部的标题。

将所有这些放在一起,我们得到以下结果:

尝试使用下拉菜单选择单个变量。

散布矩阵

使用下拉菜单可能并不总是一个好的选择,因为我们经常希望同时可视化几个变量,以揭示它们之间可能的关系。我们可以使用散点图来做到这一点,散点图是探索性数据分析的一部分。

我们将使用的天气数据集记录了几个变量,如最高和最低温度、风向和风速、阳光量和相对湿度。对于每个数据点,我们也知道结果,即是否下雨以及降雨量。现在的目标是可视化所有这些变量是如何影响降雨结果和降雨量的。我们首先给结果变量分配标签(0 表示无雨,1 表示下雨)。然后,我们使用go.Splom模块在矩阵上绘制所有这些变量。同样,基本的代码主干保持不变。

注意,我们没有指定调色板,而是使用了 colorscale 来代替colorscale='temps'。查看 plotly 文档获得所有可用的colorscales.列表showupperhalf参数设置为 true 时将产生一个完整的矩阵,其中每个变量都绘制两次,一次在对角线下方,一次在对角线上方。

结论

在本文中,我们讨论了如何绘制基本的折线图,以及何时用面积图替换它。然后,我们讨论了如何绘制散点图以及何时用散点图替换散点图。在此过程中,我们讨论了如何定制 plotly graph_objects 来生成符合我们要求的图表。我们通过对基本的代码主干做一些小的改动来实现这种定制。

本系列的后续文章将重点讨论绘制地图,深入使用颜色和动画来实现高级数据可视化。感谢阅读。请分享您的反馈。

基于自然语言处理和深度学习的下一个单词预测

原文:https://towardsdatascience.com/next-word-prediction-with-nlp-and-deep-learning-48b9fe0a17bf?source=collection_archive---------1-----------------------

虚拟助理项目

使用 LSTM 设计单词预测系统

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

来源:照片由阿玛多·洛雷罗在 unsplash 上拍摄

如果你的设备能够预测你打算输入的下一个单词是什么,这不是很酷吗?这类似于预测文本键盘在 What’s App、Facebook Messenger、Instagram、电子邮件甚至谷歌搜索等应用上的工作方式。下面是一张理解这些预测性搜索的图片。

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

作者截图

当我们输入天气如何时,我们已经收到了一些预测。我们可以看到某些接下来的单词是天气预报。对特定用户发短信或打字的下一个单词的预测可能非常棒。通过了解用户的短信模式,可以节省大量时间。这也可以被我们的虚拟助手用来完成某些句子。总的来说,预测搜索系统和下一个单词预测是一个非常有趣的概念,我们将实施。

注: 这是虚拟助手系列的 part-2。同一主题将有更多即将到来的部分,我们将介绍如何使用深度学习技术和 python 构建自己的虚拟助手。

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

来源:乔恩·泰森在 unsplash 上拍摄的照片

简介:

本节将介绍构建的下一个单词预测模型将确切执行什么。该模型将考虑特定句子的最后一个单词,并预测下一个可能的单词。我们将使用自然语言处理、语言建模和深度学习的方法。我们将从分析数据开始,然后对数据进行预处理。然后,我们将对这些数据进行符号化,最终建立深度学习模型。深度学习模型将使用 LSTM 的来构建。完整的代码将在文章的最后提供,并带有一个到 GitHub 库的链接。

方法:

文本数据的数据集很容易找到,我们可以考虑古登堡计划,这是一项数字化和存档文化作品的志愿者工作,旨在“鼓励电子书的创作和发行”。从这里我们可以得到许多故事、文档和文本数据,这些都是我们问题陈述所必需的。数据集链接可以从这里获得。我们将使用弗朗兹·卡夫卡的《变形记》一书中的文本。你可以从这里下载数据集。然而,如果你有时间收集自己的电子邮件和短信数据,那么我强烈建议你这样做。这将对你的虚拟助理项目非常有帮助,在这个项目中,预测关键词将做出类似于你的短信风格或类似于你撰写电子邮件的风格的预测。

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

来源:布雷特·乔丹在 unsplash 上拍摄的照片

预处理数据集:

第一步是从变形数据集中移除所有不必要的数据。我们将删除数据集的起点和终点。这是与我们无关的数据。起始行应该如下所示:

一天早上,当格雷戈尔·萨姆萨从烦恼的梦中醒来时,他发现

数据集的结束行应该是:

第一个站起来伸展她年轻的身体。

这一步完成后,将文件保存为变态 _ 干净. txt。我们将通过使用 utf-8 编码来访问变态 _ 干净. txt。我们清理过程的下一步包括替换所有不必要的额外新行、回车和 Unicode 字符。最后,我们将确保我们只有独特的话。我们将每个单词只考虑一次,并删除任何额外的重复。这将有助于模型训练更好地避免由于单词重复而造成的额外混淆。下面是文本数据预处理的完整代码。

标记化: 标记化是指将较大的文本数据、短文或语料库分割成较小的片段。这些较小的片段可以是较小的文档或文本数据行的形式。它们也可以是一本单词词典。

Keras 标记器允许我们对文本语料库进行矢量化,方法是将每个文本转换为整数序列(每个整数是字典中标记的索引)或向量,其中每个标记的系数可以是二进制的,基于单词计数,基于 tf-idf。要了解更多关于使用 Keras 的 Tokenizer 类和文本数据预处理的信息,请访问此处

然后我们将文本转换成序列。这是一种将文本数据解释为数字的方式,以便我们可以对它们进行更好的分析。然后,我们将创建训练数据集。“X”将包含输入文本数据的训练数据。“y”将包含训练数据的输出。因此,“y”包含每个输入“X”的所有下一个单词预测。

我们将通过使用从 tokenizer.word_index 中提取的长度来计算 vocab_size,然后给它加 1。我们添加 1 是因为 0 是为填充保留的,我们想从 1 开始计数。最后,我们将把我们的预测数据“y”转换成 vocab 大小的分类数据。这个函数将一个类向量(整数)转换成二进制类矩阵。这将有助于我们的损失,这将是分类 _ 交叉熵。用于数据标记化、创建数据集以及将预测集转换为分类数据的其余代码如下:

**注:**可在预处理中进行改进。您可以尝试不同的方法来改进预处理步骤,这将有助于在更少的时期内实现更好的损失和准确性。

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

来源:照片由 Clement H 在 unsplash 上拍摄

创建模型:

我们将建立一个连续模型。然后我们将创建一个嵌入层,并指定输入维度和输出维度。将输入长度指定为 1 是很重要的,因为预测将只针对一个单词进行,我们将收到该特定单词的响应。然后,我们将添加一个 LSTM 层到我们的架构。我们会给它一个 1000 单位,并确保我们返回的序列为真。这是为了确保我们可以通过另一个 LSTM 层。对于下一个 LSTM 层,我们也将通过另一个 1000 单位,但我们不需要指定返回序列,因为它默认为假。我们将使用 relu 设置为激活的密集层函数,通过一个具有 1000 个节点单元的隐藏层来传递它。最后,我们让它通过一个具有指定 vocab 大小和 softmax 激活的输出层。softmax 激活确保我们接收到一堆与 vocab 大小相等的输出概率。我们的模型结构的完整代码如下所示。在我们看完模型代码之后,我们还将看一下模型概要和模型情节。

模型摘要:

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

模型图:

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

回访:

我们将为下一个单词预测模型使用的回调如下面的代码块所示:

我们将导入培训我们的模型所需的 3 个回调。3 个重要的回调是 ModelCheckpoint、ReduceLROnPlateau 和 Tensorboard。让我们看看每个回调函数执行什么任务。

  1. ModelCheckpoint —这个回调用于存储我们的模型在训练后的权重。通过指定 save_best_only=True,我们只保存模型的最佳权重。我们将使用损失指标来监控我们的培训。
  2. ReduceLROnPlateau —该回调用于在指定数量的时期后降低优化器的学习率。这里,我们将耐心指定为 3。如果准确度在 3 个时期后没有提高,那么我们的学习率相应地降低 0.2 倍。这里用于监控的指标也是损耗。
  3. tensor board—tensor board 回调用于绘制图形的可视化,即精度和损耗的图形绘制。这里,我们将只看下一个字预测的损失图。

我们将把基于度量损失的最佳模型保存到文件 nextword1.h5 中。在访问预测功能和尝试预测我们的下一个单词时,该文件将是至关重要的。我们将等待 3 个时期来改善损失。如果没有改善,那么我们将降低学习率。最后,如果需要,我们将使用 tensorboard 函数来可视化图形和直方图。

编译和调整:

下面是编译和拟合模型的代码块。

我们正在最后一步编译和装配我们的模型。这里,我们正在训练模型并将最佳权重保存到 nextword1.h5,这样我们就不必重复地重新训练模型,并且可以在需要时使用我们保存的模型。这里我只根据训练数据进行了训练。但是,您可以选择同时使用训练数据和验证数据进行训练。我们使用的损失是 categorical _ crossentropy,它计算标签和预测之间的交叉熵损失。我们将使用的优化器是 Adam,学习率为 0.001,我们将编译度量损失模型。我们的结果如下所示:

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

图表:

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

预测:

对于预测笔记本,我们将加载以 pickle 格式存储的记号化器文件。然后,我们将加载保存在目录中的下一个单词模型。我们将使用这个相同的标记器对我们应该进行预测的每个输入句子执行标记化。在这一步之后,我们可以通过使用保存的模型对输入句子进行预测。

我们将在运行预测时使用 try 和 except 语句。我们使用这个语句是因为万一在查找输入句子时出现错误,我们不希望程序退出循环。只要用户希望脚本运行,我们就希望运行脚本。当用户想要退出脚本时,用户必须手动选择这样做。只要用户愿意,程序就会运行。

让我们简单地看一下模型所做的预测。这是按如下方式完成的:

进入你们的行列:在沉闷的
天气
进入你们的行列:收集纺织品
样品
进入你们的行列:多么艰苦的
事业
进入你们的行列:停止脚本
结束节目…

这可以通过使用预测脚本来测试,该脚本将在本文的下一节中提供。我将在下一节给出 GitHub 库的链接。正如我们所看到的,预测模型可以对大多数行进行最佳预测。“停止脚本”行将结束模型并退出程序。当我们输入行“停止脚本”时,整个程序将被终止。对于所有其他的句子,对输入行的最后一个单词进行预测。我们将考虑每行的最后一个单词,并尝试将其与下一个概率最高的单词匹配。

**注意:**在某些情况下,程序可能不会返回预期的结果。这是显而易见的,因为每个单词只被考虑一次。这将导致特定句子的某些问题,您将不会收到想要的输出。为了提高模型的准确性,你可以考虑尝试二元模型或三元模型。在这种方法中,我们只使用了一元语法。此外,在预处理步骤中还可以完成一些额外的步骤。总的来说,有很大的改进余地。

观察:

我们能够为变形数据集开发高质量的下一个单词预测。我们能够在大约 150 个时期内显著减少损失。我们开发的下一个单词预测模型在所提供的数据集上相当准确。预测的总体质量是好的。然而,可以对模型进行某些预处理步骤和某些改变,以改进模型的预测。

就这样,我们到了文章的结尾。整个代码可以通过这个链接访问。下一个单词预测模型现在已经完成,并且在数据集上表现得相当好。我建议你们所有人利用电子邮件或短信数据来预测下一个单词。这对你的虚拟助理项目会更好。请随意参考 GitHub 库获取完整代码。我也强烈推荐 Machine Learning Mastery 网站,这是一个可以了解更多信息的神奇网站。这对这个项目有很大的帮助,你可以查看网站这里。非常感谢你阅读这篇文章,我希望你们都有美好的一天!

NFL 球员呼吁与期望

原文:https://towardsdatascience.com/nfl-play-calling-vs-expectation-61074fa77a60?source=collection_archive---------30-----------------------

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

蒂姆·米尔克在 Unsplash 上的照片

哪些教练和四分卫对传球/跑位影响最大?

介绍

美式足球的独特之处在于,教练的决策和准备对比赛有着相对强烈的影响。在足球、曲棍球、篮球和几乎任何其他运动项目中,运动员并不按照教练设计的脚本(也称为“比赛”)排好队。当然,这些运动中有设计好的打法——但它们远不是这项运动的核心。对于裁判来说,在曲棍球 2 v 1 休息时吹哨子并说,好吧,你有 40 秒的比赛时间来计算你想如何接近这个得分机会,这将是很奇怪的。

打电话决策自然是很有争议的。永恒的斗争之一是基于结果的事后分析,尤其是在高杠杆的情况下。说“好吧,你应该把球跑到那里”是人类的天性。例如,第四下,短码的情况往往可以决定整个比赛的结果。有关于是否尝试完全转化的决定,也有关于如何转化的决定。如果球队最终失败,球迷们将不可避免地批评这两个决定。我确实认为,在过去的十年左右,随着梦幻足球、公开数据等的出现,球迷的知识显著增加了。所以很有可能他们在某些评论中是正确的。

这可能是最臭名昭著的“你应该运行它”的戏剧之一

打电话的决策经常受到批评,但球迷们迄今为止提供支持证据的能力有限。我们真的需要详细的数据来测试这种假设。每一个决定都有情境背景,一个人必须适应情境来评论这个行动号召。最著名的是,nflscrapR 数据集使这成为可能。这个图书馆通过使其易于访问而使详细资料大众化。

我在这篇文章中的目标并不是批评 NFL 的教练,而是分析他们对传球和跑位的影响。很有可能我最终会批评一小撮教练,但这不是我的使命。我确实认为应该尊重教练,因为他们已经达到了他们职业的顶峰,并且比我学习这项运动的时间更长。我也认为称他们不会犯错是错误的,因为最终,有太多的证据表明,一些人通过基本的决策来花费或获得他们的团队分数。在其他体育运动中也有类似的情况,公共数据分析显示传统信仰效率低下。最著名的大概是篮球中多投三分球的想法。

方法

最终,我感兴趣的是团队如何称之为游戏与期望。当球队落后时,他们往往会传球更多,因为传球变化更大,花费的时间更少。另一方面,如果一个团队经常领先,他们更有可能叫停比赛,因为他们更稳定,跑得更快。对于每一场比赛,我们可以找到一个联盟预期值,然后比较球队的表现。我从 CardinalsViz 在 Twitter 上的一条推文中获得了这个想法,我建议关注这条推文:

我将比较几种不同的方法,并展示每种方法的优缺点。这实际上是一个突出不同方法的很好的例子。我将从贝叶斯推理开始,然后转向更传统的机器学习方法。

什么是贝叶斯推理?

贝叶斯推理是一种测量未观测变量的方法。去看一场足球比赛,我们知道比赛结束的时间、距离和剩余时间。这些是独立的观察变量。如果我们想知道一个队通过的机会呢?一种解决方法是使用逻辑回归、决策树或机器学习方法。这些都很棒,但是它们有一个缺陷,就是它们是黑箱,而且只给出了点估计。贝叶斯推理以相似的准确性实现了相同的目标,但也量化了不确定性,并允许您进入过程的窗口。代价是这些概率规划推理方法需要一定水平的统计知识,增加了计算时间,并且难以扩展。缩放贝叶斯方法是一个非常活跃的研究领域。如果它们的规模更大,它们将成为许多领域的主导方法。

贝叶斯推理方法尤其适用于足球。另一个好处是,它们允许我们用少量数据更新先前的猜测。在一个 16 场比赛的赛季中,这是至关重要的。例如,我们有红人队临时主教练比尔·卡拉汉 11 场比赛的数据,所以我们应该比约翰·哈博更不确定他对比赛的影响,我们有他 10 个赛季的数据。

还有一个优点是,贝叶斯允许我们对教练的行为进行事先猜测。Kliff Kingsbury 承诺通过传球,或基本上是短码传球,彻底改革 NFL。比尔·卡拉汉在接手之前曾表示,他相信“建立跑步记录”会带来成功。对于这两位教练来说,我们的通过率会有很大不同。这看起来像是欺骗,但这是帮助模特的真实生活专家信息。经典方法没有直接的类比来整合专家信息。

或许同样重要的是,你可以告诉模型“我不知道这个家伙会如何表现”。第三位新教练扎克·泰勒,直到比赛开始我才知道。贝叶斯分析将允许我们对像他这样的人使用无知的先验。

这听起来像推销吗?如果是的话,我很抱歉,但我认为贝叶斯推理在许多领域都没有得到充分利用,所以我倾向于积极支持它。另一方面,这个问题实际上也很好地展示了它的弱点。

通过率模型

让我们从头开始,从头开始构建我们的模型。首先,我将从我们的 360,000 部戏中选取 5000 部戏,并找出一个基本通过率。为什么我要限制样本量?我之所以限制它,是因为我之前描述过的弱点——贝叶斯推理扩展性不好。对于那些实践机器学习方法的人来说,限制数据似乎很奇怪,因为数据越多几乎总是越好。

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

我也给通过率模型一个 beta (3,3)先验,说“我不认为真正的通过率会接近 100%或接近 0%”。如果你看过一两场足球比赛,这是显而易见的。我们真的不需要这么多数据的先验,但我想增加这一步,因为对于一些应用程序来说,良好的先验是必不可少的。在选择先验知识时,一个人可能犯的唯一大罪是对可能发生的事情赋予零概率。由于贝叶斯规则涉及乘以先验,所以在可能区域中具有零概率的先验将实际似然数据乘以零。Christopher Fonnesbeck 在 youtube 上有很多很棒的概率编程讲座,如果你想深入了解 prior choice 和其他相关模型,我建议你看几个。我也很乐意回答任何问题。

最后,这是我们简单通过率模型的代码:

这个 PyMC3 代码为我们提供了过去 10 年 NFL 球队通过率的可信区间:

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

注意,我只使用了第一次到第三次的数据。

右图显示了我们的 MCMC 样本链。我试图避免沉重的数学细节,所以我会掩饰它。我用 4 条链运行了 MCMC 算法,因此有 4 种颜色。如果右边的图看起来与上面的“毛毛虫”有任何不同,这意味着链没有收敛,您的结果有问题。

即使我们的样本很小,我们也可以确信 NFL 球队的真实通过率在 58%到 61%之间。你可能会说,“取平均值就行了!”。取平均值也是可行的,但是它不会给你任何不确定性区间。在预测结果和解释预期时,不确定性区间是必不可少的。当样本量很小时,它们也更重要——这在足球界经常发生。

最后,这个分析没有时间因素。一个潜在的改善方法是一个赛季接一个赛季的提高通过率。

基本逻辑回归模型

假设我们观察到球队落后时传球更多的行为。我们可以用分数差作为协变量来模拟通过率。添加一个预测变量会导致模型发生根本变化。我们不会像上面那样输出一个漂亮、干净的通过率分布,因为我们说通过率取决于分数差。

好消息是,这个新模型可以推广到你想要的任何特征,甚至是分类特征。当然,功能选择将变得很重要。使用更多的功能将是下一步。以下是我的逻辑回归模型代码,该模型使用了一个预测值,即分数差:

注意:我使用学生的 t 分布作为先验。它非常类似于正态分布,但是具有更宽的尾部(假设 nu 较低)。这在文学作品中很常见,但正常在这里也适用,因为我使用了广泛的无知先验。

现在,我们有了一个可以根据分数差异进行大幅调整的模型:

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

我想指出两件事。首先,随着更多数据接近零分差,我们的后验概率更加确定。在尾部附近,后验线呈扇形散开——我们对领先或落后 20 分的球队的数据更少。其次,分数差为零时的通过率与二项分布中的通过率相匹配。这是一个好兆头!如果有不匹配,我会担心某处出错。

添加分类变量

假设我们想要比较多个教练。在 PyMC3 中,很容易将分类变量添加到贝叶斯分析中。让我们比较一下即将到来的超级碗教练趋势:

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

鉴于相同的得分差距,安迪·雷德肯定比凯尔·沙纳汉更有可能通过。一个可能的原因是安迪·雷德有一个潜在的世代 QB 和沙纳汉有平均 QB 发挥。我们怎么能知道安迪·雷德是否因为他的四分卫而传球更多呢?通过更多的贝叶斯推断!

不过,我们很快就遇到了问题。为了准确捕捉预测变量,我们需要尽可能多的数据。然而,随着数据量的增加,MCMC 采样变得更加难以计算。其核心是,MCMC 试图“作弊”来解决积分——随着更多的数据和更多的特征,这些积分最终变得难以处理。实际上,这意味着我能够在大约 10,000 部戏剧上运行这些模型。过去 10 年的播放数据集有 360,000 次播放,因此这是不令人满意的。

标度贝叶斯推理

有多种方法来扩展这个模型,但没有一种是完美的。我会掩饰一些线索,但我不想陷入困境。正如我之前提到的,这是一个活跃的研究领域,有用的解决方案可以极大地促进多个学科的发展。首先,并非所有的 MCMC 算法都是相同的,因此并非所有算法的规模都相同。

那些来自机器学习背景的人可能会倾向于使用更多的 GPU 来解决这个问题。这是可能的,但 MCMC 采样要求链之间有一定的通信量。如果你正在寻找如何做到这一点,我建议探索 EP-MCMC 和近似 MCMC。

变分推理

变分推理结合了机器学习、梯度下降和贝叶斯推理背后的引擎。它使用一种算法来执行梯度下降,以调整和转换后验分布,直到它们最小化误差(误差通常计算为 Kullback-Leibler 散度)。这很好,因为它避免了计算边际似然积分和 MCMC 样本。不幸的是,正确执行 VI 可能需要大量精力来寻找正确的参数和避免局部极小值。即使是简单的线性回归也需要大量的参数化工作。产出不确定性中的误差也很难量化。

矢量化 MCMC

最初我打算列出这篇文章,因为我无法超越我上面所做的。相反,两天前,我在我的时间线上看到了这条非常及时的推文:

@903124S 发了一个关于逻辑回归的矢量化 MCMC 实现,这正是我需要的。这允许我将计算时间减少到一个合理的水平。因此,我能够描绘出安迪·雷德在他打更多普通四分球时的顶级四分球:

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

不出所料,我们可以相信帕特里克·马霍斯在中锋位置上时,安迪·雷德传球更多。我还输入其他变量,如停机时间、距离和剩余时间。这允许我们通过将不同的情况输入到模型中来调整情况:

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

请注意,y 轴与之前的可视化相比已经发生了变化。这显然是一种暂时的情况。

情境调整倾向是非常强大的,可能对那些为 NFL 比赛总数建模的人非常有用。一个很好的例子就是即将到来的超级碗 49 人队。他们在最近几个赛季遭受了许多伤病,这造成了一些奇怪的分裂。有了吉米·加罗波洛,他们在第一次到第三次进攻中有 53.6%的时间传球。有了他的替补四分卫,他们有 61.2%的机会通过。这清楚地表明,主教练凯尔·沙纳汉相信他的替补会比首发投得更多,对吗?如果没有其他信息,p 值等传统指标将显示完全的统计确定性,即凯尔·沙纳汉与吉米·加洛波洛的传球次数更多。相反,49 人队在他的许多快照上都领先。他的后援没有这种奢侈。通过贝叶斯推理,我们可以看到,无论四分卫是谁,凯尔·沙纳汉的传球量大致相同:

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

我在这里没有包括联盟平均水平,因为它几乎完全等于没有吉米 G 线。

问题

我们还有一些问题。我使用的贝叶斯逻辑回归模型只能处理以一致速率增长的单调特征。此数据集中的要素并不都具有这种质量。例如,在上半场结束前,球队很快就会传球,这是很常见的。我上面展示的模型不能正确区分上半场还剩两分钟和下半场开始。比赛中期的高通过率给它带来了麻烦,使它更难估计后面的情况。

支持向量机(SVM)和深度神经网络都能够处理这些线性但非单调的特征。这里我将重点介绍支持向量机,因为与其他众所周知的方法相比,它非常适合这个数据集,而且速度也非常快:

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

最右边的 SVM 明显优于其他常用方法。上面使用的逻辑回归是最左边的。

这些传统机器学习方法的快速性和准确性是很大的好处。当然缺点是无法用小样本对教练建模,无法给出不确定性的量化估计。一个 SVM 会给出一个通过率的点估计,不管是 3 号和 3 号的菜鸟主教练,还是 3 号和 15 号的 10 年主教练。当然,我们知道其中一种情况应该非常准确,而另一种情况应该非常不确定。

SVM 应用

众所周知,西雅图的头蔻驰·皮特·卡洛尔致力于跑步(除了我之前嵌入的比赛视频)。今年,这种理念受到了审视,因为他没有伟大的跑位,却拥有联盟最好的四分卫之一。我的假设是,他因为过早的跑动而损失了球队的分数,而不是像安迪·雷德那样进攻。我还没有测试过这个,所以也许他没有。

是的,这里有缺陷和警告。教练会告诉你跑位会设置传球。聪明的足球人可能会说传球的回报在减少,尽管支持这一点的证据有限。很多人也用卡罗尔的输赢记录来为他的职业生涯辩护。

而且,皮特卡罗尔对足球的了解也比我多。最后,传球可以让时钟停止,所以一般来说,叫更多的传球会导致更多的传球——因此有一些我没有考虑的二阶效应。

记住所有这些警告,我发现皮特·卡罗尔在 2019 年比联盟平均教练多跑 10%的时间。这是极端的,这也是我选择这个例子的原因。在我测试的其他皮特·卡罗尔赛季中,差异并没有这么大。这也不太合理,因为今年他的跑位和传球效率的差异更大。

这意味着他本赛季比一个普通的联盟主教练在同样情况下多跑了大约 107 分。他的传球,使用 nflscrapR 的 EPA/play,比他在那段时间的跑位效率高出近 0.17 分。这意味着他让他的球队在整个赛季中因为没有达到联盟平均水平而损失了大约 19 分。

结论

对于比较各种机器学习方法的优缺点来说,这是一个很好的玩具问题。在这篇文章的研究中,我还偶然发现了最近开发的贝叶斯神经网络和贝叶斯支持向量机。它们承诺消除我在这里描述的模型的一些弱点。就像我之前说的,这是一个非常活跃的领域,我迫不及待地想以新的方式应用新的方法。

我赶在超级碗之前发表这篇文章,所以我的代码没有平时那么干净,评论也没有平时多。很快会有更新,但是我想享受这个游戏。这里的是我的 Kaggle-GPU 驱动的矢量化 MCMC 代码,这里的是我在本地机器上使用的代码。

NGBoost

原文:https://towardsdatascience.com/ngboost-aca51711c8f5?source=collection_archive---------23-----------------------

梯度助推器

梯度增强中的自然梯度

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

保罗·吉尔摩Unsplash 上拍摄的照片

在表格数据领域,梯度助推器的统治几乎已经完成。在大多数真实世界以及竞赛中,几乎没有一个解决方案不具有来自梯度推进算法之一的至少一个模型。但是,随着机器学习社区的成熟,机器学习应用程序开始得到更多的使用,对不确定性输出的需求变得很重要。对于分类,梯度增强的输出形式已经可以让您了解模型预测的可信度。但是对于回归问题,情况并非如此。模型吐出一个数字,告诉我们这是它的预测。如何从点预测中获得不确定性估计?这个问题不仅仅是梯度推进算法的问题。但几乎适用于所有主要的 ML 算法。这是新成员 NGBoost 试图解决的问题。

如果你还没有读过本系列的前几部分,我强烈建议你去读一读,至少是第一部分,在那里我谈到了梯度增强算法,因为我认为你已经知道了什么是梯度增强。我也强烈建议阅读 VI(A) ,这样你会对什么是有更好的理解。

自然梯度增强

NGBoost 的关键创新是在 boosting 算法中使用自然渐变而不是常规渐变。通过采用这种概率途径,它在协变量的条件下,模拟了结果空间上的全概率分布。

该文件将他们的方法模块化为三个部分

  1. 基础学习者
  2. 参数分布
  3. 评分规则

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

基础学习者

在任何增强技术中,都有一些基础学习者被组合在一起以得到一个完整的模型。NGBoost 不做任何假设,并声明基础学习者可以是任何简单的模型。该实现支持决策树和岭回归作为开箱即用的基础学习器。但是您可以轻松地用任何其他 sci-kit 学习风格模型来替换它们。

参数分布

在这里,我们不是训练一个模型来预测作为点估计的结果,相反,我们预测的是一个完整的概率分布。每个分布都由几个参数来参数化。对于 eg,正态分布由其平均值和标准偏差来参数化。你不需要其他任何东西来定义一个正态分布。因此,如果我们训练模型来预测这些参数,而不是点估计,我们将有一个完整的概率分布作为预测。

评分规则

任何机器学习系统都有一个学习目标,通常情况下,它的任务是最小化一些损失。在点预测中,用损失函数将预测与数据进行比较。评分规则类似于概率回归世界。评分规则将估计的概率分布与观察到的数据进行比较。

*适当的评分规则 S 将预测的概率分布 P 和一个观察值 y *(结果)作为输入,并给预测分配分数 S(P,y) ,使得结果的真实分布获得预期中的最佳分数。

最常用的适当评分规则是对数分数 L ,当最小化时,我们得到 MLE

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

这就是我们在很多地方看到的对数可能性。评分规则由θ参数化,因为这是我们作为机器学习模型的一部分所预测的。

另一个例子是 CRPS(连续排名概率得分)。对数分数或对数似然将均方误差推广到概率空间,而 CRPS 对平均绝对误差做了同样的事情。

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

广义自然梯度

在这个系列的最后一部分,我们看到了什么是自然梯度。在那次讨论中,我们谈到了 KL 散度,因为传统上,自然梯度是根据 MLE 评分规则定义的。但本文提出了这一概念的推广,并提供了将这一概念推广到 CRPS 评分规则的方法。他们将 KL 散度推广为一般散度,并为 CRPS 评分规则提供了推导。

把所有的放在一起

现在我们已经看到了主要组件,让我们看看所有这些组件是如何协同工作的。NGBoost 是一种用于概率预测的监督学习方法,它使用 boosting 来估计条件概率分布 P(y|x) 的参数。如前所述,我们需要提前选择三个模块化组件:

  1. 基础学习者( f
  2. 参数概率分布( P 由θ 参数化)
  3. 合适的评分规则( S

对新输入 x 的预测 y|x 以条件分布 p 的形式进行,其参数θ通过对 M 个基本学习器输出和初始θ₀.的相加组合来获得让我们用 f ⁽ᵐ⁾[2].来表示 m 个基本学习者学习到的所有参数的组合函数并且对于所选择的概率分布中的每个参数,将有一组单独的基础学习者。例如,在正态分布中,μ有 f ⁽ᵐ⁾,log σ有 f ⁽ᵐ⁾。预测的输出也用一个特定阶段的比例因子(ρᵐ)和一个通用的学习速率η进行缩放:

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

这里需要注意的一点是,即使你的概率分布有 n 个参数,ρ仍然是一个标量。

算法

让我们看看论文[2]中解释的算法。

让我们考虑一个数据集

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

助推迭代 M ,学习率η,带参数θ的概率分布,合适的评分规则 S ,基础学习器 f

  1. 初始化θ₀到边缘。这只是估计分布的参数,不考虑任何协变量;类似于初始化为均值。数学上,我们解这个方程:

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

  1. 对于 M 中的每次迭代:
  2. 针对数据集中的所有 n 个示例,计算评分规则 s 相对于前一阶段θᵢᵐ⁻的参数的自然梯度 gᵢᵐ。
  3. 用于迭代 f ⁽ᵐ⁾的一组基本学习器适合于预测自然梯度 gᵢᵐ.的相应分量这个输出可以被认为是自然梯度在基础学习者类别的范围上的投影,因为我们正在训练基础学习者预测当前阶段的自然梯度。
  4. 该投影梯度然后通过比例因子ρᵐ.进行缩放这是因为自然梯度依赖于局部近似(正如我们在前面的帖子中看到的),这些局部近似在远离当前参数位置的地方不会保持良好。
    在实践中,我们使用线搜索来获得最佳的比例因子,使整体评分规则最小化。在实现中,他们发现在线搜索中将缩放因子减半效果很好。
  5. 一旦估计了缩放参数,在通过学习速率进一步缩放之后,我们通过将负缩放投影梯度添加到前一级的输出来更新参数。

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

履行

该算法在https://github.com/stanfordmlgroup/ngboost具有现成可用的 Sci-kit 学习风格实现。让我们来看看调整模型的关键参数。

超参数

  • Dist:该参数设置输出的分布。目前,该库支持用于回归的正态、对数正态和指数分布,用于分类的k _ 分类和伯努利*。默认:正常*
  • 评分:指定评分规则。目前,选项在 LogScore 或 CRPScore 之间。默认值:LogScore
  • 基础:这指定了基础学习者。这可以是任何 Sci-kit 学习估计器。默认为三层决策树
  • n_estimators:提升迭代的次数。默认值:500
  • learning_rate :学习率。默认值:0.01
  • minibatch_frac :在每次提升迭代中使用的行的百分比子样本。这与其说是性能调优,不如说是性能黑客。当数据集很大时,这个参数可以大大加快速度。

解释

尽管在使用来自机器学习模型的重要性之前需要相当多的谨慎,NGBoost 也提供了特性重要性。对于它估计的每个参数,它都有单独的一组重要性。

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

来源:NGBoost 文档[3]

但最精彩的部分不仅仅是这个,还有那辆 SHAP ,也是现成的型号。您只需要使用 TreeExplainer 来获取值。(要了解更多关于 SHAP 和其他可解释技术的信息,请查看我的另一个博客系列——可解释性:打开黑盒)。

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

来源:NGBoost 文档[3]

实验

本文还研究了该算法与其他流行算法相比的性能。有两种不同类型评估——概率评估和点估计

概率回归

在来自 UCI 机器学习知识库的各种数据集上,NGBoost 与其他主要的概率回归算法进行了比较,如蒙特卡洛丢失深度集成混凝土丢失、高斯过程、位置、规模和形状的广义加法模型(GAMLSS)分布式森林

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

点估计

他们还针对其他回归算法(如弹性网络、随机森林(Sci-kit Learn)、梯度推进(Sci-kit Learn))评估了点估计用例上的算法。

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

结论

NGBoost 的性能与现有算法一样好,甚至更好,但它还有一个额外的优势,就是为我们提供了一个概率预测。并且公式和实现足够灵活和模块化,使其易于使用。

但是这里的一个缺点是算法的性能。时间复杂度随着我们必须估计的每个额外参数而线性增加。所有效率方面的改进/改变都已经成为 LightGBM 或 XGBoost 等流行的梯度提升包的一部分,但在当前的实现中并不存在。也许它会很快被移植过来,因为我看到回购正在积极发展,并将其视为他们的目标行动项目之一。但是在这之前,这是相当慢的,尤其是对于大数据。一种解决方法是使用 minibatch_frac 参数来加快自然梯度的计算。

现在我们已经看到了所有主要的梯度助推器,让我们挑选一个样本数据集,看看它们在本系列的下一部分如何表现。

中的其他文章

参考

  1. 甘利顺一。自然梯度在学习中很有效。神经计算,第 10 卷,第 2 期,第 251–276 页。
  2. 段,托尼。NGBoost: 用于概率预测的自然梯度推进,arXiv:1910.03225 v4【cs .2020 年 6 月 9 日
  3. *NGBoost 文档,【https://stanfordmlgroup.github.io/ngboost *

原载于 2020 年 6 月 27 日 http://deep-and-shallow.com**

NGBoost 算法:解决概率预测问题

原文:https://towardsdatascience.com/ngboost-algorithm-solving-probabilistic-prediction-problems-fdbe1858ca61?source=collection_archive---------25-----------------------

ICML 2020 年

预测目标变量的分布,而不仅仅是点估计

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

照片由穆罕默德·阿里扎德Unsplash 拍摄

在翻阅 ICML 2020 录取论文时,我发现了一篇有趣的论文:

[## NGBoost:用于概率预测的自然梯度推进

我们提出了自然梯度推进(NGBoost),一种算法的一般概率预测梯度…

arxiv.org](https://arxiv.org/abs/1910.03225)

你可能会问,我们还需要多少关于梯度推进的论文?但事实上,GBT 算法家族在表格数据上表现得非常好,一直在 Kaggle 排行榜上名列前茅。这项技术非常成功,现在已经扩展到表格数据以外的领域,例如,NLP。

问题陈述

我们在这里处理的问题是,几乎所有的回归算法都不返回给定预测值 P(y|X)的目标变量的分布,而是返回目标变量 E(y|X)的期望,即点估计。这与大多数返回类别概率的分类算法相反。例如,在 scikit-learn 中,分类器有方法predict_proba(),它返回类的概率。回归子缺少这种方法,只有回归概率。

为什么这是一个问题?假设你正试图预测下周的天气。如果您需要提供一个预测范围,即预测 15–20 度,而不是预测 17.25 度,该怎么办?如果你想知道气温不会降到冰点以下的概率?你能用一个模型做所有这些预测吗?是的,如果你的模型返回给定预测值的目标变量的条件概率分布。虽然这不是一个新问题,但大多数解决方案都是针对特定问题的,我们缺少开箱即用的通用算法。

NGBoost 算法解决了这一问题:

概率推理

NGBoost 如何找到目标变量分布?

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

因此,首先,这不是一个非参数模型(如果您对连续变量的非参数建模感兴趣,可以尝试离散化或分位数转换等技术)。你必须对条件目标变量分布做一个假设。例如,它可以是正态分布(但是μ和σ都取决于 X),或者高斯混合(对于许多回归问题来说更现实的情况),或者偏斜的正态分布。对于生存分析,它将是指数分布,对于预测正值,它是伽玛分布的变体,等等。一旦你选择了你的条件分布,问题就简化为学习给定输入变量的分布的参数向量 θ

我们在优化什么?

在处理常规回归问题时,我们通常会最小化均方误差。如果我们试图预测的是一个概率分布,我们如何选择一个损失函数?在这种情况下,我们使用一个评分规则:

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

对于许多问题,我们可以使用负对数似然作为评分函数

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

在这种情况下,我们试图得到的是给定用 θ 参数化的分布,目标值 y 的可能性有多大。参数 θ 最佳选择的评分规则是最小的评分规则。评分规则之间的差异称为离差,是概率分布之间“距离”的度量。如果 Q 是真实分布,P 是预测分布,则散度为:

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

如果我们使用 MLE,那么散度就变成了 kull back–lei bler 散度,广泛用于度量概率分布之间的差异。

自然梯度

NGBoost 中的“NG”代表“自然渐变”。(或者可能是因为它是论文合著者之一吴恩达的姓氏?)为什么非要引进呢?

一旦您开始使用基于梯度的方法来学习参数 θ ,您会很快意识到两个分布之间的距离不是由它们参数的差异定义的,因此参数的梯度方向不一定表示模型的最佳改进方向

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

自然梯度通过使用散度而不是参数差来纠正这个问题:

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

所以,我们不是在参数空间里画一个单位球在点周围,寻找最大改善的方向,而是画一个散度取相同值ε的曲面。自然梯度通过统计流形的黎曼度量与规则梯度相联系:

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

如果我们用 MLE,矩阵 I 就是费希尔信息矩阵

把所有东西放在一起

使用所介绍的技术,可以创建基于 GBT 的算法来预测条件分布的参数:

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

虽然这看起来像一个常规的 GBT 算法,但它有几个重要的区别:

  1. 对于每个例子和迭代计算梯度,然后通过逆黎曼度量来改进,在 MLE 的情况下,这是逆信息矩阵。如果有几个分布参数,这不是一个大问题。
  2. 使用不依赖于示例的缩放因子,这允许对拟合的弱学习器进行全局调整

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

与其他方法相比,该算法表现出良好的性能。比较自然梯度和规则梯度的实验结果非常有趣:

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

我们可以看到,使用自然梯度显著提高了学习,结果更正确地描述了条件方差。

有趣的是,该算法也擅长预测点估计,即使它是在不同的目标上训练的。

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

Github 知识库https://github.com/stanfordmlgroup/ngboost有一个很好的例子,你可以在一个简单的数据集上使用它:

结论

预测连续变量的条件分布是困难的,预测点估计要容易得多。这与分类问题不同,在分类问题中,更多的算法能够预测类别概率。NGBoost 算法允许在给定预测值的情况下容易地获得目标变量的条件分布的参数预测。对于 NGBoost 可以处理的分发类型,几乎没有什么假设。事实上,如果正态分布不适合给定的问题,可以选择另一种分布(可能有更多的参数)来更好地适应模型。正如论文结论中所指出的,该算法有许多改进的可能性,探索理论问题和更复杂的问题。但是即使在这个阶段,NGBoost 也可以用于大多数实际问题的概率回归。

使用 Python 进行 NHL 分析

原文:https://towardsdatascience.com/nhl-analytics-with-python-6390c5d3206d?source=collection_archive---------14-----------------------

我用数据理解 NHL 的冒险

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

这就是我们今天要做的!分析的最终结果显示,亚历克斯·奥韦奇金是左翼之王。

阿纳达。曲棍球。有些人可能会说,他们是一体的,然而我从来没有真正成为一个超级体育迷。在枫叶队进入季后赛的罕见情况下,我会穿上蓝色的衣服,和我的朋友一起去酒吧等待不可避免的失望,但是当他们开始谈论球员和他们在赛季中的表现时,我开始感觉有点疏远。因此,为了更好地理解这款游戏,我决定戴上数据分析帽,深入研究一些 NHL 球员的数据。

我做的第一件事是查看什么样的数据是可用的,哦,我没有失望!实际上有一个未记录的 NHL 数据 API,包含从球队日程和花名册到 的所有内容,包括在冰上拍摄的每一个镜头,包括地点,涉及的球员和发生的时间 。对于那些好奇的人,一个很棒的人创建了一个 GIT 页面,其中有 API 调用的摘要( NHL API 文档)。有了这个,我就有了提取数据和开始做一些球员分析所需的一切。

正在提取 NHL API 数据

我所有的分析都是用 Python 完成的,为了更简单,我使用 Kaggle 作为我的编码平台。如果你还没有检查 Kaggle 出来,你真的应该。他们有一堆开放的数据集、竞赛以及分享和存储你的笔记本的方式(Jupyter)。我将在这里展示的所有代码都可以从我的 Kaggle 帐户上公开获得(参见底部的参考资料),所以请随意探索和使用它。由于数据集如此之深,我决定只提取 2019-2020 赛季的游戏事件数据进行分析。这让我可以访问到目前为止的所有比赛,包括所有冰上项目(击球、击球、对抗等。).

和往常一样,首先要做的是加载包和初始化变量。这里你会看到我使用请求从 API 获取数据,使用 pickle 保存数据以备后用。

import requests
import pickle**# Set up the API call variables**
game_data = []
year = '2019'
season_type = '02' 
max_game_ID = 1290

API 的格式允许我们传入赛季的年份,选择比赛类型,如预赛/常规赛/季后赛(本例中 02 是常规赛),最后是 max_game_ID 。这是给定年份中游戏事件的最大数量。

是时候提取数据并将 json 格式的数据存储到一个列表中了。

**# Loop over the counter and format the API call**
for i in range(0,max_game_ID):
    r = requests.get(url='[http://statsapi.web.nhl.com/api/v1/game/'](http://statsapi.web.nhl.com/api/v1/game/')
        + year + season_type +str(i).zfill(4)+'/feed/live') data = r.json()
    game_data.append(data)

现在所有的甜蜜数据都存储在一个列表中,我们可以把它保存为一个 pickle 文件。我这样做的原因是,我可以把它上传到 Kaggle 上,并让任何想做一些分析的人都可以使用它。Pickle 文件还允许存储对象,并且加载速度非常快。

with open('./'+year+'FullDataset.pkl', 'wb') as f:
    pickle.dump(game_data, f, pickle.HIGHEST_PROTOCOL)

这样我们就有了一个名为 2019FullDataset.pkl 的新文件,我们将其保存为 Kaggle 上的数据集。您可以查看底部的参考资料,获取指向我的数据集的链接。

现在是有趣的事情!你可以做很多事情,但我想做的是观察任何球员在冰面上不同位置的投篮效率,并将它们与整个联盟的平均成功率进行比较。

分析球员投篮数据

利用 NHL 的数据,我想看看在哪里球员的投篮效率最高(进球/总投篮百分比)与联盟平均水平相比。首先,让我们导入所有必需的包。注意我使用 matplotlib 进行所有的绘图。我还安装了 枕头 来导入图像数据。

*import numpy as np 
import pandas as pd 
import pickle    
import matplotlib
import matplotlib.pyplot as plt
color_map = plt.cm.winter
from matplotlib.patches import RegularPolygon
import math
from PIL import Image**# Needed for custom colour mapping!** from matplotlib.colors import ListedColormap,LinearSegmentedColormap
import matplotlib.colors as mcolors*

对于我所有的绘图,我将使用 matplotlib 方法 ListedColormap 来使用自定义的颜色图。因为我想给正值涂上不同于负值的颜色,所以我做了两个颜色映射。

*c = mcolors.ColorConverter().to_rgb()
positive_cm = ListedColormap([c(‘#e1e5e5’),c(‘#d63b36’)])
negative_cm = ListedColormap([c(‘#e1e5e5’),c(‘#28aee4’)])* 

现在我们已经解决了这个问题,让我们加载 2019 年常规赛的 pickle 数据文件,其中包含了每场比赛的所有事件数据。注意,我们正在加载之前生成的 pickle 文件。

*with open(‘../input/nhl-data/2019FullDataset.pkl’, ‘rb’) as f:
    game_data = pickle.load(f)*

计算平均投篮命中率%

加载数据后,我想首先计算出联盟在冰上每一点的平均命中率。使用字典中的事件坐标对象输入数据。在我们的分析中,我们只想关注“射门”和“进球”类型的事件。

下面是一步一步的细分:

首先,我们创建一个字典来保存整个联盟的所有射门和进球数据坐标。

***# Do some dictionary initialisation to hold our cleaned and condensed league data**
league_data = {};league_data[‘Shot’] = {};
league_data[‘Shot’][‘x’] = [];
league_data[‘Shot’][‘y’] = [];league_data[‘Goal’] = {};
league_data[‘Goal’][‘x’] = [];
league_data[‘Goal’][‘y’] = [];*

我们只想保留射门和进球数据中的事件。

*event_types = ['Shot','Goal']*

最后,我们循环播放每一个游戏,并将相关信息提取到我们的字典中。

***# First loop over the elements of game_data. Each one of these is an NHL game and contains all of the game event data.**
for data in game_data: **# It is possible that the game data is not assigned to the data
   set, so to handle this we look for the key ‘liveData’ which 
   contains all of the data we are looking for, otherwise we
   continue**
   if 'liveData' not in data:
        continue **# Drilling down into the dataset to extract the play by play
   information for the game**
   plays = data['liveData']['plays']['allPlays'] 

    for play in plays: **# For each play**
         for event in event_types: ** # For each event (Shot,Goal)** **# If the play contains one of the events**
            if play['result']['event'] in [event]:              **# If the event contains coordinates**
               if 'x' in play['coordinates']: **# Save the coordinates to the growing list**                  league_data[event]
                       ['x'].append(play['coordinates']['x'])
                    league_data[event]
                       ['y'].append(play['coordinates']['y'])*

现在我们有了联赛数据,我们可以对给定的球员做同样的事情。唯一的区别是,在提取数据时,我们将筛选射手子事件类型。

在咨询了一些冰球迷后,我被告知亚历克斯·奥韦奇金是一个很好的测试案例。他活跃在一个非常独特的领域,我们应该能够基于此来验证事情。

球员数据提取与联赛数据提取非常相似,所以我在这里只注意变化:

***# Initialise the player dictionary
full_name = 'Alex Ovechkin'**
player_data = {};player_data['Shot'] = {};
player_data['Shot']['x'] = [];
player_data['Shot']['y'] = [];player_data['Goal'] = {};
player_data['Goal']['x'] = [];
player_data['Goal']['y'] = [];*

现在我们基本上做和以前一样的过程,但是寻找球员数据。

***# Same code as before** ...for play in plays:
   **if 'players' in play:
      for player in play['players']:
         if player['player']['fullName'] in [full_name]  
            and player['playerType'] in ["Shooter","Scorer"]:**for event in event_types:

...*

在上面加粗的部分,你可以看到我们正在过滤作为射手得分手的玩家。每场比赛都包括谁参与其中,所以我们可以简单地看看奥韦奇金是否被列为射手或得分手。有了它,我们可以处理一些数字,做一些很酷的分析。

计算基本统计数据

在我们绘制位置数据之前,我想计算一下与联盟平均水平相比,玩家的高水平数据。

***# Get the total number of shots made by the player** player_total_shots = len(player_data['Shot']['x']) +
   len(player_data['Goal']['x'])**# Find the players goal score percentage**
player_goal_pct = len(player_data['Goal']['x'])/player_total_shots**# Find the total number of shots taken in the league** league_total_shots = len(league_data['Shot']['x']) +
   len(league_data['Goal']['x'])**# Get the league percentage**
league_goal_pct = len(league_data['Goal']['x'])/league_total_shots**# Calculate the spread of the SOG (Shots on Goal) %**
PL_e_spread = player_goal_pct-league_goal_pct*

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

汇总统计数据

这是我们的第一个结果。我们可以把它与官方数据进行比较,结果是一致的。

我们可以看到,奥韦奇金 5.85%的利差意味着他是一个高效的得分手。

但是在冰面上所有的点都是这样吗?他有强势的一面吗,或者有什么弱点吗?现在我们可以开始位置分析了。

射击位置分析和标绘

我们首先要做的是建立一个宁滨网格。我想知道在冰上的什么地方拍摄,但我不想看到所有的个人镜头。通过进行空间平均,我们可以做出更有洞察力和更直观的表示。来自 API 的位置数据来自:

  • X: -100 至 100 米
  • Y: -42.5 至 42.5 米

对于我们的宁滨,我们使用来自 matplotlib 的十六进制图来提取原始的宁滨数据,并将使用绘制的矩形(同样是 matplotlib)来获得最终的视觉效果。

首先,我们定义我们的图形尺寸和网格大小:

***# To keep the aspect ration correct we use a square figure size**
xbnds = np.array([-100.,100.0])
ybnds = np.array([-100,100])
extent = [xbnds[0],xbnds[1],ybnds[0],ybnds[1]]**# We are going to bin in 30 unit increments.  It is fun to play with this!** 
gridsize= 30;mincnt=0*

接下来我们会发现联盟在冰上各个位置的效率。为此,我们调用 hexbin 方法并提取位置顶点和计数数据。

需要注意的一点是,由于记分员从不在自己的网上得分,我们必须确保反面位置总是代表进攻方。

这都是因为每一个周期玩家都会换边,而坐标系是固定的。

***# First concatenate the arrays for x and y league data** league_x_all_shots = league_data['Shot']['x'] 
   + league_data['Goal']['x'];
league_y_all_shots = league_data['Shot']['y'] 
   + league_data['Goal']['y']**# Perform the coordinate flipping!** league_x_all_shots_normalized = [];
league_y_all_shots_normalized = []**# Enumerate the list so we can use the index for y also**
for i,s in enumerate(league_x_all_shots):
    if league_x_all_shots[i] <0:
        league_x_all_shots_normalized.append(-league_x_all_shots[i])
        league_y_all_shots_normalized.append(-league_y_all_shots[i])
    else:
        league_x_all_shots_normalized.append(league_x_all_shots[i])
        league_y_all_shots_normalized.append(league_y_all_shots[i])

**# Do the same treatment for the goals**
league_x_goal_normalized = [];
league_y_goal_normalized=[]
for i,s in enumerate(league_data['Goal']['x']):
    if league_data['Goal']['x'][i] <0:
       league_x_goal_normalized.append(-league_data['Goal']['x'][i])
       league_y_goal_normalized.append(-league_data['Goal']['y'][i])
    else:
       league_x_goal_normalized.append(league_data['Goal']['x'][i])
       league_y_goal_normalized.append(league_data['Goal']['y'][i])*

酷毙了。现在是为了钱!调用 hexbin 图并提取计数和位置。这部分有点长,但只要跟着评论走,就应该很清楚了。

***# First we will used the hexbin function to simply bucket our shot data into basically a 2D histogram**
league_hex_data = plt.hexbin(league_x_all_shots_normalized,
   league_y_all_shots_normalized,gridsize=gridsize,
   extent=extent,mincnt=mincnt,alpha=0.0)**# Now we extract the bin coordinates and counts**
league_verts = league_hex_data.get_offsets();
league_shot_frequency = league_hex_data.get_array();**# Do the same thing for the goal data**
league_goal_hex_data =  plt.hexbin(league_x_goal_normalized,
   league_y_goal_normalized,gridsize=gridsize,
   extent=extent,mincnt=mincnt,alpha=0.0)**# Since the grid is the same we can use a shared bin coordinate set from the above. So here we just get the counts**
league_goal_frequency = league_goal_hex_data.get_array();*

现在我们有了拍摄数据的位置和数量,我们将尝试以一种有用的方式展示它。首先,我们加载半个 NHL 溜冰场的比例模型图像。然后,我们将确保缩放我们的坐标,以匹配图像的大小,从而获得照片在冰上的准确位置。

***# Using matplotlib we create a new figure for plotting**
fig=plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)**# Clean up the figure to be completely blank**
ax.set_facecolor("white")
fig.patch.set_facecolor("white")
fig.patch.set_alpha(0.0)**# Remove the labelling of axes**
ax.set_xticklabels(labels = [''], fontsize = 18,
   alpha = .7,minor=False)
ax.set_yticklabels(labels = [''], fontsize = 18,
   alpha = .7,minor=False)**# Using pillow to get the rink image and extract the image size**
I = Image.open('../input/nhl-images/half.png')
ax.imshow(I);width, height = I.size*

此时,我们应该有一个曲棍球场的图像被绘制成 matplotlib 图像。接下来,我们要确定一些缩放因子和偏移,以对齐我们的图像和数据坐标系。

***# Calculate the scaling factor and offset (trial and error)**
scalingx=width/100-0.6;
scalingy=height/100+0.5;
x_trans=33;
y_trans=height/2**# We will want to scale the size of our hex bins with the image so we calculate a "radius" scaling factor here**
S = 3.8*scalingx;*

终于我们准备好了。Matplotlib 允许在给定的多边形形状中添加面片元素。所以我们将使用六边形元素在溜冰场图像上添加补丁。

***# Loop over the locations and draw the hex**
for i,v in enumerate(league_verts): **# Ignore empty locations**
   if league_shot_frequency[i] < 1:continue

   **# Normalize the shot frequency data between 0-1** 
   scaled_league_shot_frequency =
      league_shot_frequency[i]/max(league_shot_frequency) **# Scale the hexagon size based on shot frequency**
   radius = S*math.sqrt(scaled_league_shot_frequency) **# Finally we will plot the hexagon including the scaling and
   translations we found earlier**
   hex = RegularPolygon((x_trans+v[0]*scalingx, 
      y_trans-v[1]*scalingy),numVertices=6, radius=radius,
      orientation=np.radians(0),alpha=0.5, edgecolor=None) ax.add_patch(hex)*

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

成功!我们在联盟中有一个很好的拍摄分布

你可以看到,平均而言,这些射门相当对称,而且大多发生在网前。你也可以在蓝线上看到一个大的下降,这也是有道理的。

现在让我们对球员数据做同样的事情。唯一的区别是所有的变量都用“球员”替换了“联赛”前缀。例如:

***player**_x_all_shots = **player**_data['Shot']['x'] 
  + **player**_data['Goal']['x'];**player**_y_all_shots = **player**_data['Shot']['y'] 
  + **player**_data['Goal']['y']*

现在,我们将不再只是绘制照片,而是用绿色的显示的目标。**

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

展示了奥韦奇金的射门和进球。

非常酷!我们可以看到他在圆圈上方的左侧非常活跃。在和我的冰球朋友商量后,我发现这真的是他的地盘!但是现在我们应该看看他在冰上的效率。这是他的地盘吗,因为他经常在那里投篮?

为了做到这一点,我将使十六进制的大小,射击频率和颜色代表他的效率。

***# Get some lists initialised** 
league_efficiency = []
player_efficiency = []
relative_efficiency = []**# Looping over the league shots (which are the same in length as player)**
for i in range(0,len(league_shot_frequency)): **# We will only look at positions on the ice where the player or
    league had more than two shots during the season**
    if league_shot_frequency[i]<2 or player_shot_frequency[i]<2:
        continue **# Calculate the efficiencies** 
league_efficiency.append(
   league_goal_frequency[i]/league_shot_frequency[i])

player_efficiency.append(
   player_goal_frequency[i]/player_shot_frequency[i])

**# And the relative efficiency**
relative_efficiency.append(
   (player_goal_frequency[i]/player_shot_frequency[i]-
   (league_goal_frequency[i]/league_shot_frequency[i]))**# Keep track of the max so we can scale the colour and radius of the hex plot after**
max_league_efficiency = max(league_efficiency)
max_player_efficiency = max(player_efficiency)
max_relative_efficiency = max(relative_efficiency)
min_relative_efficiency = min(relative_efficiency)*

最后,我们为绘制在给定位置拍摄的相对效率的最后阶段做好了准备。

***# Loop over the locations and draw the hex**
for i,v in enumerate(player_verts): **# Here we will only include locations where the player made at
    least on shot.  We will adjust this later for plotting.**
    if player_shot_frequency[i] < 1:continue

   ** # Scaling the frequencies**
    scaled_player_shot_frequency =
        player_shot_frequency[i]/max(player_shot_frequency) **# Calculate a radius of the hex**
    radius = S*math.sqrt(scaled_player_shot_frequency)

 **# Find the player efficiency and relative at this point on the
    ice.** player_efficiency =
        player_goal_frequency[i]/player_shot_frequency[i]
    league_efficiency =
        league_goal_frequency[i]/league_shot_frequency[i] **# This is what we were after the whole time!** 
    relative_efficiency = player_efficiency - league_efficiency

   **# Since there can be positive and negative efficiencies
   (relative) we colour the more efficient locations red and the
   less blue.** if relative_efficiency>0:
        colour = positive_cm(math.pow(relative_efficiency,0.1))
   else:
        colour = negative_cm(math.pow(-relative_efficiency,0.1))

   **# And finally we plot!   ** 
   hex = RegularPolygon((x_trans+v[0]*scalingx,
       y_trans-v[1]*scalingy),numVertices=6, radius=radius,
       orientation=np.radians(0),facecolor=colour,alpha=1,
       edgecolor=None)
   ax.add_patch(hex)*

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

看起来不错,但是有点忙。

最后一步,我们可以增加要查看的数据点的阈值。让我们只看看他在赛季中拍摄超过 4 张照片的地点。

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

最终图像可用于后期处理和发布。

哇!现在看起来相当不错!我们可以清楚地看到奥韦奇金在左路是一个超级高效的球员,这也是他投篮最多的地方。与联盟平均水平相比,他是惊人的。

我们确实发现的一件事是,当谈到在网前投篮时,他低于平均水平(根据十六进制尺寸,这看起来很常见)。这可能是帮助他提高或用来对付他的东西!

最后,我把这个给了我的曲棍球朋友,经过回顾,他们基本上说:

”丫显然嗯了一声。那完全是他的地盘!”

你知道吗?这是我能收到的最好的评论。

参考和代码

图片

本文中的所有图片都是我自己用 python + Photopea 制作的

数据抓取

* [## NHL 分析-数据收集

使用 Kaggle 笔记本探索和运行机器学习代码|使用来自非数据源的数据

www.kaggle.com](https://www.kaggle.com/kapastor/nhl-analytics-data-collection)

数据集

[## NHL 数据

2015-2019 赛季常规 NHL 数据。包括所有游戏和事件。

www.kaggle.com](https://www.kaggle.com/kapastor/nhl-data)

数据分析

[## NHL 分析-镜头分布

使用 Kaggle 笔记本探索和运行机器学习代码|使用来自多个数据源的数据

www.kaggle.com](https://www.kaggle.com/kapastor/nhl-analysis-shot-distribution)

NHL API

http://statsapi.web.nhl.com/api/v1/game/*

让 python 代码看起来更好的九个简单步骤

原文:https://towardsdatascience.com/nine-simple-steps-for-better-looking-python-code-87e5d9d3b1cf?source=collection_archive---------6-----------------------

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

来源com break,经由 pixabay 。(Pixabay 许可证)

我会定期查看补充学术论文、发布数据集的代码,或者分析 Kaggle 竞赛的解决方案。

我非常尊重分享代码以复制他们的结果的研究人员,以及参与机器学习(ML)竞赛并分享他们的解决方案的人。

有代码总比没有好,但我相信它的可读性还可以提高。

我看到的代码让我想起了我在学术界写的代码。我不喜欢它。这是我进入工业界的原因之一。在现代社会,能够编写代码变得和懂英语一样重要。

成为一个更好的程序员的最好方法是加入一个高编码标准的公司。尽管如此,在这篇博文中,我将谈论当你独自在代码库工作时的情况。

本文的目标读者是研究人员、数据科学家和初级软件开发人员。

成为一个更好的程序员是一个持续的过程。你写的代码越多,你就变得越好。

我不会告诉你如何变得更好,我会告诉你如何通过对你的开发过程做一些简单的调整来让你的代码看起来更好。

简单,我的意思是容易实现,每一步少于 5 分钟,并且改变应该迫使你做得更好。

我们很懒。如果某件事属于“很好做”的范畴,我们会找到很多正当的理由来避免去做。你被迫做的事情会更好。首先,你会去做,因为你没有选择,后来这就变得很自然了。

另一个原因是决策吸意志力。你在一个小决定上花了一点意志力,结果,增加了第二天早上在健身房睡过头的机会。当我们强迫行为时,我们节省了宝贵的能量。对那些感兴趣的人来说,《意志力:重新发现人类最大的力量》一书广泛地讨论了这个话题。

I:使用版本控制来跟踪代码中的变更

对代码进行版本控制看起来似乎是一个显而易见的必备条件,但事实并非如此。在研究生院,我的导师是一个非常聪明的人,他开发了一种算法来执行哈伯德模型的量子蒙特卡罗。世界各地的许多研究人员使用该代码来推进理论凝聚态物理。当我开始使用它时,我很惊讶竟然没有一个集中的存储库。交换密码是通过电子邮件进行的。一位科学家开发的错误修复和新功能没有传播给其他用户。此外,我还见过我的同事如何使用不同的文件夹进行代码版本控制。

代码版本控制和在 GitHub 或其他服务上使用集中的存储库是必须的。许多研究人员正在这样做,但如果你离开计算机科学,大多数教授和研究生都处于用文件夹版本化和在电子邮件中发送代码的阶段。

**问:**你什么时候需要开始在你的项目中使用 git?
T5 A:从代码的第一行开始。

:什么时候需要在 GitHub 上创建一个资源库,并将代码推送到那里?
**答:**从第一行代码开始。如果您认为由于法律或其他原因,您的代码不能公开共享,请创建一个私有存储库。在所有其他情况下,选择公开回购。

当人们等到代码看起来不错的时候才公开它,这是一个错误。我的许多同事感到非常不安全,因为他们的 GitHub 库中会有不完美的代码。他们担心人们会认为他们是糟糕的程序员。现实一点,没人会评判你。此外,你现在写的每一个代码在六个月后看起来都会很糟糕。

私有存储库比没有存储库要好。公共存储库比私有存储库更好。

我也喜欢 Kaggle 比赛的参与者在比赛结束后释放管道的方式。这是一个好习惯,对他们和社区都有帮助。

此外,有一个叫做 Sourcegraph 的工具,它允许你在所有公共 Github 库的代码中执行搜索。极其方便。这会让你更有效率。

链接:

第二:不要推到总分行

当你在一个团队中工作时,你不要直接推进到主分支。您创建一个单独的分支,在其中工作,创建一个拉请求(PR),将拉请求合并到主服务器。这比直接推动掌握更复杂。原因是为了合并拉请求,您的更改应该通过各种检查。手动检查是由您的合作者进行的代码审查。自动检查包括语法、风格、测试等。

当您独自工作时,您没有机会审查代码,但是自动检查的力量仍然存在。

重要的是调整 GitHub 上的设置,这样即使你想推送也无法到达 master。正如我们上面讨论的,这可以防止滑倒,节省意志力。

III:使用持续集成/持续交付(CI/CD)系统

经历创建分支和合并的过程是一种开销。主要原因是它允许对代码变更进行检查。设置持续集成系统后,将检查您创建的每个拉式请求,只有在通过所有检查后,才会合并拉式请求。

有许多服务提供 CI/CD 功能。GitHub Actions、CircleCi、Travis、Buildkite 等

我会推荐 GitHub Actions。它是免费的,在私有和公共库上都可以工作,易于设置。

所有这些听起来可能非常复杂,但是实际上,您只需要向存储库添加一个配置文件。

  • 一个简单的例子:我的带有帮助函数的库。我检查代码样式、格式,并运行测试。
  • 复杂示例:在albuminations库中,我们检查语法、代码风格、运行测试、检查自动文档构建,并且我们为不同的 python 版本和操作系统 Linux、Windows、Mac OS 执行这些操作

重要!您需要更改 GitHub repo 中的设置,以便在所有检查都处于绿色区域的情况下,您将无法合并请求。

链接:

四:代码格式器

有许多方法可以格式化同一段代码。

  • 函数之间有多少空格?
  • 代码中的行有多长?
  • 进口的排序是怎样的?
  • 应该用什么样的引号来定义字符串?
  • 等等

有一些工具叫做代码格式化器。如果你在你的代码上运行它们,它们会修改代码以符合格式化程序的要求。

通用类型格式化程序:

  • YAPF:非常灵活,你可以配置它来适应你想要的风格。
  • 黑色:无弹性。只能配置线路的长度。

选择一个并将其添加到您的 CI/CD 配置中。我喜欢黑色的。它让我所有的项目和使用黑色的项目看起来都一样。

代码格式化程序减少了上下文切换,这使得代码的阅读更加容易。

还有更具体的格式化程序。例如 。Isort 只是对导入进行排序。

您需要在两个地方运行格式化程序:

  1. 在 CI/CD 中。在这里,您以检查模式运行它:格式化程序会告诉您有他们想要格式化的文件,但是代码将保持不变。
  2. 在提交更改之前。它会重新格式化你的代码。

链接:

V:预提交挂钩

在上一步中,我们讨论了在提交之前在本地运行格式化程序的重要性。

比如说,我们用异色

我们需要运行:

black .isort

这很烦人。更好的解决方案是用这些命令创建一个 bash 脚本,比如“code_formatter.sh”并运行它。

创建这样的脚本是一种流行的方法,但是问题是,除非你强迫他们,否则人们不会这样做。

有一个更好的解决方案叫做预提交挂钩。这个想法类似于 bash 脚本,但是您希望在每次提交之前运行的东西将会在您提交时准确地运行

git commit -m “<commit message>”

这看起来是一个很小的区别,其实不是。使用预提交,您的行为是强制的,并且,正如我们上面讨论的,它工作得更好。

Q : PyCharm 执行良好的格式化。为什么我需要一个预提交钩子?
A :你可能忘了用 PyCharm 格式化你的代码。此外,当你有两个或更多的人,你要确保他们的格式是相同的。对于预提交挂钩,所有人都将拥有相同的配置,这是您的存储库的一部分。我建议两者都做,使用预提交钩子并用 PyCharm 格式化代码。

问:如果我不从控制台执行提交,而是直接从 PyCharm 执行,会怎么样?
答:您可以配置 PyCharm 在每次提交时运行预提交钩子。

链接:

六:棉绒

不改变你的代码,但是检查它并且寻找可能的问题的工具,叫做 linters。最常见的是 flake8

它可以寻找:

它是一个强大的工具,我建议将它添加到您的 CI/CD 配置以及预提交钩子中。

链接:

VII: Mypy:静态类型检查器

从 Python 3 开始,您可以向代码中的函数添加类型注释。这是可选的,但强烈推荐。

一个原因是有类型注释的代码更容易阅读。

当您在代码中看到:

x: pd.DataFrame

它的信息量远远超过

x

当然,你首先不应该把你的变量命名为“x”。尽管如此,在这篇博文中,我谈论的是改进代码的简单、自动的方法,好的命名比简单要难一些。

类型注释是可选的。没有它们,代码也能很好地工作。有一个叫做 Mypy 的工具可以检查:

  • 函数和输入参数的类型注释
  • 变量类型及其操作之间的一致性

这是一个非常有用的工具。所有大的科技公司都会检查他们 python 代码的每一个 pull 请求。

它迫使你编写更容易阅读的代码,重写过于复杂、写得不好的函数,并帮助你识别错误。

应将 Mypy 检查添加到 CI/CD 和预提交挂钩中。

链接:

VIII:对预提交挂钩进行更多检查

你可以用许多不同的东西来扩展你的预提交钩子。

  • 删除尾随空白。
  • 文件的结尾在新的一行。
  • 排序要求 txt。
  • 检查 yaml 文件的正确格式。
  • 等等

您可以为应该自动发生的代码格式化和检查创建自己的挂钩。

链接:

九:外部工具

使用 ML 来分析每个拉请求的代码的工具有一个活跃的开发领域。

所有这些对公共存储库都是免费的,其中一些对私人存储库也是免费的。我看不出有什么理由不为您的公共代码启用它们。

链接:

结论

如果一个人至少实现了这些技术中的一些,他/她的代码将变得更容易阅读。

但这只是第一步。

还有其他标准技术,如:

  • 单元测试
  • 使用假设的单元测试
  • Python 环境
  • 建筑包装
  • 码头化
  • 自动文档
  • 等等

但是它们不能像我上面描述的步骤那样简单地实施。因此,我们将把它们留给另一个时间。😃

对于那些在实现我所描述的技术时有问题的读者,请随时联系我,我会扩展相应的要点。

熊猫高级索引的本质

原文:https://towardsdatascience.com/nitty-gritty-of-advanced-indexing-in-pandas-9850b2d50220?source=collection_archive---------56-----------------------

为什么多级索引并不像看起来那么令人生畏

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

奥兹古·奥兹登Unsplash 拍摄的照片

我经常听到这句美丽的名言,

“有时我们看不到眼前的东西”

任何事情都是如此——无论是错误的原因还是错误本身,拼写错误的变量名,没有设置inplace = True,想知道为什么我的数据帧没有改变,等等。不仅仅是错误或打字错误,甚至是功能。我们不认为基本功能是产品的组成部分。熊猫的指数是一个经常被忽视但在数据分析和处理中起着关键作用的东西。

索引、序列和数据帧是 Pandas 的核心数据结构。索引是数据帧中某个位置的标识符或地址。而操作一维和二维索引是最常见的做法。学习处理任意数量的维度有时会非常方便。幸运的是,Pandas 使能够将高维数据使用和存储到一维数据结构(如序列)和二维数据结构(如数据帧)中。这就是众所周知的分级索引、高级索引或多级索引。

在这篇文章中,我将解释现实世界中多级索引的基本操作方法。

首先,我从 Kaggle 获得了这个数据集。数据集包含一些关于汽车的样本数据,如下图所示。

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

样本汽车数据

繁琐的道:

比方说,我想知道是否有丰田汽车的车身样式是轿车,燃料类型是柴油。

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

这是我们找到想要的结果的方法之一。如果我们想知道所有车身样式为旅行车的柴油汽车的品牌,并检查其价格是否在 20,000 美元以内,该怎么办?

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

我们仍然获得了想要的输出,但是代码并不整洁。随着查询变得越来越复杂,代码变得越来越单调和冗长。

高级索引或分层索引:

分级索引可以帮助我们处理任意数量的维度。它可以帮助我们过滤、聚合、组织和操作数据,进行真正强大的数据分析。

1)操纵索引:

让我们从为数据帧设置索引开始。为了创建多索引数据帧,我们向属性index传递一个值列表,而不是一个字符串。我的数据集中有如此多的分类特征,但我将首先考虑make,因为我想知道不同汽车组的详细信息。另外,为了更好的组织,我会考虑body_stylefuel_type

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

集合 _ 索引

让我们看一下索引,看看它们为我们带来了什么。

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

多级索引

多索引包含三个不同的属性,即级别、代码和名称。

  • levels属性指的是索引的标签名。索引从左开始排序。在这种情况下,make是最高的索引,其值为level = 0。也可以简称为level = 'make'
  • codes属性指的是编码级别的每个数据点的标签。
  • 属性包含了级别的字符串名称。

行和列都可以有多级索引。我们可以通过简单地将reset_index()函数传递给 DataFrame 来重置索引。

使用多级索引时需要注意的是,在尝试执行任何切片操作之前,先对索引进行排序。切片操作会故意抛出如下所示的警告。

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

未排序的多索引数据框的警告

我们可以通过将sort_index()函数传递给多索引数据帧来避免这种情况。此外,Pandas 依赖于被排序的索引来进行最优的检索和搜索操作。我们可以使用返回布尔值的index.is_lexsorted()函数来检查索引是否已经排序。

其他有用的功能有

  • swap_level()
  • 堆栈()
  • 拆分()

swap_level()可用于交换数据帧内的级别。它采用两个参数ij,这两个参数是作为字符串传递的级别名称。传递这个函数不会改变值的顺序。

stack()unstack()功能带有一个附加参数,您可以选择堆叠或不堆叠的层级,其中堆叠的层级成为新的最低层级(最右边)。

你可以继续检查官方的熊猫文档来获得更多的索引修改。

2)访问数据:

让我们看看是否可以使用我们的多索引数据框架找到任何车身类型为轿车、燃料类型为柴油的丰田汽车。

  • 使用.loc

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

。通信线路(LinesofCommunication)

.[loc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html)主要用于基于标签的选择,其中标签名称在元组内传递。有时它与pd.IndexSlice结合在一起。例如,查找所有柴油汽车。

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

警察。索引切片。通信线路(LinesofCommunication)

注意,在pd.IndexSlice内部,第一个:选择所有make的车厢,第二个:选择所有body_style的车厢,而外部的:选择所有列。

  • 使用.xs

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

。特小号

.[xs](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.xs.html)获取数据帧的特定横截面,其中轴参数默认为零axis=0,并获取一个level参数,该参数指示要使用的级别。

  • 使用.query

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

。询问

.[query](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.query.html)使用布尔表达式进行过滤或切片。注意查询中使用的andor超过了&|。这是因为查询方法中的表达式是使用[eval()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.eval.html#pandas.eval)函数动态计算的。此外,默认情况下,该方法使用修改后的 Python 语法。

  • 使用get_level_values

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

获取级别值

[get_level_values](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.get_level_values.html)对请求的级别使用标签值的向量。这里,级别值可以作为整数或字符串传递。

更一般地说,locxs用于基于标签的选择,而queryget_level_values有助于生成用于过滤的条件表达式。并非所有这些方法都同样有效,每种方法在不同的情况下效果最好。我们有责任选择最佳的检索方法!

3)汇总数据:

所有内置的数据聚合方法,如mean()min()max()sum()都可以应用于分层数据框架。它与level参数一起传递,该参数影响计算聚合的数据部分。一个简单的例子是找出所有大众汽车的平均价格。

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

平均价格

使用熊猫索引一直是有趣和好奇的!请随意查看我的 GitHub repo 这里的包含了文章中提到的所有代码。

感谢你一路阅读到这里。如果你有任何问题、反馈或批评,请在评论区告诉我。祝你今天开心!玩的开心!

自然语言处理与图形密切相关

原文:https://towardsdatascience.com/nlp-and-graphs-go-hand-in-hand-with-neo4j-and-apoc-e57f59f46845?source=collection_archive---------6-----------------------

了解如何使用 Neo4j 设置 NLP 管道并分析其结果

准备好,因为今天我们将角色扮演《黑客帝国》中的 Neo。在他得到启示的时刻之后,Neo 意识到,尽管这个世界看起来非常混乱无序,但在所有这些混乱背后隐藏着一个结构化的绿色代码。我们将利用他在混乱中寻找隐藏结构的知识,并将其应用于文本。一旦我们戴上 Neo4j 眼镜,起初可能看起来只是无组织的混乱,很快就会变得非常结构化,充满洞察力。

议程

  1. 设置 Neo4j 桌面
  2. 图形导入
  3. 文本分类
  4. 命名实体识别
  5. 情感分析
  6. 二部网络的单部投影
  7. 社区检测
  8. 数据丰富

设置 Neo4j 桌面环境

这是我们第一次在博客中使用 Neo4j 桌面。可以在 Neo4j 下载中心获得。这是一个 GUI 应用程序,它使本地 Neo4j 数据库的管理更加舒适和方便,因为它提供了只需几次点击就可以启动数据库实例等功能。由于这也可能是您第一次与它交互,我准备了一个简短的指南,介绍如何设置 Neo4j 环境并做好一切准备。

成功安装 Neo4j 桌面后,您应该能够在显示器上看到图形界面。我们将通过点击添加数据库按钮来创建一个新的数据库实例。

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

选择创建本地图形,设置密码,按创建。我们现在已经成功地实例化了一个新的数据库。下一步是向数据库实例添加插件。单击数据库实例右上角的三个点,并选择管理

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

转到插件选项卡,安装您需要的插件。对于这篇博文,你将需要 APOC图形数据科学插件。

附言:是的,GDS 图书馆现已推出 Neo4j 4.x 版本

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

APOC NLP 程序存储在一个单独的文件中,因此,我们必须手动安装它们。我们可以安装任何自定义插件,只需将其复制到插件文件夹。打开如下所示的插件文件夹,将 APOC NLP 依赖关系复制到那里。

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

APOC 静态值存储器

APOC 允许我们在静态值存储器中存储敏感信息,比如 JDBC 凭证或 API 密钥。可以把它看作是 Neo4j 的环境文件,我们在其中定义了以后可以用 cypher 轻松访问的变量。我们将把 GCP 和 AWS 凭证存储在静态值存储中。第一步是获取 API 凭证:

获得凭证后,我们继续在$Neo4j/conf文件夹中创建apoc.conf。将这三行存储在文件中。

apoc.static.aws.apiKey=$yourAwsApiKey
apoc.static.aws.apiSecret=$yourAwsApiSecret
apoc.static.gcp.apiKey=$yourGcpApiKey

我们现在可以启动 Neo4j 实例,并调查静态存储是否按照查询的预期工作。

RETURN apoc.static.getAll("aws") AS aws;

您应该会看到类似这样的内容:

美国焊接协会

{
  "apiKey": "$yourapiKey",
  "apiSecret": "$yourapiSecret"
}

如果到目前为止您已经成功地完成了所有步骤,那么您应该有一个添加了 APOC NLP 和 GDS 库的 Neo4j 实例在运行。如果您不想设置静态值存储,只需将您的云凭证直接复制并粘贴到查询中即可。

图形导入

我们将使用由 Kevin Toms 提供的新闻数据集。它包含大约 10000 篇新闻文章的内容。该数据集于 2019 年 1 月编制。不幸的是,作者没有提供关于数据集的来源和时间线的信息。在开始之前,我们必须下载数据集并将其复制到导入文件夹。您可以从 Neo4j 桌面访问导入文件夹,如下所示。

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

我们将每篇文章作为一个单独的节点导入,标题和内容作为节点的属性存储。

LOAD CSV WITH HEADERS FROM "file:///text_summarizer_data.csv" as row
CREATE (a:Article{title:row.title, content: row.content})

用谷歌进行文本分类

我们将从由谷歌的自然语言 API 支持的 APOC 文本分类程序开始我们的分析。它将输入文本分为不同的内容类别。据我所知,它对新闻文章最有效,但对虚构文学不太有效。谷歌每月提供 30,000 个免费分类请求,所以这足够启动一个爱好项目了。

由于我们需要处理 10.000 篇文章,我们将使用apoc.periodic.iterate 过程来处理批处理过程。在文档中了解更多信息。这个过程需要几分钟,所以在你等待的时候,请随意喝杯咖啡或者做你最喜欢的瑜伽姿势。

CALL apoc.periodic.iterate("
   // get all articles
   MATCH (node:Article) RETURN node
  ","
   // classify each article
   CALL apoc.nlp.gcp.classify.graph(node, {
       // we retrieve gcp api key from static value storage
       key: apoc.static.get('gcp.apiKey'),
       // node property that contains the text
       nodeProperty: 'content',
       write:true
    }) YIELD graph RETURN distinct 'done'",
    {batchSize:10})

当你啜饮美味的咖啡时,我们可以看看分类的结果。让我们从查看一些类别名称的例子开始,这样我们就知道我们在处理什么了。

MATCH (n:Category)
RETURN n.name as category
LIMIT 5

结果

类别名称的深度在一至三层之间。级别由斜杠字符(/)分隔。比如*“/新闻"只包含一级,而”/金融/保险"*包含两级类别。我们将提取每个分类名称的顶级类别,并将其存储回我们的图中。这将使我们能够更直接地根据顶级类别进行筛选和分组。新的图表模式将如下所示:

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

在 Neo4j 中处理层次树时,我学到了一些规则,可以帮助我们简化和优化查询。其中之一是我们应该在整个树中只有一个单一的关系类型。这样,我们可以很容易地查询一个或两个层次的深度,正如我们将看到的。

让我们为顶级类别节点定义一个惟一的约束。

CREATE CONSTRAINT ON (t:TopCategory) ASSERT t.name IS UNIQUE;

我们可以通过拆分分类名称来提取和存储顶级类别。

MATCH (n:Category)
WITH n, split(n.name,'/')[1] as top_level
MERGE (t:TopCategory{name:top_level})
MERGE (n)-[:CATEGORY]->(t)

让我们通过顶级类别来查看文章的数量。您可以观察到,由于我们在类别树中只处理一种关系类型,所以遍历两个层次非常简单。

MATCH (t:TopCategory)
RETURN t.name as category, 
       size((t)<-[:CATEGORY*2..2]-()) as number_of_articles
ORDER BY number_of_articles DESC LIMIT 10

结果

需要注意的一点是,查询结果显示的文章比图表中实际显示的要多。这是因为有些文章有不止一个分类。我们将通过查看来自科学类别的两篇文章的内容来快速检查结果。

MATCH (t:TopCategory{name:"Science"})<-[:CATEGORY*2..2]-(article)
RETURN article.content as text LIMIT 2

结果

两篇文章似乎都非常关注科学和技术。我们并没有对结果进行深入的分析,但是我们会假设自然语言 API 很好地完成了对新闻文章进行分类的工作。

命名实体识别

本质上,NER 是一个识别文本中各种实体并将它们按类别(如人员、组织、位置等)分组的过程。我们寻找的实体类型完全取决于用例。有时我们想知道在给定的文章中提到了哪些人和组织,而其他时候我们可能更有兴趣知道提到了哪些基因和蛋白质。有很多预先训练好的模型可供你使用,如果没有一个适合你,你可以训练你自己的 NER 模型,但是这超出了这篇博文的范围。让我们看一个在上面的科学文章中找到的命名实体的例子。

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

显示可视化

我们可以观察到,该模型善于发现文本中的人物和组织,但并不完美。我们真的不能怪它给“Kibble”贴上组织的标签。该模型被训练来识别新闻文章中的人和组织,而不是真正被设计来阅读科学文章。AWS 和 GCP 都在其云 API 组合中提供了自己的 NER 模型。我们将在两个随机的故事中比较它们,并选择更适合我们的用例。

MATCH (node:Article)
WITH node LIMIT 2
RETURN node.content as text

结果

NER 和 GCP

我们将从使用 Google 的自然语言 API 识别命名实体开始。有一个专用的 APOC 过程apoc.nlp.gcp.entities.*,这使得与 API 的交互非常容易。该程序有两种不同的模式:

  • 串流:串流结果
  • 图形:创建一个虚拟图形,并选择性地存储结果

我们将使用程序的图形模式。如上所述,图形模式创建了一个虚拟图形,我们可以用 Neo4j 浏览器来可视化它。通过设置参数write:false,我们不会将结果存储回 Neo4j。

MATCH (node:Article)
WITH node LIMIT 2
CALL apoc.nlp.gcp.entities.graph(node, {
    // Your api key
    key: apoc.static.get('gcp.apiKey'),
    // Property of the node that
    // contains the text
    nodeProperty: 'content',
    // do not store results in graph
    write:false
}) YIELD graph as g
RETURN g

结果

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

实体类型:

  • 绿色:人
  • 蓝色:数字
  • 橙色:位置
  • 红色:其他
  • 黄色:事件

GCP 发现了五种不同类型的实体。如果我们看看实体类型的人,我们会注意到,在“艾琳·麦肯”之上,GCP 也把“母亲”和“女人”这样的词作为一个人。有趣的是,它将“搜索”识别为一个事件。在我看来,GCP 试图给尽可能多的词贴上标签,这可能是一件好事,也可能是一件坏事,取决于用例。

带 AWS 的 NER

与 GCP 类似,我们也有专门的 APOC 程序apoc.nlp.aws.entities.*用于 AWS NER 请求。

MATCH (n:Article)
WITH n LIMIT 2
CALL apoc.nlp.aws.entities.graph(n, {
    // AWS api key
    key: apoc.static.get('aws.apiKey'),
    // AWS api secret
    secret: apoc.static.get('aws.apiSecret'),
    // Property of the node
    // that contains the text
    nodeProperty: 'content',
    write:false
}) YIELD graph as g
RETURN g

结果

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

实体类型:

  • 绿色:人
  • 布朗:组织
  • 橙色:日期
  • 红色:位置
  • 黄色:数量
  • 蓝色:商业项目

乍一看,我们可以观察到 AWS 找到的实体比 GCP 少。它也不包括人实体下的“女人”和“母亲”这样的词。我的观点是,如果你试图做某种主题建模,那么 GCP 可能更适合,而对于创建知识图,AWS 可能更好。我们将使用 AWS 来分析和提取数据库中所有文章的实体。因为我们需要批处理我们的 NER 过程,我们将使用apoc.periodic.iterate过程。

CALL apoc.periodic.iterate("
    MATCH (n:Article)
    WITH collect(n) as total
    // Create a batch of 25 articles
    CALL apoc.coll.partition(total,25) 
    YIELD value as nodes
    RETURN nodes
    ","
    CALL apoc.nlp.aws.entities.graph(nodes, {
       key: apoc.static.get('aws.apiKey'),
       secret: apoc.static.get('aws.apiSecret'),
       nodeProperty: 'content',
       relationshipType: 'AWS_ENTITY',
       // store the results to Neo4j
       write:true
    }) YIELD graph
     RETURN distinct 'done'", {batchSize:1})

如果你抓紧时间,在 NER 结束之前,你还有时间做另一个伟大的瑜伽姿势。让我知道进展如何。好了,我们现在可以检查 NER 进程的结果了。我们将从调查文章中提到最多的人开始。

MATCH (n:PERSON) 
RETURN n.text as person, 
       size((n)<-[:AWS_ENTITY]-()) as mentions
ORDER BY mentions DESC LIMIT 10

结果

我们选择 AWS 是因为它不包括 person type 下的“orphan”或“genealogist”这样的词。我想没有一个模型是完美的,因为我们可以看到“项目经理”、“首席执行官”和“总裁”是作为人实体出现的。解决这个问题的一个方法是添加共指解析作为 NER 管道的一个步骤,但是由于我们正在处理第三方 NLP 管道,我们没有这个闲心。现在让我们看看体育文章中提到最多的人。

MATCH (n:PERSON) 
RETURN n.text as person,  
    size((n)<-[:AWS_ENTITY]-()-[:CATEGORY*2..2]->({name:'Sports'})) as mentions
ORDER BY mentions DESC LIMIT 10

结果

所有十大提及的体育名人都是印度板球运动员。Virat Kohli 确实很突出,所以我猜他是队长。这种 NER 提取法的一个弱点是 Virat Kohli 和 Kohli 被视为两个独立的实体。这可以解释,他有时被提到全名,其他时候只提到姓。另一个有趣的用例是查看哪些位置和事件共享了最多的文章。

MATCH (a:Article)-[:AWS_ENTITY]->(event:EVENT),
      (a)-[:AWS_ENTITY]->(location:LOCATION)
RETURN location.text as location,
       event.text as event,
       count(*) as mentions
ORDER BY mentions DESC
LIMIT 10

结果

结果看起来相当不错。看起来像 CES 2019 在拉斯维加斯举行,联合印度在加尔各答举行集会,20 国集团在阿根廷举行论坛,世界经济论坛在达沃斯举行。除此之外,英国和欧盟还在讨论英国退出欧盟问题。

情感分析

AWS 还在其理解 API 包中提供情绪分析。它将文本分为四个不同的情感组:

  • 积极的
  • 中立的
  • 否定的;消极的;负面的;负的
  • 混合的

我们将对所有文章进行情感分析。我认为新闻应该或多或少是中性的,除了体育。有一个特殊的 APOC 程序apoc.nlp.aws.sentiment.*使用与 NER 处理程序相同的两种模式(流&图)处理 AWS 情绪 API 请求。

CALL apoc.periodic.iterate("
    MATCH (n:Article)
    WITH collect(n) as total
    CALL apoc.coll.partition(total,25) 
    YIELD value as nodes
    RETURN nodes
    ","
    CALL apoc.nlp.aws.sentiment.graph(nodes, {
       key: apoc.static.get('aws.apiKey'),
       secret: apoc.static.get('aws.apiSecret'),
       nodeProperty: 'content',
       write:true
    }) YIELD graph
     RETURN distinct 'done'", {batchSize:1})

我们将看看体育文章中被 AWS 理解为积极的人和事件的共同提及。

MATCH (a:Article)
WHERE a.sentiment = 'Positive' AND 
      (a)-[:CATEGORY*2..2]->({name:'Sports'})
MATCH (a)-[:AWS_ENTITY]->(person:PERSON),
      (a)-[:AWS_ENTITY]->(event:EVENT)
RETURN person.text as person,
       event.text as event,
       count(*) as mentions
ORDER BY mentions DESC LIMIT 10

结果

看起来网球运动员正在赢得网球比赛。由 Virat Kohli 担任队长的印度板球队赢得了 2011 年世界杯和墨尔本测试赛。从结果来看,我大概是这么想的。

图形数据科学图书馆

如果你读过我的博客文章,你就会知道我喜欢写关于图形数据科学库的文章。这个博客也不例外。首先,我们将借助节点相似性算法将一个二分网络投影到一个一分网络。在下一步中,我们将使用 Louvain 算法在投影的单分图中搜索社区。

二部网络的单部投影

二分网络只是一种奇特的说法,即图包含两组不同的节点,同样,一个一分网络只包含一组节点。在我们的例子中,我们将从一个包含文章和 NER 实体的双向网络开始。下一步,我们将借助 GDS 的相似性算法,把它投射到一个单片式网络上。这里有一个图表,显示了幕后实际发生的事情。

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

我们可以把单向投射看作是把间接关系转化为直接关系的过程。相似性算法之间的区别只是用于计算相似性得分或权重的度量。例如,节点相似性算法使用 Jaccard 相似性分数。它被定义为交集的大小除以并集的大小。如果我们愿意,我们也可以投射一个由文章而不是人组成的单部分网络,并检查相似的文章是如何基于其中提到的实体的。

在我们运行任何算法之前,让我们快速回顾一下 GDS 图书馆是如何工作的。

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

这个形象是善意地借用了 官方文件

图表分析管道由三个主要部分组成。在第一部分中,图形加载器从 Neo4j 中读取存储的图形,并将其作为内存中的投影图形加载。我们可以使用本机投影cypher 投影来加载投影图。第二步,我们按顺序执行图算法。我们可以使用一个图算法的结果作为另一个图算法的输入。最后但同样重要的是,我们将结果存储或流回 Neo4j。

我们将使用 cypher 投影来加载内存中的图形。如果你需要快速复习一下它是如何工作的,我建议你看一下官方文档。在 node 语句中,我们将描述顶级新闻类别中的所有文章以及所有 person 实体。在关系陈述中,我们将描述新闻文章和人实体之间的所有链接。

CALL gds.graph.create.cypher("person_similarity",
 // match articles that are in the 'News' category
 "MATCH (a:Article) WHERE (a)-[:CATEGORY*2..2]->({name:'News'})
  RETURN id(a) as id, labels(a) as labels
  UNION
  // match all person entities
  MATCH (p:PERSON) RETURN id(p) as id, labels(p) as labels
  ","
  // match all links between news articles and person entities
  MATCH (a:Article)-[r:AWS_ENTITY]->(p:PERSON) 
  WHERE (a)-[:CATEGORY*2..2]->({name:'News'})
  RETURN id(p) as source, id(a) as target, type(r) as type")

下一步是在节点相似性算法的帮助下推断人实体之间的相似性网络。我们可以用以下三个参数来影响相似性网络的稀疏或密集程度:

  • DegreeCutoff:算法必须考虑的节点的最少链接数
  • similarityCutoff:仍被视为相似的一对节点之间的最小相似性得分
  • topK:对每个节点相似关系数量的限制

通常,我们很少或根本不知道图会有多稀疏。我们绝对不想以一个完整的图结束。我们可以用算法的stats模式来评价相似度网络。这样,在对上述三个参数进行微调之前,我们不会存储或改变任何结果。

CALL gds.nodeSimilarity.stats('person_similarity')
YIELD nodesCompared, similarityDistribution
RETURN nodesCompared as nodes,
       apoc.math.round(similarityDistribution.min,2) as min, 
       apoc.math.round(similarityDistribution.p25,2) as p25, 
       apoc.math.round(similarityDistribution.p50,2) as p50,
       apoc.math.round(similarityDistribution.p75,2) as p75,
       apoc.math.round(similarityDistribution.p90,2) as p90,
       apoc.math.round(similarityDistribution.mean,2) as mean

结果

乍一看,节点似乎不是很相似。这很好。我们想推断一个稀疏网络,因为社区检测算法在非常密集的图上表现不佳。确定最佳的相似性参数值需要艺术和科学的结合,但是有了一些经验,你会变得很擅长。我们将把similarityCutoff设置为 0.6,同时将degreeCutofftopK参数保留为默认值。使用mutate模式,我们将算法的结果存储回内存中的投影图。

CALL gds.nodeSimilarity.mutate('person_similarity', 
    {degreeCutoff:1, similarityCutoff:0.6, topK:10,
     mutateRelationshipType: 'SIMILAR', mutateProperty: 'score'})

社区检测

社区检测算法旨在帮助我们理解复杂网络的结构。最明显的应用是在社交网络中寻找朋友群体。我们认为社区是一组紧密相连的节点,类似于一群朋友是如何高度互联的。让我们看看一些社区结构的可视化,以获得更好的理解。

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

这是我几年前做的一个可视化,当时我正在分析漫威宇宙中的社区结构。节点的颜色表示组从属关系。我们可以观察到,社区由高度互连的节点组成,这些节点与其他集群的链接较少。在这篇博文中,我们将使用 Louvain 算法来检查我们推断的相似性网络的社区结构。

CALL gds.louvain.stream('person_similarity', 
    {nodeLabels:['PERSON'], relationshipTypes:['SIMILAR']})
YIELD nodeId, communityId
RETURN communityId, 
        collect(gds.util.asNode(nodeId).text) as members
ORDER BY size(members) DESC LIMIT 5

结果

发现的社区相对较小。这是similarityCutoff参数值的直接结果。如果我们选择一个较低的阈值,更多的节点将被认为是相似的,我们可能会得到更大的社区。同样,如果我们增加数据集的规模,多处理几千篇文章,我们也会得到更大的社区。也就是说,是时候进入数据丰富的章节,为我们的实体添加一些上下文了。

数据丰富

我们在图表中发现了几个小社区。有些人相当有名,不难找到社区成员之间的共同点。例如,我知道哈里森·福特、哈莉·贝瑞和吉姆·帕森斯都是演员,所以我会假设其他人也是演员。我们可以去谷歌一下。一个更好的想法是,使用外部数据提供者,如 Google Knowledge GraphWikiData ,有计划地丰富我们图表中的实体。这正是我们下一步要做的。

我们将只充实在前一个查询中返回的人员(前五个最大社区的成员)。为了避免多次运行社区检测,我们将首先把 Louvain 算法的结果存储回 Neo4j。

CALL gds.louvain.write('person_similarity', 
    {nodeLabels:['PERSON'], relationshipTypes:['SIMILAR'], 
     writeProperty:'louvain'})

谷歌知识图

很久以前我已经在我的一篇博客文章中使用了谷歌知识图 API。我写作的原因之一是我有一个有用的 cypher 查询库,我可以从中复制粘贴并在以后的分析中使用。

我们将使用知识图 API 通过描述和详细描述属性来丰富最大社区的成员。

// get the members of the top five communities
MATCH (p:PERSON)
WITH p.louvain as communityId, collect(p) as members 
ORDER BY size(members) DESC LIMIT 5
UNWIND members as member
WITH member, apoc.text.urlencode(member.text) as name, 
     apoc.static.get('gcp.apiKey') as key
// send a request to KG API
CALL apoc.load.json("https://kgsearch.googleapis.com/v1/entities:search?query=" + 
     name + "&key=" + key + "&limit=1&indent=True")  YIELD value
WITH member, value['itemListElement'][0]['result'] as results
// store results back to Neo4j
SET member.kg_description = results.description,
    member.kg_detailed_description = results.detailedDescription.articleBody

现在让我们来看看最大的五个人群,包括他们的知识图表描述。

MATCH (p:PERSON)
RETURN p.louvain as communityId, 
       collect(p.text) as members,
       collect(DISTINCT p.kg_description) as description 
ORDER BY size(members) DESC LIMIT 5

结果

随着丰富描述的增加,我们对谁是社区成员有了更好的认识。我们可以观察到最大的集群由网球运动员组成。知识图表没有识别第二大组的任何成员。然而,它知道“乌鲁比亚”实际上是印度的一个城市,而不是一个人。

维基数据浓缩

我订阅每周一期的 twin4j 开发者简讯的主要原因是我可以从其他人那里复制粘贴密码查询。马克·李约瑟做了一个关于如何用密码查询维基数据的系列。他做了所有的艰苦工作,所以我们可以通过复制粘贴他的密码查询来享受他的劳动成果。他还开发了我们刚刚使用的 APOC NLP 程序,这是对 Mark 的称赞!

对于那些以前从未听说过 Wikidata 的人来说,他们将自己描述为一个自由开放的知识库,可以被人类和机器阅读和编辑。检查了一下之后,我可以说它的信息非常丰富,绝对值得一看。我们将向最大社区的成员添加出生日期、职业和国籍信息。查看维基数据查询服务了解更多关于数据丰富选项的细节。

CALL apoc.periodic.iterate(
  // get all persons from the biggest five communities
  "MATCH (p:PERSON) 
   WITH p.louvain as communityId, 
          collect(p) as members 
          ORDER BY size(members) DESC LIMIT 5
   UNWIND members as member
   RETURN member
   ","
    // prepare a sparql query
    WITH 'SELECT *
    WHERE { ?person
                  rdfs:label \"' + member.text + '\"@en ;
                  wdt:P569 ?dateOfBirth ;                
                  wdt:P106 [ rdfs:label ?occupation ] ;
                  wdt:P27 [ rdfs:label ?countryName ] .
           filter(lang(?countryName) = \"en\" && lang(?occupation) = \"en\")
           }' AS sparql, member
    // make a request to wikidata
    CALL apoc.load.jsonParams(
      \"https://query.wikidata.org/sparql?query=\" + apoc.text.urlencode(sparql),
      { Accept: \"application/sparql-results+json\"},
      null
    )
    YIELD value CALL apoc.do.when(
      // if there are any results
      size(value.results.bindings) > 0,
      // store results
      'WITH value.results.bindings[0] AS result, member
       SET member.dateOfBirth = date(datetime(result.dateOfBirth.value)),
           member.wikidataImportDone = true
       // store nationality
       MERGE (c:Country {name: result.countryName.value })
       MERGE (member)-[:NATIONALITY]->(c)
       // store occupation
       MERGE (o:Occupation {name: result.occupation.value})
       MERGE (member)-[:HAS_OCCUPATION]->(o)
       RETURN member',
      // else if no results 
      'SET member.wikidataImportDone = true RETURN member',
      {value: value, member: member})
    YIELD value AS result
    RETURN count(*)",
  {batchSize: 20})

现在让我们来看看最大的社区,包括我们从维基数据获得的额外信息。

MATCH (p:PERSON) 
RETURN p.louvain as communityId,
       collect(p.text) as members,
       apoc.math.round(avg(duration.inMonths(p.dateOfBirth, date()).years),2) as average_age,
       collect(distinct [(p)-[:NATIONALITY]->(n) | n.name][0]) as nationalities,
       collect(distinct [(p)-[:HAS_OCCUPATION]->(o) | o.name][0]) as occupations
ORDER BY size(members) DESC LIMIT 5

结果

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

结果看起来令人印象深刻。我打赌你不知道至少有两个人叫吉姆·帕森斯。其中一个是演员,另一个是赛车手。我们只是触及了维基数据 API 提供的信息的表面。

结论

如果你还在这里,我向你致敬。这是我迄今为止最长的一篇博文。尽管有时感觉就像我在写一本迷你书或我那本不存在的书的一个章节,但我很高兴,因为我认为它真的很好,我在这个过程中学到了很多。我希望你也是。如果你喜欢它,并希望我写更长的帖子,请让我知道。同时,你可以下载 Neo4j 并开始开发你自己的 NLP 管道。别忘了订阅 Twin4j 简讯

代码可在 GitHub 上获得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值