TowardsDataScience-博客中文翻译-2020-一百二十四-

TowardsDataScience 博客中文翻译 2020(一百二十四)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

使用数据科学来了解和应对冠状病毒和其他流行病。

原文:https://towardsdatascience.com/using-data-science-to-understand-and-attack-the-coronavirus-and-other-epidemics-41019c6b0c86?source=collection_archive---------28-----------------------

流行病危机的理解、行动和预防策略。

在 2019 年的最后一天,中华人民共和国政府向国际卫生当局通报了中国武汉市的一系列不明疾病病例。七天后,中国当局确认该病毒是一种属于普通流感家族的冠状病毒,SARS 和 MERS。国际科学界将这种病毒命名为新冠肺炎,根据流行病学标准,这种病毒已经从流行病变成了疫情,因为迄今为止它已经在全球传播到了 75 个国家。

截至 2020 年 3 月 4 日,已有 3062 人死于该病毒(最后一天为 162 人),报告的病例总数达 92860 多例,其中大部分来自 mainland China ( 原始数据)。

世界卫生组织(世卫组织)对这种病毒的一些症状描述如下:

临床症状和体征主要是发热,在某些情况下,呼吸困难和胸部 x 光检查可见双肺浸润性肺炎。

目前已知该病毒的致死率没有其他类似疫情高(约 2%的感染以死亡告终,类似 SARS 和 MERS 的病毒约为 60%)。众所周知,传播的容易程度相对较高,病毒通常需要大约 10 到 15 天才能无症状地潜伏。正因如此,被感染的人往往会在这段时间内传染他人,而自己并不知道自己生病了。这可能会给卫生系统带来风险,因为没有足够的医院床位来处理大量受感染的病人。

矛盾的是,这种病毒的低致死率恰恰是它如此危险的原因,因为无法遏制它有可能大规模传播传染病,并且它的“低致死率”比近几十年来任何类似的病毒都要夺去更多的生命。

然而,在全球化时代,这种疾病和其他呼吸系统疾病爆发的大规模性质表明了大规模的战斗战略,其中协调和信息交流成为理解、遏制并最终解决流行病的基本支柱。

这就是数据科学发挥作用的地方;被描述为大规模计算机数据分析工具,以及人工智能(AI)和机器学习(ML) 技术,以更强的理解能力处理这种规模的问题。

在 2020 年中期,我们离不开这些技术,在这一层面,成功战略的决策和规划必须由数据驱动

数据科学有大量的方法,其有效性由统计的严格性来保证,然而,由于该领域的持续发展,以及数据科学家参与的不断创新过程,它们的实施可能变得更像一门艺术而不是科学:没有完美的配方。

在本文中, deep_dive 的团队试图提出一些数据科学的想法和应用,这些想法和应用可能会通过以下工作流程对正确处理这场危机产生影响:

  1. 理解这一现象
  2. 行动
  3. 预防

在第一阶段,我们将通过提取尽可能多的关于病毒的信息,并使用 GIS(地理信息系统)技术和图形分析从数据可视化中理解病毒,来寻求理解这种现象。这些信息将作为行动阶段中模型的训练和部署的基本输入,在此阶段,我们提出了使用时空聚类、基因组数据和一些风险模型的具体应用。最后,在预防阶段,我们将探索数据架构问题,为解决未来类似性质的问题打下更坚实的基础。

理解这一现象

理解流行病学现象的一个关键问题是正确诊断发现该现象的阶段。

疫情在哪里?传染是如何发生的?有多少人生病了?传播有多快?有多少人处于危险中,谁最容易受到影响?诊断最好的方法是什么?

让我们试着用一个典型的工作流来回答这些问题,这个工作流从探索性分析开始。

这一阶段包括提取原始数据可能提供给我们的信息,我们提出以下列表作为起点:

  • 数据可视化。
  • 地理热点(GIS)
  • 网络分析。

数据可视化。

数据可视化是提取现象潜在模式的最有效的工具之一;在分析的初始阶段,正确地查看数据允许我们生成第一个假设。

当谈到一种流行病时,看到图表的用处会立即浮现在我们脑海中,这些图表可以让我们了解病毒的传播和致命性中的时空演变。

自 2010 年 4 月 3 日起新冠肺炎病例的演变

这一时间序列可以直观地显示所报告的病毒病例的全球演变情况。我们可以通过二次模型的拟合来想象数据的趋势。

正如我们所见,二次模型似乎不是拟合数据的最佳方式;然而,这种模型的动画可以让我们更好地理解问题的维度及其潜在规模。

新的每日感染呈线性增长(总是恒定)的模型意味着总感染是二次的。

通常情况下,流行病学中使用的模型具有指数增长,其发展可以很快超过像这样的二次模型。

地理热点

众所周知,该疾病的首次爆发发生在中国,这就是为什么我们的地理分析将分为两个区域:中国和世界其他地区。首先,让我们想象一下每个地区确诊病例的演变情况。

很明显,中国是疫情的焦点,然而,鉴于全球化世界的相互关联性,在世界其他地区出现同等规模的疫情是可行的。此外,世界其他地区 3 月 3 日确诊的新增病例总数似乎超过了中国。让我们更详细地看一下每个地区:

武汉市位于中国中部的湖北地区,是迄今为止发现确诊病例最多的地方。

目前,中国有 80304 例报告病例,因此在下图中,病例数对应的颜色超出了建议的比例。

这张地图令人担忧的是跨境感染的增长率和各类国家新疫情的出现。截至 3 月 4 日,已有 75 个国家确诊病例。

现在让我们看看中国和世界其他地方报道的死亡案例。

截至 3 月 3 日,已有 3112 人死于该病毒。

显然,来自新冠肺炎的最大数量的死亡发生在病毒感染源附近,这再次将中国置于全球比较范围之外,在 3112 例死亡中有 2946 例。

探究意大利、伊朗和韩国的案例很有趣,这些国家的致死率似乎高于其他国家:一个有趣的假设可能是发现传染病例的有效性很低,低估了目前传染病例的数量。

网络分析。

鉴于病毒携带者(人类和一些动物)的离散性,从网络角度研究传播和传染是很方便的。在这些网络中,每个节点意味着一个人,而(节点间的连接)意味着传染。鉴于传染病是随时间变化的进化过程,这种现象的建模变得极其复杂。

为了更好地理解这个案例,让我们把我们的分析从全球蔓延抽象到本地,例如,在墨西哥城地铁(CDMX)设施内。

假设一节地铁车厢 2 米* 40 米,每节车厢有 7 节。这产生了一个 560 𝑚的空间,传染可能发生。如果车满了,让我们假设每个𝑚有 9 个人,交互的数量会变得巨大。

𝑚的相互作用总数是 C(9,2)=36 的二项式系数,即 36 个相互作用。现在让我们假设在中心的人被感染了,并假设给定人口密度的感染率,然后我们可以模拟感染。

例如,假设在 15 分钟的互动中,与感染者一起乘坐地铁的每个人都有 30%的机会被感染。根据这些参数,在大约 2 小时内,8 名邻近乘客将已经被感染,这也是假设新感染者不会立即感染其他人(重要的是要注意,这 30%是一个说明性的例子,巨大的流行病学努力必须集中在尽可能估计这些比率上,在此链接中您可以找到更多信息)。

下图让您看到在几次模拟后,这个平均值如何收敛到 120 秒。

很明显,这些参数不能轻易假设,每个参数都必须以尽可能好的方式进行估计。基于这种简单推理的更复杂的模型可以在下面的链接中更详细地查阅。

行动

对这些现象采取行动自然是最复杂的部分。在这一阶段,所阐述的解决方案涉及大规模的后勤和行动部署、巨大的资源支出和非凡的政治能力,更不用说适当的规程、训练有素的卫生专业人员和深思熟虑的解决方案。如果不知道我们面临的是什么,不知道提议的解决方案是否是根据探索阶段发现的相关方面设计的,所有这些都不可能正确执行。

模型部署中的可扩展性变得至关重要,因为一个对大规模现象(如流行病)做出响应的系统必须能够处理高水平的并发,而不会在任何时候损害响应的有效性速度

本节概述了医学流行病学应用的可能模型部署,以及每个模型必须考虑的事项。

基因组展示。

由于医学科学的进步,冠状病毒的基因组序列很快被确定,现在是开源的。下图是测序的 DNA 的前 100 个含氮碱基的图片:

从这个视觉序列中,我们可以确认一些生物学的基本知识,例如任何生物的 DNA 都是只有四个含氮碱基(A、C、G、T)的序列。

完整的图像可以从完整的原始信息以及其他相关的基因组信息发展而来,这些信息可以在这里找到。

这个数字可能在美学上很吸引人,然而,很少有见解能为我们对这一现象的分析增加价值。

从这个庞大的序列中提取信息并不是本文的重点,然而,使用主成分分析(PCA)等技术或其他维度缩减技术,我们可以帮助实现不同病毒株之间的相似性度量,这可以指导我们创建有效的疫苗。另一方面,遗传树让我们能够可视化突变的进化。冠状病毒的进化可以在这里查阅

时空聚类

除了识别热点之外,另一种时空检测方法是基线的使用。这些让我们能够根据历史信息确定我们在正常范围内会考虑什么。通过这种方式,我们可以有效地检测对典型或历史行为的异常感染的聚集(集群)。

作为该技术的应用示例,我们将使用 2018 年明尼苏达州接种疫苗的学生人数的公共数据集。

我们发现该地区(集群)接种疫苗的幼儿园人数(约 85%)明显低于该州其他地区的百分比(95%)。值得注意的是,检测到的组并不对应于明尼阿波利斯市人口密度最高的区域,因为所实施的算法使用了考虑到人口的基线,因此我们可以检测地理上对于形成有效策略不明显的情况。

2018-2019 学年明尼苏达州接种疫苗的学生人数的公共数据集。

这种分析方法可用于多种流行病学应用。一方面,我们可以发现病例数明显高于其他地区的地区。类似地,如果新冠肺炎冠状病毒疫苗可用,我们可以使用该数据集来识别高风险区域。

从这里,我们将冠状病毒感染和疫苗接种率相关联,以便在感染开始不受控制地传播之前,更好地将资源分配给传播感染的最高风险地区,避免这样做可能是的疏忽。****

风险模型

流行病风险模型可以预测某种疾病的感染演变,以及这将如何影响所涉及的其他参与者。

这些模型非常复杂,因为它们依赖于许多特定于每种病毒环境的相互作用。冠状病毒的规模使得风险模型的设计成为一项强制性任务,因为只有这样才能衡量所涉及的成本:确定城市宵禁、关闭市场或拥挤的空间、移民和机场措施等。

有了足够的航班信息,就可以估计来自被归类为“风险”区域的客流,并在此基础上对采取强制性预防措施进行成本效益分析,例如对每个到达的乘客进行诊断,并在检测到的情况下启动健康协议,以避免通过这种方式传染。

只有跟踪在一些国家检测到的感染趋势,以及可用的医疗护理能力,才有可能估算出达到饱和的距离。

以意大利为例,该国的医疗保健能力目前为每 1000 名居民 3.4 张病床。穿越这个国家冠状病毒病例的趋势,我们观察以下模型。

****

值得一提的是,最符合流行病学趋势的模型不是多项式模型,而是指数模型。也有适合当地特点的模型来调查地区发展。

考虑到对病例发现趋势和可用床位数量的调整(考虑到之前没有工作),在 36 至 49 天内,意大利医院将没有一张床位可用于治疗新冠肺炎患者。重要的是要考虑到该模型认为所有患者都患有严重疾病,因此需要床位,这不会在包含更多变量的更专业的模型中减少一点趋势。获得一个世界或地区地图来可视化饱和的距离和一些其他风险地图是一个悬而未决的任务,应该很快进行。在这个环节,有一些传播图的例子。

如果我们将这些模型用于全球战略,我们将不得不考虑大规模模型的部署。只有通过像 Google Cloud、Amazon Web Services 或任何其他形式的互联网托管这样的云服务,才能完成如此重大的任务。每个模型都是不同的,因为其不同的体系结构和功能最适合每种情况,并且取决于每个模型的计算、内存和可用性要求。

预防

如果我们能有效地控制疫情,我们就不能停下来。了解最新的最佳实践非常重要,因为只有这样,我们才能为这种性质的危机做好准备。本节讨论数据科学的一个非常重要的部分,它与数据基础设施有关,在数据工程中比在数据分析中更常见。

卫生部门的隐私

保护使用 ML 模型的个人隐私是一个相关的伦理问题,尤其是在使用的数据可能包含敏感信息的情况下,如卫生部门。

我们感谢约翰·霍普金斯大学提供关于这一发展的每日更新,可以在他们的 github 知识库中查阅。

此外,这一主题在未来可能会变得越来越与使用个人数据的组织相关,因为他们将必须遵守保护此类数据流通的新法规,例如通用数据保护法规(GDPR)。

幸运的是,今天已经开发出了(并且仍在开发中)工具,为这种问题提供了技术解决方案,这在本质上是相当法律和政治的。这方面最著名的工具是联合学习、差分隐私和安全多方计算(SMPC)** 。总之,这些工具可以为开发预测模型提供一个框架,同时对数据和模型进行加密,从而保护用户隐私。**

例如,联合学习中的一个范式转变意味着学习不再是集中式的(左),而是分布式的(右)。也就是说,模型移动到数据所在的地方,而不是相反。****

****

集中式和分布式学习

想象一下,开发了一种工具,它可以成功地使用生物数据、地理定位和搜索引擎查询(如 Google)的组合来预测流行病的动态。在这种情况下,这些数据可能被用来对用户、他们的日常习惯以及他们的健康状况进行敏感的观察,而这些观察可能会被用于不利的方面。否则,用户可能甚至不愿意提供这些数据,从而使开发这样一个模型变得困难。这就造成了一个道德困境:能够对流行病做出预测会带来直接的社会利益,但这意味着个人隐私会被削弱。这里提到的工具可以促进有效模型的开发,从而克服这一困境。

要了解这个主题的更多信息,我们建议研究一下 OpenMinedDropout Labs 的工作。

结论

尽管国际科学界的反应比以往任何时候都更加敏捷,关于该病毒的信息已在各研究中心共享,并且国际卫生当局已在足够大的范围内及时启动了相关协议,该病毒尚未得到遏制,预计短期内也不会得到遏制。****

认为这种病毒的低死亡率是不保持最严格措施的理由将是一个严重的错误。看看冠状病毒的传播模式,很容易想象如何在几周内整个国家的卫生系统可能完全崩溃,进一步危及容易死于这种病毒的人,从而每天增加死于新冠肺炎的人数。

活跃在人工智能模型开发领域的科学界(deep _ dive 的团队是其中的一员)的贡献,提供了有价值的分析和预测方法来缓解这种和其他性质的危机。

我们感谢所有参与这项工作的人,以及那些合作完成这篇文章的人。

曼努埃尔·阿拉贡内斯杰罗姆·阿兰达哈维尔·科尔斯卡蜜拉·布朗内斯阿图罗·马尔克斯 和杰罗姆·马蒂****

利用数据科学挖掘二战的新故事

原文:https://towardsdatascience.com/using-data-science-to-unearth-new-stories-of-wwii-20f968699a55?source=collection_archive---------51-----------------------

胜利日 75 年后,利用大数据、分析和数据可视化照亮过去

1945 年 5 月 8 日,经过五年旷日持久的战斗,第二次世界大战在欧洲结束。

自那以后的 75 年里,关于二战的话题几乎没有被提及。这场战争的大方向已经被反复推敲。

然而,在数据科学和历史的交汇处,有可能发掘出直到今天才被讲述的二战新历史。

两年前,我想出了一个主意,建立一个交互式仪表板,使用大数据讲述二战期间陆军航空队战斗群的故事。

华莱士·特鲁斯洛

仪表板后面是我自己的家庭故事:我的祖父沃利在欧洲的 B-24 机组人员中服役至死。他一生都在与创伤后应激障碍作斗争,这让他的战时服役对他生活中的每个人来说都是一个谜。因为我的职业是数据科学家,所以在沃利去世 20 年后,我转向数据来讲述他的故事。

当我开始研究沃利在第 44 炸弹小组的服役时,我最终偶然发现了二战时期新的数字化数据来源。尽管有越来越多的关于二战的数字信息可用,但存放这些数据的数据库使得汇总和分析这些数据变得不可能。

我想超越沃利的故事,看看第 44 炸弹组逍遥法外,以背景沃利的服务。例如,知道沃利在战争中飞行了 42 次任务,但不知道第 44 轰炸大队的飞行员在战争过程中飞行任务的平均次数,这没有什么意义。

通过大量的人工努力和偶然的预感,我确定沃利执行的任务超过了第 44 轰炸大队所有 5000 名飞行员的 99.5%。这是从数据中收集的一种见解,它从根本上改变了我对沃利战争和我所认识的祖父的看法。

我知道如果我能创建一个可操作的数据集,就会有成百上千的洞见,就像我收集的关于我祖父的洞见一样,等着被发现。

仪表板的想法源于我想看看第 44 轰炸组所有 5000 名成员的飞行任务、战斗损失和人口统计数据之间的关系——这是一项前所未有的艰巨任务。

我从探索如何利用为业务分析设计的 21 世纪数字工具讲述大数据战争的新历史开始。这些工具是我研究人力资本问题的日常工作的主要内容,在回顾过去时非常有用。

我学到的是,从 75 岁的老人身上挖掘洞察力与从新数据中提取洞察力没有明显的不同。

在建立仪表板之前,我花了一年的大部分时间在网上搜集和清理了 150 万个数据点,涉及战争期间在第 44 轰炸队服役的所有 5000 名飞行员的人口统计数据、战时任务和战斗损失。

在这个过程中,我了解到 75 年前的数据充满了质量问题。最初是用手捕捉,然后手动输入数据库,要纠正的不一致的规模和范围是深远的。获得最终数据集包括 10,000 多行数据清理语法。

带着清理过的数据,我制作了仪表板线框,旨在讲述一个关于集体影响的视觉故事和在第 44 轰炸队服役的 5000 名轰炸机男孩的奇异故事。

经过四个月的休假,第 44 炸弹组数据仪表板在谷歌数据工作室成为现实。

第 44 个炸弹组数据仪表板讲述了一段新的二战历史,直到今天,欧洲战争在 V-E 日结束 75 年后才成为可能。

仪表板从 150 万个数据点中提取数据,具有 100 多种数据可视化,位于数据科学和历史的交汇处。它讲述了第 44 炸弹小组在二战中的集体影响,以及在该小组服役的 5000 名士兵的奇异故事,包括我的祖父沃利。

以下是第 44 轰炸机群在二战期间从未公开的大数据历史,其中包含数据洞察和摘录自 第 44 轰炸机群数据仪表板 :

第 44 轰炸群是驻扎在英格兰的第一个 B-24 重型轰炸机群。他们是空战的先驱。

在盟军登陆欧洲土地的近两年前,第 44 轰炸组飞进了欧洲堡垒。从 1942 年末到 1945 年的胜利日,他们连续飞行了 29 个月。超过一半的第 44 轰炸组的任务(57%)是在 1944 年进行的。在 1945 年五个月的作战行动中,21%的战时任务被执行——与 1943 年十二个月的飞行量相同。

第 44 轰炸队参加了欧洲战区的每一场主要战役。在 344 次任务中,第 44 轰炸组轰炸了 8 个不同的国家,包括德国的 117 个不同的城市和法国的 66 个不同的城市。

第 44 轰炸群的损失是空前的。超过一半(53%)的 B-24 轰炸机在战斗中损失。超过四分之一(26%)在第 44 军服役的飞行员被杀或成为战俘(n=1280)。

从统计数据来看,完成一次服役期是不太可能的。战争结束时,一次旅行包括 35 个任务。阵亡的 44 名飞行员平均执行 9 次飞行任务;成为战俘的飞行员平均执行 11 次任务。

根据人口统计,在第 44 轰炸队服役的 5000 名飞行员来自各行各业:

出生状态

  • 一半来自美国人口最多的 6 个州,另一半来自中间的每个州。
  • 他们来自除阿拉斯加以外的每个州。
  • 将近四分之一(23%)的人出生在纽约、宾夕法尼亚或马萨诸塞州。
  • 五分之一(21%)的人出生在加利福尼亚州、德克萨斯州、伊利诺伊州和俄亥俄州,这些在 1940 年人口普查中人口最多的州不在东北部。

种族&国籍

  • 除了种族,第 44 轰炸组的成员几乎在各个方面都是不同的:第 44 轰炸组 95%的飞行员是白人。注意:非裔美国人不被允许在陆军航空兵中担任战斗飞行员——除了 1941 年接受的少数非裔美国人担任战斗飞行员——即“黑色轰炸机”。

婚姻状况

  • 几乎十分之八(77%)的人在入伍时是单身。这个群体的平均年龄是 22 岁。
  • 只有十分之一(13%)的人在入伍时已经结婚。他们的平均年龄为 27 岁,比单身的同龄人要大。

迄今为止,从未被告知的第 44 轰炸组集体影响的大数据历史一直是焦点,它忽略了在该组服役的 5000 名飞行员的奇异故事。

用大数据制作叙事的美妙之处在于,讲述故事的可能性呈指数级增长,既能看到“大局”,又能放大“大海捞针”般的小故事——只需点击几下鼠标。

仪表板是过滤大数据的有用工具,可以精确定位单一事件。通过创造性地利用 Google Data Studio 等仪表板软件中可用的标准数据元素,可以构建一个类似于数据库的用户界面,这样用户就可以使用各种过滤器轻松确定单个数据点。

在讲述第 44 炸弹组的故事时,与该组集体影响的故事相对应的是搜索单个飞行员故事的能力,这一功能是第 44 炸弹组数据仪表板的基石:

胜利日第 75 天提醒人们过去的存在。在大数据和历史的交汇点上,有一个前所未有的机会来揭示我们祖先的故事。

数据科学家处于一个独特的位置,可以创造性地利用我们的行业工具进行创新,利用大数据、分析和数据可视化挖掘过去的新故事。

数据科学和历史的交集把我带回了我祖父沃利的战争。他默默无闻的英雄主义和克服重重困难生存下来的故事,来自我拼凑的数据点。过了 75 年,人们才知道沃利在战争中做出的巨大牺牲。

第 44 轰炸组 B-24 轰炸机在英格兰上空(来源:希普达姆飞行俱乐部博物馆)

大数据有能力从我们个人和集体的历史中讲述深刻的人类故事。

让 V-E 日 75 周年纪念活动鼓励探索历史和数据科学的交汇点:

  • 通过探索位于www.44thbombgroup.org的完整仪表盘,继续学习二战期间第 44 轰炸组飞行员的集体影响和独特故事。
  • 开始探索如何利用你作为数据科学家的技能来挖掘过去的故事,包括你祖先的故事。

用数据寻找最残忍的食人尸歌

原文:https://towardsdatascience.com/using-data-to-find-the-most-brutal-cannibal-corpse-song-bf318d0b3ef4?source=collection_archive---------17-----------------------

(来源:瑞安·哈维尔拍摄的照片)

我们能依靠数据来确定有史以来最残忍的食人尸宋吗?—但是,首先,什么是“野蛮行为”?我们真的可以用一首歌来衡量它吗?—是的,我们可以。算是吧。

活跃了三十多年的食人尸是有史以来最重要的死亡金属乐队之一。我仍然记得当他们的第一张专辑“被吃回生命”在 1990 年发行时(那时我十二岁):它在金属界引起了震动。以前从来没有一支乐队在音乐、歌词和视觉方式上表现出如此极端的“残忍”(专辑封面上的“僵尸吃自己的内脏”完美地概括了这一切)。

在这种背景下,受埃文·奥本海默(Evan Oppenheimer)的博客文章的启发,我开始着手用数据驱动的方法寻找最残忍的食人尸体歌曲。

项目简介

这个项目可能看起来有点傻,但它实际上是应用分析的一个很好的练习和展示。当我们不仅缺乏有标签的数据(我们实际上从一开始就没有任何数据——我的唱片架上的食人唱片帮不上什么忙…),而且——更糟糕的是——我们甚至还没有一个清晰的、可衡量的目标值定义时,这是一个不那么微不足道的情况。

所以,最初的问题是:

  • 残忍的定义是什么?
  • 如何衡量一首歌的残暴程度?
  • 我们能获得什么数据来构建这样一个“残暴指数”?

食人尸首张专辑的美术作品。30 年过去了,考虑到随后两张唱片的封面艺术,它现在看起来实际上是无害的。但是有一件事你绝对应该认真对待:“父母忠告”贴纸!(来源:文森特·洛克的作品

数据&代码:用 Python 进行分析。从 Spotify API 请求音频相关功能(使用 spotipy 库)和从 genius 网站抓取歌词(使用 API 请求和 web 抓取与 beautifulsoup4 库的混合方法)。如果你想更深入地研究或编码:所有代码,包括一些额外的数据探索、推理和解释(以及一些额外的声音剪辑),都可以在这个 GitHub repo 中找到。

定义“总残暴指数”

根据《免费词典》,残暴被定义为:“无情、残酷、苛刻或无情的状态或品质。”

对我来说,在一首歌里有两种表达方式:

  1. 到了声音特征(比如音乐本身的能量和效价)
  2. 歌词(一个充满‘苛刻而残酷’的词汇)

所以,为了定义一个'总残暴指数 ' ( TBI )我们必须首先计算然后结合这两个元素。我将把第一个称为'声波野蛮指数 ' ( sbi ),第二个称为'抒情野蛮指数 ' ( lbi )。为了构建 TBI ,我们可以取它们的平均值(算术平均值):

总残暴指数,TBI=

*(0.5 声波残暴指数,sbi) +

*(0.5 抒情兽性指数,lbi)

因为我将确保 sbi 和 lbi 都在相同的范围内,从 0 到 1,所以将权重设置为 0.5 意味着歌曲中的总残忍程度由歌曲的歌词和音乐决定。(对我来说没问题,但是当然,如果需要,可以通过设置不同的权重来改变比例。)

野蛮音乐:计算‘声波野蛮指数’

为了确定一首歌曲的声音残忍程度,我依赖于 Spotify 为他们网站上的每首歌曲提供的两个音频功能。你可以通过它们的 API 来访问它们(详见 GitHub repo 中的第一个笔记本)。

许多提供的音频功能不符合我们的特殊目的,但有两个特别有趣。第一个是“”。据 Spotify 称:

  • 是一个从 0.0 到 1.0 的量度,描述一个音轨所传达的音乐积极性。高价曲目听起来更积极(例如,快乐、愉快、欣快),而低价曲目听起来更消极(例如,悲伤、沮丧、愤怒)。

好吧,我们当然希望一首残酷的歌曲大部分是负面的(也就是低价),但我们不希望它是悲伤或沮丧的,所以我们需要结合高价和高能来抵消这一点。幸运的是,Spotify 也为我们提供了这一功能(甚至在描述中提到了死亡金属——罢工!):

  • 能量 是从 0.0 到 1.0 的度量,代表强度和活动的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。对该属性有贡献的感知特征包括动态范围、感知响度、音色、开始速率和一般熵。

因此,让我们来看看从 Spotify 数据库中搜集的 164 首食人尸体歌曲的的分布,每首歌曲的评分范围为 0 到 1:

化合价和能量分数分布图。“距离图”显示了歌曲在相应特征的范围频谱上的分布情况。

是的,这些歌曲确实倾向于消极的一面(低价)——但是,见鬼,看看那种能量分布……都被挤在最大值附近!相当激烈的东西,然后…

通过计算能量1-化合价(从 1 中减去化合价,这样更高的值意味着它更“负”)的平均值,我们可以创建一个“声波残暴指数”(sbi)的方程。这样一来,最残酷的歌曲将是那些既高能又低价,同时又对两者同等重视的歌曲:

计算所有歌曲的 sbi 导致以下结果:通常所有歌曲的范围在 0.7 和 1 之间。音乐上最残酷歌曲的竞争非常非常激烈。以 2014 年专辑《一个骨骼领域》中的歌曲高速撞击飞溅险胜。(在这里听一段 30 秒的声音样本。不错吧,嗯?)

sbi 评分排名前五的歌曲。前十几首歌左右超级接近。

所有歌曲的 sbi 分数的频率分布。总的范围是 0 到 1,但是没有一首歌的分数低于 0.7。有很多歌真的很接近 max 值。

因为得分最高的歌曲的得分如此接近,我们不得不希望“抒情暴力指数”能够做出更明确的区分。

(而且,可以这么说,会的……)

(非常)早期的食人尸体队列,可能在 1990 年左右。从一些旧金属杂志上扫描下来的。(来源: Pinterest )

残忍的歌词:计算“歌词残忍指数”

这是更棘手的部分,不仅仅是因为我觉得必须以某种免责声明开始:食人尸的歌词很恶心。真的。这是他们应该做的。但是他们音乐的一个好处是你实际上无法理解他们在说什么。;-)

(顺便说一下:1995 年,当时的参议员鲍勃·多尔指责该乐队“破坏了美国的国民性”。在德国,前三张专辑的销售和现场演出直到 2006 年才被禁止。)

尽管如此,我还是从 genius.com 搜集了所有食人僵尸歌曲的歌词。但是如何确定他们的残暴程度呢?

为了帮助我,我找到了 NRC(加拿大国家研究委员会)提供的“化合价、唤醒和支配词汇”。它包括一个超过 20,000 个英语单词及其效价,唤醒和优势分数的列表,范围从 0(最低 V/A/D)到 1(最高 V/A/D)。

而一家独大对我们帮助不大,(再来!)和唤起正是我要找的。根据核管制委员会:

  • 是积极—消极或愉快—不愉快的维度
  • 唤醒是兴奋——平静还是主动——被动维度

那就让我们选择低价、高唤醒度的词汇组合吧!这可能是最接近残酷的。引起强烈负面情绪的东西。是的,从 20,000 个词条的整个词典中筛选出具有最高低价高唤醒分数组合的单词(我称之为 wbi ,‘单词残暴指数’)会返回我们正在寻找的结果:

根据“单词残暴指数”在 VAD 词典中排名前 10 的单词。请注意,为了方便起见,我将(1 价)转换成了‘反价’特性。通过简单地取‘唤醒’和‘反价’的平均值,然后将它们全部归一化到从 0 到 1 的范围,为词典中的每个单词计算 wbi。

那么,如何利用这个“野蛮得分”的词汇呢?最简单的解决方案可能是根据每首歌出现的所有单词来计算平均 wbi 值。但是凭直觉,我不认为这是最好的方法,因为我对包含(必要但不重要)“非残酷”单词的分数和分布不感兴趣。

我在寻找一个乐谱

  1. 考虑到在一首歌中使用了多少次野蛮的词语,

2.他们在总字数中所占的比例是多少

3.这就带来了与歌曲持续时间相关的所有这些。(因为,当然,我们不仅仅想要许多残酷的话语,我们想要一个高密度的残酷话语,意味着每分钟尽可能多的残酷话语。)**

所以,我最终选择的 lbi 是:

(可以看出,残暴词的字数/道具在最终等式中被数学抵消了。)

好吧,但这让我们解决最后一个问题:如何将一个词归类为残暴?通过“单词残暴指数”,我们对每个单词都有一个从 0 到 1 的连续评分,现在必须将其转换为“残暴”/“非残暴”的硬二进制分类。

经过一些测试后,我(尽管有些武断)决定这样做:取词典中 20,000 个单词的总体 wbi 分布,并将其中具有最高 wbi 分数的 5%归类为“野蛮”。其余 95%的单词被归类为“非野蛮”——所以总体来说,20 个单词中有 1 个被认为是“野蛮的”:

****灰线标志着“野蛮”词语的界限。所有单词中有 5%的 wbi 阈值为> 0.797。他们被归类为“野蛮人”。其余的不是。(顺便说一句:我试验了不同的阈值,对最终结果的影响很小,得分最高的歌曲从未改变。)

是时候通过一个基本的 NLP 管道来输入所有这些食人尸体歌曲的粗糙歌词了(对它们进行清理、标记和词条化,然后删除所谓的“停用词”,这些词通常没有什么隐含意义(如果有的话)。结果:每首歌曲的单个内容单词的干净列表,然后使用词典进行 wbi 评分和分类。wbi_score >大于阈值(0.797)的所有单词都被标记为“野蛮”。

****歌曲文本通过预处理管道的旅程。从字符串到词汇化的标记,到 wbi 分数,再到最终的单词分类。

因此,在根据上面定义的公式计算所有歌曲的“抒情暴力指数”后(歌曲的持续时间是从 Spotify 单独刮出的),我们可以查看最终的分布和具有最高“抒情暴力指数”分数的 5 首歌曲:

****lbi-分配。请记住,履行机构没有产生一个明确的赢家。这里我们有一些明显的“异常值”在上端!它们是:

lbi-score 排名前五的歌曲。

结果很明显。《刺伤惨案》是目前为止“抒情残暴指数评分最高的歌曲。这首歌只有短短的 1 分 41 秒,这无疑是一个很大的帮助。食人者尸体成功地在这么短的时间内包装了许多残忍的词语……(我真的不想深入抒情的细节,但文本以多次重复短语“刺、砍、砍、杀”开始——相信我,这是迄今为止最精彩的部分……)

食人尸体后来排队,表现正常的宣传片拍摄。(来源

将所有这些放在一起:计算 TBI

一个人不需要更高的数学学位来决定哪首歌将是最终的赢家,但是,无论如何,让我们把所有东西都汇集在'总暴行指数'** (TBI)😗*

哦,我们几乎得到了一个漂亮的钟形正态分布!标准化并组合 sbi 和 lbi 后,我们仍然可以看到上面已经检测到的异常值。

所以,最后…他们来了…有史以来最残忍的 5 首食人尸体歌曲!(据我 TBI。)

他们来了。注意“穿刺伤大屠杀”有多突出!

就是这样:《穿刺伤屠杀》是迄今为止最残忍的食人尸歌。所以,现在,如果你敢,请点击这里来听一听。

(如果你想看到更多的分析,比如大麻尸体在音乐上是否与食人尸体一样残忍,或者基督·巴恩斯或乔治·费舍尔是否是更残忍的歌手,而且——真的很有趣——食人尸体似乎在将 TBI 调小一些时处于最佳状态,那么看看 GitHub 上笔记本 中的附录部分。)

资源/大大感谢:

奖金

如果你喜欢(或想喜欢)死亡金属——这些是 2019 年我的唱盘上旋转次数最高的唱片
(它们都是:K-I-L-L-E-R):

1) 石化——掏空空洞 LP

2)女巫的呕吐物——深埋在无底的坟墓里 LP

3)爬山虎—索诺兰恶化 LP

4)弱侧—向前进入黑暗 LP

5)骨骼残骸——注定悲惨 LP

从垃圾箱到灯泡:将家庭垃圾转化为本地清洁能源

原文:https://towardsdatascience.com/using-data-to-help-turn-household-waste-into-local-clean-energy-cb06aaddad8e?source=collection_archive---------28-----------------------

在这里查看我的项目:https://github . com/tcastanley/MSW-to-Energy-feed-Analysis

格伦·凯莉Unsplash 拍摄的照片

对于我在 熨斗学校的沉浸式数据科学项目 中的最后一个顶点项目,我决定测试我的新技能,并继续推进我对数据、浪费和能源之间关系的个人调查。最近,我一直在学习更多关于城市固体废物转化为能源的各种方法。迄今为止,我遇到的最有前途和最有效的技术是等离子弧气化。在我的研究中,我发现了解用作原料的城市固体废物的具体组成细节是设计等离子气化设备的许多关键步骤之一。我开始为我的顶点项目做的是,看看我是否能找到一些 MSW 收集数据集并进行原料分析,目的是计算每个样品的特定 废物类型成分 能量密度(kWh/kg )总能量(kWh)

等离子气化

在我们进入计算近似城市固体垃圾能量值所需的数据分析之前,让我们多谈一点关于范式转换技术,等离子气化。

“在缺氧环境中将含碳物质转化为气体产品,以生产能源产品和副产品。”城市固体废物到能源的转化过程”,Gary C. Young,2010 年

上面的技术定义是不言而喻的,尽管可以通过将这一过程视为能够获取地球上几乎任何材料(除了金属和玻璃)并将其转化为基本分子成分来进一步简化。这种产品叫做 【合成气】 ,主要由一氧化碳氢气【H2】组成。合成气可以进一步加工成许多不同的有价值的化学产品,尽管这取决于设备的用途和设计。我有兴趣了解如何从城市垃圾发电。

气化设施的提案需要收入预测,就像大多数商业企业一样。这些预测取决于对用作气化过程原料的燃料中的能量的理解。这就是通过原料分析确定能量密度发挥作用的地方。我想建立一个模型,它可以接收特定的废物样本,根据一系列物品的重量进行测量,并预测其能量密度。

这一过程的另一个副产品是玻璃化炉渣或生物炭,它们是气化过程的结果。根据城市固体废物原料的具体成分,这种产品的收入存在不同的市场。

总而言之,使用这项技术作为解决全球日益增长的城市垃圾问题的一部分是显而易见的。洗涤技术安装在整个气化过程中,以确保空气排放符合当地健康和安全法规。当人们将此与室外厌氧消化的替代长期解决方案进行比较时,会向大气中释放无数吨有害甲烷,等离子体气化就变成了一个商业问题,以及如何使这一解决方案可持续并为所有利益相关者带来利润。

数据清理和建模

作者图片

本项目的原料分析测量从伯利兹、所罗门群岛和瓦努阿图收集的城市固体废物样本数据中的能量密度(kWh/kg)家庭固体废物总能量(kWh)

这些数据是由亚太水资源顾问公司 (APWC)委托 英联邦垃圾项目 (CLiP)在 环境渔业和水产养殖科学中心 (CEFAS)的支持下收集的。收集这些数据的最初目的是支持地方政府制定更好的海洋废物管理战略。

每个国家的每个数据集都有超过 95,000 行需要清理和重新格式化。数据集中有许多重复的项目,因为每个 MSW 样品都是用 3 种不同的方法测量的:重量、计数和体积。为了计算能量密度,我只需要查看重量值(kg ),对此进行过滤可以大大减少每个数据集中的行数。在删除了所有多余的和不必要的值,并重塑数据集后,我准备建模。用于两个顶级模型的数据集的最终形状是(438,29),代表 438 个不同的家庭生活垃圾样本,每个样本具有 29 个项目特征的特定组合。这些来自 3 个不同国家的数据,被用来训练和测试我的项目中表现最好的模型。

作为过程的一部分,我经历了许多不同的模型类型。我使用 StatsmodelsSci-kit Learn 从一些基本的线性回归模型开始,这两种模型都不理想,因为它们大量地过度拟合了数据。从那以后,我继续研究决策树、随机森林和 XGBoost 模型,它们都比基本的线性回归模型表现得更好。最后,我用我最喜欢的架构结束了我的建模工作,这是一个多层感知器(MLP) ,或者最近被称为神经网络

图一。XGBoost 模型特征重要性— 作者提供的家庭生活垃圾总能量(kWh)图像

我想强调的一个值得注意的建模特性是特性重要性方法,它是 XGBoost 模型的标准。我直观地绘制了这些信息(图 1 ),这些信息代表了模型中每个特定特征的重要性或相对影响。

正如上面清楚显示的那样,我们看到前 3 个重要特性的顺序是:

  1. 食物
  2. 其他有机物
  3. 尿布

那么这意味着什么呢?既然这个模型已经过训练和测试,可以预测代表以 kWh 为单位的总能量的因变量,我们应该可以看到那些对每个家庭的总能量值影响最大的项目。碰巧的是,这三种物质本质上都是有机物,这意味着它们都含有相对较高的碳含量。因此,我们可以得出结论,这个模型在捕捉和评估碳含量较高的项目方面比碳含量较低的项目做得好。

图二。伯利兹模型——左图:每个地点的平均垃圾成分和能量密度(千瓦时/千克),右图:收集地点,每个地点都有垃圾详情。作者图片

现在应该指出的是,这就是像这样的数据科学分析中偏见蔓延的危险所在。标记这些数据所需的计算的本质是使用预定的科学公式(参见 净热值维基 )来完成的,根据定义,这些公式将高含碳材料的相对重要性插入到数据中。这是一个很好的迹象,模型反映了这一点,因为这是一个科学事实,碳浓度越高的材料越富含能源。然而,记住这样的偏差总是存在于数据中仍然是至关重要的,记住不断地欣赏和反思这一事实对于理解每个生成的模型的尽可能多的细微差别是至关重要的。

结果

下面是我对每个国家的顶级模型的快照比较,以及综合了所有 3 个国家数据的最终模型。训练-测试分割是模型被训练和测试的数据实例的数量。对于每个因变量,我显示每个数据集的中值,与每个模型各自的均方根误差(RMSE)进行比较。在描述这类回归问题的模型预测准确性时,RMSE 是一个被广泛接受的主要指标。基本上,这意味着我的模型预期它的预测会偏离真实值的平均值{\ F3 。}

图三。顶级模型比较—最佳模型是组合模型,通过增加数据来吹捧最准确的指标!作者图片

如上所述,最终的组合型号是所有型号中性能最好的型号,远远超过其他 3 种型号。

家庭生活垃圾总能量(kWh) r 平方: 0.9943

能量密度(千瓦时/千克)r 平方: 0.9575

这一结果并不令人惊讶,因为有更多数据进行训练的模型能够看到更多独特的特征组合,因此在进行未来预测时可以更好地解释它们。不管怎样,看到现实沿着预期的轨迹发展,我还是很放心的,我对我的最终模型非常满意。

那又怎样?结论和未来工作

你可能会问自己,那又怎样?这到底意味着什么?哪里来的精力!?嗯,从这个项目的结果中可以得出一些不同的见解。

城市固体废物成分:了解每个家庭以及特定地区的平均城市固体废物成分,可以帮助参与城市固体废物管理的所有利益相关者做出决策。从垃圾收集机构到家庭用户,更好地了解垃圾成分可以提供行为洞察以及潜在的社会经济洞察。然而,这些见解需要更多的定性数据分析,因为这超出了我的项目范围。

模式 1 -家庭生活垃圾总能量(kWh): 我认为,改变人们处理垃圾的方式的关键一步是改变思维方式。我希望这个模型能够成为用户应用的基础,通过它,有适当激励的房主可以输入关于他们自己的城市生活垃圾的数据,并获得潜在的能源输出(也许有一天还会有能源信用)。像这样的想法有发展的途径,我相信在城市固体垃圾和电力公司之间存在一个三角洲,在那里用户可以开始把他们的【废物】视为一种能源。我相信,为对垃圾更负责任的用户开发和整合更好的激励计划,将有助于提醒人们我们星球的循环性质,记住他们的垃圾可以在以后被更好或更坏地利用,而不是简单地被扔掉和遗忘。

模型 2 -能量密度(kWh/kg): 该模型可以为所有城市固体废物管理利益相关者提供许多与模型 1 相同的见解。然而,最大的区别在于能量密度作为一种度量标准的更普遍的性质。该模型使用每种废物的相对重量来确定一个样品的含量,而不是试图预测总共存在多少能量。这是一个更有价值的衡量标准,尤其是对那些投资于城市固体废物转化为能源的企业而言。我希望将这个模型发展到这样一个程度,即可以建立一个精细管理的城市生活垃圾项目列表作为模型的特征。在此基础上,可以轻松完成潜在设施的原料分析,并可靠地得出每个地区的准确能源密度分数。

我从这个项目中获得了很多乐趣,我也越来越习惯写这些博客了!对于那些走到这一步的人,非常感谢!我计划很快开始一个新的博客系列,以帮助我的数据科学修订和未来的学习,请继续关注!如果任何人有任何问题,评论,或者只是想聊天,请随时给我发电子邮件@ tcastanley@gmail.com!干杯!

使用 Spotipy 观察 80/90 年代音乐和现代嘻哈音乐之间的差异

原文:https://towardsdatascience.com/using-data-visualizations-to-understand-the-vast-shift-from-80s-90s-hip-hop-to-today-afa0f942685?source=collection_archive---------30-----------------------

实践教程

早期和当代嘻哈音乐的结构性差异。

戴夫·韦瑟罗尔Unsplash 上拍摄的照片

回到 80/90 年代,hip-hip 走上了布朗克斯的街头,成为当今世界上最知名的流派之一。从那以后,对音乐的处理和接受随着美国文化一起发展。由于没有完全体验过 80/90 年代的音乐,我开始思考 80/90 年代和我们今天所听的音乐之间的结构性差异。当我读到 Kish Lal 的这篇文章时,我对 hip-hop 取得的巨大进步很感兴趣。大多数 80/90 年代的说唱歌手会把“反叛”嘻哈作为一种社会政治运动来表达黑人的斗争;现在,Kanye West 为越来越受欢迎的“情感”嘻哈音乐铺平了道路。

“西方代表着未来。这些变化会被听到,但也会导致文化的改变。”

那么,嘻哈改变了哪些特质?由于听一堆 80/90 年代的嘻哈音乐需要一段时间,所以我利用了 Spotify 的 API,这样我就可以一次提取多个曲目的“音乐属性”。我在这里的目标是检查音乐数据,找出嘻哈的哪些成分发生了变化;通过使用 Spotify 的 API 和数据可视化工具,我希望获得关于与今天相比是什么让嘻哈音乐在 80/90 年代流行以及它在新一代中扮演的角色的见解。有了数据可视化,很容易画出这两种类型之间的差异,并以图形方式显示嘻哈喜好的变化。

我的过程

  1. 我在我的 Spotify 帐户下创建了 2 个播放列表。一个包含 100 首流行的 80/90 年代嘻哈歌曲;另一个包含了 100 首流行的新嘻哈歌曲。我使用了多个 Spotify 管理的播放列表中的歌曲,以及当时和现在一些最受欢迎的艺术家的歌曲。
  2. 要使用 Spotify API,我需要访问我的 Spotify 凭据。通过进入 Spotify For Developers ,我登录了我的 Spotify 账户,点击了“创建一个应用”。之后,我能够看到并记下我的客户 ID 和客户机密。
  3. 计算机编程语言

在 Jupyter Notebook 中,我使用 spot ipy(Spotify Web API 的 Python 库)从每个播放列表中获取音乐数据。我还使用熊猫来创建数据框并保存数据集。

import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import timeclient_id = '<YOUR CLIENT ID>'
client_secret = '<YOUR CLIENT SECRET>'
client_credentials_manager = SpotifyClientCredentials('<YOUR CLIENT ID>', '<YOUR CLIENT SECRET>')sp = spotipy.Spotify(client_credentials_manager = client_credentials_manager)

在这之后,我使用一个函数来获取我的播放列表中每首曲目的 id。为此,需要您的 Spotify 用户名和 Spotify 播放列表 URI。要获得播放列表 URI 你去播放列表,点击 3 点,点击共享,然后“复制 Spotify URI”。

def getTrackIDs(user, playlist_id):
    ids = []
    playlist = sp.user_playlist(user, playlist_id)
    for item in playlist['tracks']['items']:
        track = item['track']
        ids.append(track['id'])
    return idsids = getTrackIDs('<YOUR USERNAME>', '<SPOTIFY PLAYLIST URI>')

下一部分检索所有的曲目属性,如发行日期、流行度、专辑和所有可以在 Spotify API 中找到的音频特性。我使用的属性列表如下,稍后我将展示每个属性的定义。

def getTrackFeatures(id):
  meta = sp.track(id)
  features = sp.audio_features(id)name = meta['name']
  artist = meta['album']['artists'][0]['name']
  length = meta['duration_ms']# features
  acousticness = features[0]['acousticness']
  danceability = features[0]['danceability']
  energy = features[0]['energy']
  instrumentalness = features[0]['instrumentalness']
  liveness = features[0]['liveness']
  loudness = features[0]['loudness']
  speechiness = features[0]['speechiness']
  tempo = features[0]['tempo']track = [name, artist, length, danceability, acousticness, danceability, energy, instrumentalness, liveness, loudness, speechiness, tempo]
  return track

我循环播放曲目,应用上面的函数,并将数据集保存到一个 CSV 文件中,因为我将使用 RStudio 来绘制数据集。

# loop
tracks = []
for i in range(len(ids)):
  time.sleep(.5)
  track = getTrackFeatures(ids[i])
  tracks.append(track)track# create dataset
df = pd.DataFrame(tracks, columns = ['name', 'artist', 'length', 'danceability', 'acousticness', 'danceability', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo'])df.to_csv("<your file name>.csv", sep = ',')

4.我对现代 hip hop 播放列表重复了上述过程,并使用 RStudio 绘制图表,比较并找出 80/90 年代 hip hop 和新 hip hop 之间的差异。

以下是我在此过程中使用的特性/属性列表(您可以在这里看到所有属性):

  • 可跳舞性:描述了一首曲目根据音乐元素的组合适合跳舞的程度,包括速度、节奏稳定性、节拍强度和整体规律性。越接近 1.0,越适合跳舞。
  • 语音:检测音轨中是否存在语音。越是类似语音的录音(例如脱口秀、有声读物、诗歌),属性值就越接近 1.0。
  • 乐器性:预测音轨是否不包含人声。“Ooh”和“aah”在这种情况下被视为乐器。Rap 或口语词轨道明显是“有声的”。越接近 1.0,轨道越可能不包含人声内容。
  • 价:描述一首曲目所传达的音乐积极性。高价(接近 1)的音轨听起来更积极(例如,开心、愉快、欣快),而低价(接近 0)的音轨听起来更消极(例如,悲伤、沮丧、愤怒)。
  • 能量:代表强度和活动性的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。越接近 1.0,强度和活跃度越大。
  • 速度:轨道的总体估计速度,单位为每分钟节拍数(BPM)。在音乐术语中,速度是给定作品的速度或步调,直接来源于平均节拍持续时间。
  • 响度:一个音轨的整体响度,单位为分贝(dB)。响度值是整个轨道的平均值,可用于比较轨道的相对响度。越接近 0,音轨的声音越大。
  • 声学:0.0 到 1.0 的音轨是否声学的置信度度量。越接近 1,轨道的声音越大。

调查的结果

在绘制图表后,我注意到在可跳舞性、语言能力和乐器演奏能力上的差异可以忽略不计。大多数关键的差异来自于像能量、化合价、速度、响度和声音这样的属性。

我们将关注哪些关系:

  1. 化合价和能量
  2. 化合价和节奏
  3. 效价和响度
  4. 化合价和声音

我选择将这些属性与效价进行比较的原因是,80/90 年代和现在的音乐效价差异最大,能量、节奏、响度和声音是最有可能改变歌曲积极性/情绪的因素。

化合价和能量

作者图片

作者图片

新 Hip Hop: (趋势)无明确趋势且多为零散;(观察)这些歌曲的模式接近低价值(0.3)和中等能量值(0.5),这意味着这些歌曲大多有一个否定/压抑的声音,有些强烈/充满活力。

老嘻哈:(趋势)似乎没有趋势;(观察)歌曲的调式具有高价值(0.7)和中高能值(0.7),这意味着该播放列表中的大多数曲目是更快乐/乐观的歌曲,具有活力和更响亮的感觉。一些价值较低的轨道也有较高的能量值,这就是为什么没有具体的趋势。

化合价和节奏

作者图片

作者图片

新嘻哈:(趋势)价越高,赛道越快;(观察)轨道的模式包含快节奏 (~140 bpm)和中等价(~0.5)。此播放列表中的曲目节奏不同;听起来越消极的声音通常越慢,听起来积极的声音越快。

老嘻哈:(趋势)没有明确的趋势。(观察)这个播放列表中的曲目模式接近于一个慢节奏 (~90 bpm)和一个非常高的价(~0.8)。这首歌中听起来更快乐的歌曲大多是较慢的歌曲,但一些低价的歌曲也在 90 bpm 左右。

效价和响度

作者图片

作者图片

新嘻哈:(潮流)无潮流。(观察)尽管有价值,但几乎所有的曲目都 (~-5 分贝)这可能是由于现在的音乐家比过去有更多的制作技巧和设备。

老嘻哈:(趋势)没有明确的趋势。(观察)高价歌曲似乎从-15 分贝到-4 分贝不等,但大多数歌曲都有高价的中到高响度 (~-6 分贝)。

化合价和声音

作者图片

作者图片

新嘻哈:(潮流)无潮流。(观察)尽管有价值,但大多数音轨都有中低音质(~ 0–0.3)。较新的音乐似乎包含很多更自然的声音,如钢琴、吉他、未经处理的人声。

老嘻哈:(潮流)无潮流。(观察)变化要小得多,因为该播放列表中的大多数曲目都具有高价和低音质(~0.0)。这些曲目大多不太自然,比如电子拍子和吉他。

总结

我在上面谈到了新嘻哈和 80/90 年代嘻哈的一些最重要的区别,现在我将通过提供一些例子和解释/结论来更深入地谈论它们,说明嘻哈偏好是如何变化的。

化合价和能量

80/90 年代和新嘻哈之间的主要区别是效价。我们这一代人更喜欢不太乐观、令人沮丧的歌曲,而 80/90 年代的嘻哈则更积极、更有前瞻性,与更高的强度和能量水平相关。两个播放列表都有一个非常明显的趋势,即一首曲目的价值越低,它包含的能量就越少。

80/90 年代嘻哈

80/90 年代积极嘻哈音乐的最佳范例是 Nas 的《世界是你的》,奎恩·拉提法的《联合国青年队》,公敌的《与权力作斗争》,停止暴力运动的《自我毁灭》,以及西海岸全明星乐队的《我们都一样》。80/90 年代的嘻哈是一场“社会政治运动”,这些只是许多乐观和充满希望的歌曲中的一部分,这些歌曲谈论黑人对黑人犯罪,制止帮派暴力,鼓励团结,生活课程,以及对年轻黑人的鼓舞人心的积极信息。

新嘻哈

新嘻哈比原来的嘻哈少了很多积极/活力。一些例子包括 Cordae ft 的“Gifted”。罗迪·里奇,Juice Wrld 的《许愿池》,NF 的《瘫痪》,未来派的《没有别人》,逻辑的《完美》。其中一个区别是,今天大多数嘻哈艺术家谈论金钱和名誉,以及与毒瘾、心理健康、他们的过去、努力成为最好、与其他说唱歌手竞争等的斗争。他们不会隐瞒自己内心的黑暗面和生活的丰富面。不是说今天的说唱歌手对反对种族主义的运动没有贡献;许多人仍然这样做,但他们所有的歌曲很可能不是围绕一个点。他们在音乐中谈论的东西有更多的变化,粉丝们似乎喜欢这种开放性和多样性。

新嘻哈音乐中关于黑人运动的音乐似乎正在兴起,尤其是现在,歌曲包括黑人思想的“思想与每个人”,Lil Baby 的“更大的画面”,Anime 的“根”,Beyonce ft 的“自由”。肯德里克·拉马尔。因此,“社会-政治运动”可能会在嘻哈音乐中回归,但他们很可能会比 80/90 年代更“不快乐”。

声学

较新的嘻哈音乐有更多的音响效果,这意味着更多的曲目由吉他、钢琴、管弦乐或自然人声组成。80/90 年代的嘻哈音乐由电吉他、合成器、鼓和自动调音人声等电音组成。

80/90 年代嘻哈

电子放克、EDM 和 techno 是当时流行的嘻哈电子子流派,所以大多数音乐很可能包含电子音乐的元素;这就是为什么尽管它们有价值,但声学值却很低的原因。一些带有电子元素的歌曲包括马索·曼恩和莱德曼的《达·洛克怀尔德》、塞浦瑞斯·希尔的《大脑中的疯狂》、莫布·迪普的《适者生存》和图帕克的《我四处走动》。

新嘻哈

我们可以看到,较新的嘻哈歌手创作的歌曲在图中的价值上变化更大。由于艺术家现在制作不太积极的歌曲,旧音乐中的电子元素不适合新艺术家试图带来的新音调和情感。像钢琴、吉他和自然声音这样的声学元素使用得更频繁,尽管有价。沮丧的歌曲倾向于使用更多的音响作为背景,因为它融入了柔和的元素,甚至更高价的歌曲也使用一些音响。在我看来,声学的使用是激发听众情绪的最佳方式,并使他们更容易以艺术家希望的方式与音乐联系起来。例子包括麦克莫尔的《橘子酱》、罗辑的《灵魂食物 II》、NF 的《我怀念那些日子》、YoungBoy 的《不易》永不再断;这些大多使用自然的人声或钢琴作为背景。声学现在是一个更受欢迎的使用元素,尤其是在嘻哈音乐的低保真度子类别中,那里通常没有人声或文字。

拍子

较新的嘻哈音乐通常比 80/90 年代的嘻哈音乐节奏更快。如果你听 80/90 年代的 hip hop,并将其与今天的 hip hop 进行比较,这一点是最容易理解的。你可以注意到,一般来说,像 Nas、2Pac、IceCube 和德瑞医生这样的老艺人说唱速度稍慢,并拉长了他们的歌词,而今天的许多艺人说唱速度快得多,如 NF、Logic、Quadeca、Drake、Migos 等。较新的 hip hop 中的高价歌曲具有快速的节奏以及低价歌曲,并且许多低价歌曲比更积极的歌曲快得多(NF(不太积极)vs Future(更积极)就是一个例子)。似乎有一个总的趋势,节奏越慢,歌曲就越不罗嗦,你会发现新嘻哈的节奏比 80/90 年代的嘻哈有更多的变化。

音量

对于该属性,新 Hip Hop 和 80/90 年代 Hip Hop 关于它们不同的价数值具有几乎相同的响度值。大多数新嘻哈的价值较低,声音较大;大多数 80/90 后嘻哈的价值更高,也更响亮。可能导致新嘻哈比旧嘻哈更响亮的主要原因之一是乐器和嘻哈节拍的使用增加,包括贝斯、高频打击乐、鼓和增加新嘻哈活力的新元素。

结论

  • 一首流行的 80/90 年代嘻哈音乐=高能量,低声音,慢节奏,安静
  • 一首流行的现代嘻哈音乐=变化的能量(大部分是低能量),高音质,快节奏,更大声

在整个项目中,我能够通过使用数据可视化来比较两代人(80/90 年代和今天)的嘻哈音乐,以发现嘻哈文化、艺术家和一代偏好的转变带来了哪些实质性的变化。我也逐渐明白了在每一代人中,什么会成为流行歌曲。到底什么是嘻哈音乐,这可能仍然令人困惑;但有趣的是,hip-hop 的特点和喜好不可避免地会随着时间而变化。嘻哈是一种前瞻性的音乐类型,因为久而久之,更多的艺术家在他们的音乐中使用新的技术或强调不同的音乐属性。不可想象的数量的人也在网上制作歌曲,推动嘻哈文化,他们每个人都可能给这种风格带来新的东西,给音乐行业带来更多的变化。

我尝试用深度学习来预测股市

原文:https://towardsdatascience.com/using-deep-learning-ai-to-predict-the-stock-market-9399cf15a312?source=collection_archive---------0-----------------------

用神经网络预测股票价格

Vladimir Solomyani 在 Unsplash 上的照片

我想象一下,如果你能够知道一只股票下周是涨是跌,然后用你剩余的现金,你会把所有的钱投资或做空这只股票。玩了股票市场之后,知道了股票会升值还是贬值,你可能会成为百万富翁!

可惜这是不可能的,因为没有人能知道未来。然而,我们可以根据我们现在和过去对任何股票的信息做出估计和明智的预测。根据股票价格过去的走势和模式做出的估计称为 技术分析 。我们可以使用技术分析( TA )来预测一只股票的价格方向,然而,这并不是 100%准确的。事实上,一些交易者批评 TA,并说它在预测未来方面和占星术一样有效。但是也有其他交易者相信它,并且建立了长期成功的交易生涯。

在我们的例子中,我们将使用的神经网络将利用 TA 来帮助它做出明智的预测。我们将实现的特定神经网络被称为 递归神经网络——LSTM。之前我们利用 RNN 来预测比特币价格(见下面的文章):

[## 我尝试了深度学习模型来预测比特币价格

使用神经网络预测比特币价格

towardsdatascience.com](/predicting-bitcoin-prices-with-deep-learning-438bc3cf9a6f)

在文章中,我们探索了 LSTM 预测比特币价格的用法。我们稍微研究了一下 LSTM 模型的背景,并给出了如何编程来预测 BTC 价格的说明。然而,我们将输入数据限制在比特币自身的价格历史上,不包括其他变量,如成交量或移动平均线等技术指标。

多变量输入

由于我们构建的最后一个 RNN 只能接受一个序列(过去的收盘价)来预测未来,我们想看看是否有可能向神经网络添加更多的数据。也许这些其他数据可以提高我们的价格预测?也许通过在我们的数据集中添加 TA 指标,神经网络可能能够做出更准确的预测?—这正是我们想要在这里完成的。

在接下来的几个部分中,我们将构建一个新的递归神经网络,它能够以技术指标的形式接收多条信息,从而预测股票市场的未来价格。

在这里注册一个中级会员,可以无限制地访问和支持像我这样的内容!在你的支持下,我赚了一小部分会费。谢谢!

价格历史和技术指标

为了使用神经网络来预测股票市场,我们将利用来自&【p500】的价格。这将使我们对股票市场有一个总体的了解,通过使用 RNN,我们也许能够判断出市场的走向。

下载价格历史记录

要为我们的神经网络检索正确的数据,你需要去雅虎财经 下载间谍 的价格。我们将为 SPY 下载五年的价格历史,作为一个方便的.csv文件。

另一种选择是使用财务数据 API,如 EOD 历史数据 。注册是免费的,你可以访问大量的数据集。披露:我通过上面的链接从任何购买中赚取一小笔佣金。

技术指标

在我们下载了 SPY 的价格历史之后,我们可以应用技术分析 Python 库来生成技术指标值。此处更深入地介绍了我们能够从中检索指标值的流程:

[## 我是如何为比特币的技术指标编码的

使用 Python 创建比特币的技术指标

towardsdatascience.com](/technical-indicators-on-bitcoin-using-python-c392b4a33810)

上面的文章详细介绍了我们为了检索 SPY 的指标值而使用的确切的 TA Python 库。

编码神经网络

导入库

让我们通过首先导入一些库来开始编码我们的神经网络:

首先,我们导入了一些常用的 Python 库( numpy、pandas 等)。接下来,我们导入了之前用来创建 BTC 技术指标的技术分析库(上文中的提到了)。然后,我们从tensor flow Keras导入神经网络库。导入必要的库后,我们将加载从雅虎财经下载的文件。

预处理数据

日期时间转换

加载数据后,我们需要执行一些预处理,以便为神经网络准备数据,我们需要做的第一件事是将数据帧的索引转换为日期时间格式。然后,我们将数据中的Date列设置为 DF 的索引。

创建技术指标

接下来,我们将使用ta库创建一些技术指标。为了涵盖尽可能多的技术分析,我们将使用库中所有可用的指标。然后,从数据集中删除除了指标收盘* 价格之外的所有数据。*

最近的数据

一旦我们创建了技术指标值,我们就可以从原始数据集中删除一些行。我们将只包括最后 1000 行数据,以便更准确地反映当前的市场环境。

缩放数据

当缩放我们的数据时,有多种方法可以确保我们的数据仍然被准确地表示。试验不同的缩放器以查看它们对模型性能的影响可能是有用的。

在我们的例子中,我们将利用RobustScaler来扩展我们的数据。这样做是为了使极端的异常值几乎没有影响,并有望改善训练时间和整体模型性能。

使用强大的缩放器缩放收盘价

助手功能

在我们开始构建神经网络之前,让我们创建一些辅助函数来更好地优化这个过程。我们将详细解释每个功能。

  1. split_sequence —此函数分割多元时间序列。在我们的例子中,输入值将是股票的收盘价指标。这将把这些值拆分成我们的 Xy 变量。 X 值将包含过去的收盘价和技术指标。 y 值将包含我们的目标值(仅未来收盘价)。
  2. visualize_training_results —这个函数将帮助我们评估刚刚创建的神经网络。我们在评估我们的 NN 时要寻找的东西是 收敛 。随着训练的进行,损失准确度的验证值和常规值必须开始与对齐。如果它们不收敛,那么这可能是过度拟合/欠拟合的迹象。我们必须回过头来修改神经网络的结构,这意味着改变层/节点的数量,改变优化函数等。
  3. layer_maker —这个函数构造了我们的神经网络的主体。在这里,我们可以自定义层数和节点数。它还有一个正则化选项,可以在必要时添加漏失层,以防止过拟合/欠拟合。
  4. validater —该函数使用特定日期范围的预测值创建 DF。这个范围随着每个循环向前滚动。范围的间隔是可定制的。我们使用这个 DF 来评估模型的预测,稍后将它们与实际值进行比较。
  5. val_rmse —该函数将返回模型预测值与实际值相比的均方根误差( RMSE )。返回值代表我们的模型预测的平均偏差。总体目标是降低我们模型预测的 RMSE。

拆分数据

为了适当地格式化我们的数据,我们需要将数据分成两个序列。这些序列的长度可以修改,但我们将使用过去 90 天的值来预测未来 30 天的价格。然后,split_sequence函数会将我们的数据格式化成适当的 Xy 变量,其中 X 包含过去 90 天的收盘价指标,而 y 包含未来 30 天的收盘价。

# How many periods looking back to learn
n_per_in  = 90# How many periods to predict
n_per_out = 30# Features 
n_features = df.shape[1]# Splitting the data into appropriate sequences
X, y = split_sequence(df.to_numpy(), n_per_in, n_per_out)

我们的神经网络将对这些信息做的是了解过去 90 天的收盘价和技术指标值如何影响未来 30 天的收盘价。

神经网络建模

现在我们可以开始构建我们的神经网络了!下面的代码是我们如何用自定义层和节点构建神经网络的。

这是我们开始试验以下参数的地方:

  • 层数
  • 节点数量
  • 不同的激活功能
  • 不同的优化器
  • 时期数和批量大小

我们为这些参数中的每一个输入的值都必须被探究,因为每一个值都会对整个模型的质量产生显著的影响。可能有一些方法可以找到每个参数的最佳值。对于我们的例子,我们主观地测试了每个参数的不同值,我们发现的最佳值可以在上面的代码片段中看到。

如果你希望了解更多关于这些变量背后的推理和概念,那么建议你阅读我们之前的 关于深度学习和比特币 的文章。

可视化损失和准确性

训练后,我们将使用自定义助手函数来可视化神经网络的进展:

visualize_training_results(res)

随着我们的网络训练,我们可以看到损失减少和准确性增加。一般来说,随着历元数量的增加,我们会寻找两条线 汇聚或 对齐。如果他们没有,那么这是一个迹象,表明该模型是不充分的,我们将需要回去,改变一些参数。

模型验证

我们评估模型预测质量的另一种方法是对照实际值进行测试,并使用我们的自定义帮助函数val_rmse计算 RMSE。

在这里,我们将预测值与实际值绘制在一起,看看比较的效果如何。如果预测值的曲线非常偏离实际值,那么我们知道我们的模型是有缺陷的。但是,如果这些值看起来非常接近,并且 RMSE 非常低,那么我们可以得出结论,我们的模型是可以接受的。

我们的模型似乎在开始做得很好,但它不能捕捉或模拟价格的剧烈波动。这可能是为什么最后三个预测看起来非常遥远。也许通过更多的训练和实验,我们的模型可以预测这些运动。

预测未来

一旦我们对模型的表现感到满意,我们就可以用它来预测未来的值。这一部分相对于我们已经做的事情来说相当简单。

这里我们只是从下载的.csv文件中预测出最近的值。运行代码后,我们将看到以下预测:

下个月间谍价格预测

我们做到了!—间谍的预测价格。用这些知识做你想做的事情,但是记住一件事:股票市场是不可预测的。这里预测的值是而不是确定的。它们可能比随机猜测要好,因为这些值是基于过去的技术指标和价格模式的有根据的猜测。

关闭

我们能够成功地构建一个 LSTM 层的递归神经网络,它能够接受多个输入而不是一个。模型的质量可能因人而异,取决于他们希望在模型上花费多少时间。即使预测未来是不可能的,但这些预测对于那些希望了解股票未来价格走势的人来说非常有用。但是,它很可能认为这种方式比随机猜测要好。

在 eodhistoricaldata.com 另外出版

来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

资源

[## Marcos an 93/价格预测者

使用时间序列模型:萨里玛和 FB 先知预测比特币价格:使用递归神经网络:LSTM…

github.com](https://github.com/marcosan93/Price-Forecaster) [## 我尝试了深度学习模型来预测比特币价格

使用神经网络预测比特币价格

towardsdatascience.com](/predicting-bitcoin-prices-with-deep-learning-438bc3cf9a6f) [## 我是如何为比特币的技术指标编码的

使用 Python 创建比特币的技术指标

towardsdatascience.com](/technical-indicators-on-bitcoin-using-python-c392b4a33810) [## 如何开发用于时间序列预测的 LSTM 模型-机器学习掌握

长短期记忆网络,简称 LSTMs,可用于时间序列预测。有很多种…

machinelearningmastery.com](https://machinelearningmastery.com/how-to-develop-lstm-models-for-time-series-forecasting/)

使用深度学习进行端到端多类文本分类

原文:https://towardsdatascience.com/using-deep-learning-for-end-to-end-multiclass-text-classification-39b46aecac81?source=collection_archive---------4-----------------------

图片由 Anrita1705 来自 Pixabay

你有没有想过有毒评论是如何在 Quora 或 Reddit 这样的平台上被自动标记的?或者邮件如何被标记为垃圾邮件?或者是什么决定了向你展示哪些在线广告?

以上都是文本分类在不同领域应用的例子。文本分类是自然语言处理(NLP)中的一项常见任务,它将长度不定的文本序列转换成单一类别。

从上面的例子中出现的一个主题是所有的都有一个二进制的目标类。比如要么评论有毒没毒,要么评论假没假。简而言之,只有两个目标类,因此称为二进制。

但情况并非总是如此,有些问题可能有两个以上的目标类。这些问题被方便地称为多类分类,这就是我们在这篇文章中要关注的问题。多类分类的一些例子包括:

  • 评论的情绪:积极、消极或中立(三类)
  • 新闻按类型分类:娱乐、教育、政治等。

另外,如果你对在工作中使用的定制深度学习工作站或服务器感兴趣,Exxact Corporation 有一系列基于人工智能的解决方案,起价为 3700 美元,带有几个英伟达 RTX 30 系列 GPU,3 年保修和深度学习软件堆栈。

在本帖中,我们将使用各种深度学习方法来解决一个多类文本分类问题。

数据集/问题描述

在这篇文章中,我使用了来自 Kaggle 的 UCI ML 药物评论数据集。它包含超过 200,000 个患者药物评论,以及相关条件。数据集有许多列,但是我们将只使用其中的两列来完成 NLP 任务。

所以,我们的数据集看起来像这样:

任务:我们希望根据药物综述对主要疾病进行分类。

word2vec 嵌入入门:

在我们进一步研究文本分类之前,我们需要一种在词汇表中用数字表示单词的方法。为什么?因为我们大部分的 ML 模型都需要数字,而不是文本。

实现这个目标的一种方法是使用单词向量的一次性编码,但这不是正确的选择。给定大量的词汇,这种表示将占用大量的空间,并且它不能准确地表达不同单词之间的相似性,例如如果我们想要找到数字单词 x 和 y 之间的余弦相似性:

给定独热编码向量的结构,不同单词之间的相似度总是为 0。

Word2Vec 通过为我们提供一个固定长度(通常比词汇表大小小得多)的单词向量表示来克服上述困难。它还捕捉不同单词之间的相似性和类比关系。

单词向量的学习方式允许我们学习不同的类比。这使我们能够对单词进行代数运算,这在以前是不可能的。

比如:什么是王者——男人+女人?结果是皇后。

Word2Vec 向量也帮助我们找到单词之间的相似性。如果我们寻找与“好”相似的词,我们会发现很棒,很棒,等等。word2vec 的这一特性使得它在文本分类中具有不可估量的价值。有了这个,我们的深度学习网络就明白了“好”和“伟大”是意思相近的词。

简单地说,word2vec 为单词创建了固定长度的向量,为字典中的每个单词(和常见的二元模型)提供了一个 d 维向量。

这些词向量通常是预先训练好的,并由其他人在像维基百科、推特等大型文本语料库上训练后提供。最常用的预训练词向量有手套和 300 维词向量的快速文本。在这篇文章中,我们将使用手套词向量。

数据预处理

在大多数情况下,文本数据并不完全干净。来自不同来源的数据具有不同的特征,这使得文本预处理成为分类管道中最关键的步骤之一。例如,来自 Twitter 的文本数据不同于在 Quora 或其他新闻/博客平台上找到的文本数据,每种数据都需要区别对待。然而,我们将在这篇文章中讨论的技术对于你在 NLP 的丛林中可能遇到的几乎任何类型的数据都足够通用。

a)清除特殊字符并删除标点符号

我们的预处理管道很大程度上依赖于我们将用于分类任务的 word2vec 嵌入。原则上,我们的预处理应该匹配在训练单词嵌入之前使用的预处理。由于大多数嵌入没有为标点符号和其他特殊字符提供向量值,所以我们要做的第一件事就是去掉文本数据中的特殊字符。

# Some preprocesssing that will be common to all the text classification methods you will see.import re

def clean_text(x):
    pattern = r'[^a-zA-z0-9\s]'
    text = re.sub(pattern, '', x)
    return x

b)清洁编号

为什么我们要用#s 代替数字?因为包括 Glove 在内的大多数嵌入都以这种方式对其文本进行了预处理。

小 Python 绝招: 我们在下面的代码中使用 if 语句来预先检查文本中是否存在数字,因为if总是比re.sub命令快,而且我们的大部分文本都不包含数字。

def clean_numbers(x):
    if bool(re.search(r'\d', x)):
        x = re.sub('[0-9]{5,}', '#####', x)
        x = re.sub('[0-9]**{4}**', '####', x)
        x = re.sub('[0-9]**{3}**', '###', x)
        x = re.sub('[0-9]**{2}**', '##', x)
    return x

c)消除收缩

缩写是我们用撇号写的单词。缩写的例子是像“不是”或“不是”这样的词。因为我们想标准化我们的文本,所以扩展这些缩写是有意义的。下面我们使用收缩映射和正则表达式函数来完成。

contraction_dict = {"ain't": "is not", "aren't": "are not","can't": "cannot", "'cause": "because", "could've": "could have"}def _get_contractions(contraction_dict):
    contraction_re = re.compile('(**%s**)' % '|'.join(contraction_dict.keys()))
    return contraction_dict, contraction_re
contractions, contractions_re = _get_contractions(contraction_dict)def replace_contractions(text):
    def replace(match):
        return contractions[match.group(0)]
    return contractions_re.sub(replace, text)*# Usage*
replace_contractions("this's a text with contraction")

除了以上技巧,你可能还想做拼写纠正。但是因为我们的帖子已经很长了,我们现在就离开它。

数据表示:序列创建

使深度学习成为 NLP 的首选的一件事是,我们不必从我们的文本数据中手工设计特征;深度学习算法将一系列文本作为输入,像人类一样学习其结构。由于机器不能理解文字,它们希望数据是数字形式的。因此,我们需要将文本数据表示为一系列数字。

为了理解这是如何做到的,我们需要了解一些关于 Keras Tokenizer 函数的知识。其他记号赋予器也是可行的,但是 Keras 记号赋予器对我来说是个不错的选择。

a)记号赋予器

简而言之,记号赋予器是一个将句子拆分成单词的实用函数。keras.preprocessing.text.Tokenizer将文本标记(拆分)成标记(单词),同时只保留文本语料库中出现次数最多的单词。

#Signature:
Tokenizer(num_words=None, filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
lower=True, split=' ', char_level=False, oov_token=None, document_count=0, **kwargs)

num_words 参数只保留文本中预先指定数量的单词。这是有帮助的,因为我们不希望我们的模型因为考虑不经常出现的单词而受到很多干扰。在真实世界的数据中,我们使用 num_words 参数留下的大多数单词通常都是拼写错误的单词。缺省情况下,记号赋予器还会过滤掉一些不想要的记号,并将文本转换成小写。

一旦适合数据,tokenizer 还保留一个单词索引(一个我们可以用来为单词分配唯一编号的字典),可以通过 tokenizer.word_index 访问。索引词典中的单词按出现频率排列。

因此,使用记号赋予器的全部代码如下:

from keras.preprocessing.text import Tokenizer## Tokenize the sentences
tokenizer = Tokenizer(num_words=max_features)
tokenizer.fit_on_texts(list(train_X)+list(test_X))
train_X = tokenizer.texts_to_sequences(train_X)
test_X = tokenizer.texts_to_sequences(test_X)

其中train_Xtest_X是语料库中的文档列表。

b)填充序列

通常,我们的模型期望每个文本序列(每个训练示例)具有相同的长度(相同数量的单词/标记)。我们可以使用maxlen参数对此进行控制。

例如:

train_X = pad_sequences(train_X, maxlen=maxlen)
test_X = pad_sequences(test_X, maxlen=maxlen)

现在我们的训练数据包含了一个数字列表。每个列表都有相同的长度。我们还有word_index,它是文本语料库中出现最多的单词的字典。

c)编码目标变量的标签

Pytorch 模型期望目标变量是一个数字,而不是一个字符串。我们可以使用来自sklearn的标签编码器来转换我们的目标变量。

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
train_y = le.fit_transform(train_y.values)
test_y = le.transform(test_y.values)

负载嵌入

首先,我们需要加载所需的手套嵌入。

def load_glove(word_index):
    EMBEDDING_FILE = '../input/glove840b300dtxt/glove.840B.300d.txt'
    def get_coefs(word,*arr): return word, np.asarray(arr, dtype='float32')[:300]
    embeddings_index = dict(get_coefs(*o.split(" ")) for o in open(EMBEDDING_FILE))

    all_embs = np.stack(embeddings_index.values())
    emb_mean,emb_std = -0.005838499,0.48782197
    embed_size = all_embs.shape[1]nb_words = min(max_features, len(word_index)+1)
    embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))
    for word, i in word_index.items():
        if i >= max_features: continue
        embedding_vector = embeddings_index.get(word)
        if embedding_vector is not None: 
            embedding_matrix[i] = embedding_vector
        else:
            embedding_vector = embeddings_index.get(word.capitalize())
            if embedding_vector is not None: 
                embedding_matrix[i] = embedding_vector
    return embedding_matrixembedding_matrix = load_glove(tokenizer.word_index)

一定要把下载这些手套向量的文件夹路径放进去。embeddings_index包含什么?它是一个字典,其中的键是单词,值是单词向量,一个长度为 300 的np.array。这部词典的长度大约是十亿。

因为我们只想要嵌入在我们的word_index中的单词,我们将使用来自我们的标记器的单词索引创建一个只包含所需嵌入的矩阵。

深度学习模型

1.TextCNN

在 Yoon Kim 的论文用于句子分类的卷积神经网络中首次提出了使用 CNN 对文本进行分类的想法。

表示:这个想法的中心概念是把我们的文档看作图像。但是怎么做呢?假设我们有一个句子,我们有maxlen = 70,嵌入大小= 300。我们可以创建一个形状为 70×300 的数字矩阵来表示这个句子。图像也有一个矩阵,其中各个元素是像素值。但是任务的输入不是图像像素,而是用矩阵表示的句子或文档。矩阵的每一行对应一个单词向量。

卷积思想:对于图像,我们移动我们的 conv。水平和垂直过滤,但对于文本,我们将内核大小固定为 filter_size x embed_size,即(3,300),我们将垂直向下移动卷积,同时查看三个单词,因为我们在这种情况下的过滤器大小为 3。这个想法似乎是正确的,因为我们的卷积滤波器不分裂字嵌入;它会查看每个单词的完整嵌入。此外,我们可以将过滤器的大小想象成一元、二元、三元等。因为我们正在分别查看 1、2、3 和 5 个单词的上下文窗口。

这里是 CNN 网络编码在 Pytorch 的文本分类。

class CNN_Text(nn.Module):    
    def __init__(self):
        super(CNN_Text, self).__init__()
        filter_sizes = [1,2,3,5]
        num_filters = 36
        n_classes = len(le.classes_)
        self.embedding = nn.Embedding(max_features, embed_size)
        self.embedding.weight = nn.Parameter(torch.tensor(embedding_matrix, dtype=torch.float32))
        self.embedding.weight.requires_grad = False
        self.convs1 = nn.ModuleList([nn.Conv2d(1, num_filters, (K, embed_size)) for K in filter_sizes])
        self.dropout = nn.Dropout(0.1)
        self.fc1 = nn.Linear(len(filter_sizes)*num_filters, n_classes)def forward(self, x):
        x = self.embedding(x)  
        x = x.unsqueeze(1)  
        x = [F.relu(conv(x)).squeeze(3) for conv in self.convs1] 
        x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x]  
        x = torch.cat(x, 1)
        x = self.dropout(x)  
        logit = self.fc1(x) 
        return logit

2.双向 RNN (LSTM/GRU)

TextCNN 很适合文本分类,因为它处理近距离的单词。比如它可以一起看《纽约》。然而,它仍然不能处理特定文本序列中提供的所有上下文。它仍然没有学习数据的顺序结构,其中每个单词都依赖于前一个单词或前一个句子中的一个单词。

RNNs 可以帮助我们。他们可以使用隐藏状态记住以前的信息,并将其与当前任务联系起来。

长短期记忆网络(LSTM)是 RNN 的一个子类,专门用于长时间记忆信息。此外,双向 LSTM 保持了两个方向的上下文信息,这在文本分类任务中非常有用(但是,它不适用于时间序列预测任务,因为在这种情况下我们无法看到未来)。

为了简单解释双向 RNN,可以把 RNN 单元想象成一个黑盒,它接受一个隐藏状态(向量)和一个字向量作为输入,给出一个输出向量和下一个隐藏状态。这个盒子有一些权重需要使用损耗的反向传播来调整。此外,相同的单元格应用于所有单词,以便句子中的单词共享权重。这种现象被称为重量共享。

Hidden state, Word vector ->(RNN Cell) -> Output Vector , Next Hidden state

对于像“你永远不会相信”这样的长度为 4 的序列,RNN 单元给出 4 个输出向量,这些向量可以连接起来,然后用作密集前馈架构的一部分。

在双向 RNN 中,唯一的变化是我们以通常的方式以及相反的方式阅读文本。所以我们并行堆叠两个 rnn,我们得到 8 个输出向量来附加。

一旦我们得到了输出向量,我们就将它们发送到一系列密集层,最后是 softmax 层,以构建文本分类器。

在大多数情况下,您需要了解如何在神经网络中堆叠一些层以获得最佳结果。如果性能更好,我们可以在网络中尝试多个双向 GRU/LSTM 层。

由于 RNNs 的局限性,例如不记得长期依赖关系,在实践中,我们几乎总是使用 LSTM/GRU 来建模长期依赖关系。在这种情况下,您可以将上图中的 RNN 单元格想象为 LSTM 单元格或 GRU 单元格。

以下是 Pytorch 中用于该网络的一些代码:

class **BiLSTM**(nn.Module):
    def __init__(self):
        super(BiLSTM, self).__init__()
        self.hidden_size = 64
        drp = 0.1
        n_classes = len(le.classes_)
        self.embedding = nn.Embedding(max_features, embed_size)
        self.embedding.weight = nn.Parameter(torch.tensor(embedding_matrix, dtype=torch.float32))
        self.embedding.weight.requires_grad = False
        self.lstm = nn.LSTM(embed_size, self.hidden_size, bidirectional=True, batch_first=True)
        self.linear = nn.Linear(self.hidden_size*4 , 64)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(drp)
        self.out = nn.Linear(64, n_classes)

    def forward(self, x):
        *#rint(x.size())*
        h_embedding = self.embedding(x)
        *#_embedding = torch.squeeze(torch.unsqueeze(h_embedding, 0))*
        h_lstm, _ = self.lstm(h_embedding)
        avg_pool = torch.mean(h_lstm, 1)
        max_pool, _ = torch.max(h_lstm, 1)
        conc = torch.cat(( avg_pool, max_pool), 1)
        conc = self.relu(self.linear(conc))
        conc = self.dropout(conc)
        out = self.out(conc)
        return out

培养

下面是我们用来训练 BiLSTM 模型的代码。代码注释的很好,请大家通读代码理解。你可能也想看看我在 Pytorch 上的帖子。

n_epochs = 6
model = BiLSTM()
loss_fn = nn.CrossEntropyLoss(reduction='sum')
optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.001)
model.cuda()

*# Load train and test in CUDA Memory*
x_train = torch.tensor(train_X, dtype=torch.long).cuda()
y_train = torch.tensor(train_y, dtype=torch.long).cuda()
x_cv = torch.tensor(test_X, dtype=torch.long).cuda()
y_cv = torch.tensor(test_y, dtype=torch.long).cuda()

*# Create Torch datasets*
train = torch.utils.data.TensorDataset(x_train, y_train)
valid = torch.utils.data.TensorDataset(x_cv, y_cv)

*# Create Data Loaders*
train_loader = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid, batch_size=batch_size, shuffle=False)

train_loss = []
valid_loss = []

for epoch **in** range(n_epochs):
    start_time = time.time()
    *# Set model to train configuration*
    model.train()
    avg_loss = 0\.  
    for i, (x_batch, y_batch) **in** enumerate(train_loader):
        *# Predict/Forward Pass*
        y_pred = model(x_batch)
        *# Compute loss*
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        avg_loss += loss.item() / len(train_loader)

    *# Set model to validation configuration -Doesn't get trained here*
    model.eval()        
    avg_val_loss = 0.
    val_preds = np.zeros((len(x_cv),len(le.classes_)))

    for i, (x_batch, y_batch) **in** enumerate(valid_loader):
        y_pred = model(x_batch).detach()
        avg_val_loss += loss_fn(y_pred, y_batch).item() / len(valid_loader)
        *# keep/store predictions*
        val_preds[i * batch_size:(i+1) * batch_size] =F.softmax(y_pred).cpu().numpy()

    *# Check Accuracy*
    val_accuracy = sum(val_preds.argmax(axis=1)==test_y)/len(test_y)
    train_loss.append(avg_loss)
    valid_loss.append(avg_val_loss)
    elapsed_time = time.time() - start_time 
    print('Epoch **{}**/**{}** **\t** loss=**{:.4f}** **\t** val_loss=**{:.4f}**  **\t** val_acc=**{:.4f}**  **\t** time=**{:.2f}**s'.format(
                epoch + 1, n_epochs, avg_loss, avg_val_loss, val_accuracy, elapsed_time))

培训输出如下所示:

结果/预测

import scikitplot as skplt
y_true = [le.classes_[x] for x **in** test_y]
y_pred = [le.classes_[x] for x **in** val_preds.argmax(axis=1)]
skplt.metrics.plot_confusion_matrix(
    y_true, 
    y_pred,
    figsize=(12,12),x_tick_rotation=90)

以下是 BiLSTM 模型结果的混淆矩阵。我们可以看到,我们的模型做得相当好,在验证数据集上有 87%的准确率。

有趣的是,即使在模型表现不佳的地方,这也是可以理解的。例如,该模型混淆了减肥和肥胖、抑郁和焦虑、抑郁和双相情感障碍。我不是专家,但是这些病确实感觉挺像的。

结论

在这篇文章中,我们介绍了用于文本分类的深度学习架构,如 LSTM 和 CNN,并解释了 NLP 深度学习中使用的不同步骤。

仍然有很多可以做的来改进这个模型的性能。改变学习速率、使用学习速率表、使用额外的特征、丰富嵌入、去除拼写错误等。我希望这段样板代码能为您可能面临的任何文本分类问题提供一个参考基准。

你可以在 Github 或者这个 Kaggle 内核上找到完整的工作代码。

如果你想了解更多关于自然语言处理的知识,我想从 DeepLearning.ai 中调出一门关于自然语言处理的优秀课程,一定要去看看。

我以后也会写更多这样的帖子。让我知道你对这个系列的看法。在 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。

此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。

这个故事最初发表于这里

使用组织学载玻片上的深度学习来筛选癌症生物标志物

原文:https://towardsdatascience.com/using-deep-learning-on-histology-slides-to-screen-for-cancer-biomarkers-4c595e93c3dc?source=collection_archive---------53-----------------------

深度学习能从基本的 H&E 染色中提取新的见解吗?

来源:作者

我们采访了来自 Pixel Scientia LabsHeather Couture ,她分享了她最近关于在 H & E 载玻片上使用深度学习来筛选癌症生物标志物的研究见解。

基因组分析和 H&E 染色简介

传统上,科学家分析疾病,如癌症,集中于单个基因(基因测试)。但今天,他们越来越多地使用基因组分析来研究构成癌症的所有不同基因,并提供该疾病的全面概况。

虽然这种基因组分析可以提供准确、具体的见解,通过识别正确的癌症亚型来帮助治疗疾病,但它也是缓慢而昂贵的。

相比之下,分析 H & E 污渍要简单得多,也便宜得多。这包括在标准显微镜下观察染色的组织或细胞样本。H & E 将细胞核变成蓝色,细胞质变成粉红色,这样病理学家可以更容易地解读载玻片。但是它提供了不同于基因组分析的信息。

Heather 利用深度学习,开发了基于 H&E 图像预测癌细胞的一些基因组属性的过程。这意味着病理学家可以更快、更经济地识别癌症生物标志物。

机器学习与快速、廉价的 H&E 染色相结合,可以实现更昂贵的个性化医学基因组测试的一些好处。图片来源:作者

希瑟为我们描述了这一过程:

“对于乳腺癌分析,我们预测肿瘤的分子特性。这可以用于个性化治疗。如果你知道肿瘤的确切亚型,这可能意味着一种特定的药物比另一种更可能有效。

通常,这些评估是通过基因组数据完成的。我们在北卡罗来纳大学的团队发现,我们可以从 H & E 组织切片——在显微镜下染色和成像的切片——预测其中一些特性。这不是一个完美的结果,但在投入时间和金钱进行完整的基因组测试之前,它可以作为一个筛选过程。”

[ 注册我们的时事通讯,了解研究人员如何将机器学习应用于他们的分析管道。]

机器学习可以做出医生做不到的预测

这不仅仅是因为机器通常效率更高,能够处理更大的数据集。基于容易获得的 H&E 载玻片预测分子性质根本不是人类可以做到的。正如希瑟所说:

病理学家可以使用一种不同类型的染色,并能够获得蛋白质生物标记。有很多方法可以做到这一点,这是实验室的标准,但不是来自 H & E,这是最常见的染色诊断方法。”

这并不意味着算法会取代人类。希瑟预言了一个人类和机器携手工作的世界。但正如她指出的那样,只有时间才能告诉我们这种伙伴关系将如何运作:

“这大概不应该是艾对病理学家吧。应该是 AI 和病理学家联合。他们是如何合作的?是不是 AI 先看图像,找到病理学家应该看的区域?AI 会检查病理学家的回答来寻找错误吗?或者他们做不同的任务:人工智能可以做一些平凡的事情——比如计数细胞,寻找细胞分裂,或者类似的一些小事——然后病理学家接管。”

这是一个快速发展的研究领域,所以希望我们能很快找到这些问题的答案。希瑟报道了最近的兴趣激增:

“一篇又一篇论文展示了使用机器学习进行生物标记分析的各种方法。仅在上周,《自然》杂志就有两篇论文对几种癌症的概念进行了测试。本周还有另一个也在使用机器学习来预测分子特性。因此,这一特殊领域似乎终于突然起飞了。”

深度学习如何推进 H&E 幻灯片的自动化分析

使用机器学习来自动化分析的想法并不新鲜,但深度学习的最新进展给该领域带来了新的生命。具体来说,深度学习最近彻底改变了图像分析领域,允许机器识别图像中的模式——包括 H & E 污渍。

Heather 描述了深度学习如何使团队从手工制作机器学习功能(一个缓慢、不准确的过程)过渡到将完整图像输入深度学习算法,然后自动识别有意义的片段:

“我们之前在皮肤癌上使用了手工制作的特征。我们将分割单个的细胞和细胞核,描述它们的形状、结构、排列等等。我们用它来试图预测不同的样本是否有突变——但我们做不到。我们根本没有得到有统计学意义的结果。

深度学习已经出现,但工具包还没有出现。没有张量流;没有 PyTorch。根据我的经验,从手工制作的功能到深度学习是造成差异的原因。我猜其他队也是如此。”

让人工智能模型进入临床:三大挑战

尽管这些进步令人兴奋,但仍有许多挑战需要克服。Heather 提醒我们,她的 H&E 研究可能还需要数年时间才能被临床采用:

“使用深度学习来预测分子生物标志物在很大程度上仍处于研究领域。离临床使用还差得远。这需要做更多的工作。”

任何进步都可能是一步一步来的。希瑟认为第一个真正的用例可能是一个简单的筛选工具,以建议人类病理学家下一步该做什么:

“结果可能是,‘这很可能是 x 亚型,没有 y 突变。’由此,病理学家可以决定进行更详细的筛查,或者支付基因组测试的费用。”

Heather 概述了在 H&E 幻灯片上的深度学习被广泛应用于临床之前仍然需要克服的一些主要障碍。

挑战 1:使用不同的实验室设备获得稳定的结果

鲁棒性——以可预测的方式跨不同设置执行的能力——通常是机器学习管道的一个挑战。对于 H&E 载玻片来说尤其如此,因为用于创建数据的设备是高度敏感的,并且不总是在不同的实验室之间标准化。

任何在真实世界环境中对 H&E 幻灯片使用深度学习的解决方案都需要能够处理这些差异。目前,如果不同实验室的测量结果不一致,那么根据一套设备的数据训练的模型将无法理解不同实验室的数据。正如希瑟所解释的:

“算法需要是健壮的。它需要推广到不同的扫描仪、不同的显微镜和不同的染色技术。H & E 染色的一个问题是,如果由不同的实验室进行,染色的强度可能会略有不同。

随着时间的推移,污渍可能会褪色,如果用不同的扫描仪扫描,效果会有所不同。应对这些挑战是需要解决的事情,但这是一个不小的问题。”

挑战 2:在小数据集上使用深度学习算法

与 Heather 研究的气候变化数据集相比,H&E 数据集通常很小。这对机器学习来说是一个问题,机器学习通常依赖于庞大的数据集(数十万个样本)来找到可概括的模式。正如希瑟所说:

“我们正在处理更小的数据集。在某些情况下,数百名患者可能被视为“大型”数据集。1000 是可以的,但有时我们只能得到 1000。这与机器学习的其他应用领域截然不同。”

样本数量往往很少,每个单独的样本也很“广”(包含大量信息),这给构建可靠的深度学习算法的挑战增加了额外的一层复杂性。然而,希瑟对克服这一挑战持乐观态度:

“你可以用不同的算法。这些图像很大,所以我们在每个图像中有更多的图像块。对于训练一个 CNN ,其实很棒。有些事情我们需要以不同的方式来做——比如我们不能一次将整个图像通过 GPU。我们需要将它分成更小的块,有时需要二级分类器来整合来自更小块的预测或特征。

但是有解决方案,这是一个活跃的研究领域。”

挑战 3:建立具有所需专业知识的团队

使用机器学习和 H&E 幻灯片需要几个领域的深厚和广泛的专业知识。作为一名顾问,希瑟经常发现自己在填补空白,做从咨询工作到编码机器学习模型的一切事情。她描述了她所做的各种工作:

“一方面,我的角色可能纯粹是一名顾问。我每周会见我的客户一次,帮助他们解决他们的问题,提出建议,或者给他们指出已经存在的图书馆和研究。

或者我可能会替他们编码:实现一个概念证明或者帮助他们调试一些东西。或者在另一端,我可能会领导他们的机器学习工作。我可能是他们团队中第一个机器学习的人,让他们起来运行,帮助他们建立一个团队。”

希瑟专门帮助公司建立概念证明——他们的目标是最终解决方案的精简版本。然后,她将这些交给机器学习工程师团队,他们可以将它们转化为生产化的解决方案。她说,一个常见的挑战是,公司从学术界雇佣的人可能没有使用过真实世界的数据集:

“作为课程的一部分,他们在学习时使用了学术基准数据集,但他们通常没有真正处理过真实数据的挑战。真实数据是有噪声的。不平衡。标签可能是错误的;地面真理可能是错误的。”

寻找具有重叠经验的多学科专家是一项挑战。但正如希瑟所说,这是必要的:

“构建这些解决方案需要来自许多不同领域的人:不仅仅是机器学习,还有病理学、遗传学、统计学,根据你正在做的事情,可能还有许多其他领域。”

使用深度学习从 X 射线图像中检测由 NCOV-19 引起的肺炎

原文:https://towardsdatascience.com/using-deep-learning-to-detect-ncov-19-from-x-ray-images-1a89701d1acd?source=collection_archive---------9-----------------------

开发一个初步的诊断模型,有可能对抗疫情病毒

左图:新冠肺炎正面 x 光片。 右图:链球菌感染。(均以 CC-NC-SA 授权)。两幅图像都显示肺炎。什么能把他们区分开来?图像是作为正在建设的开源数据集的一部分收集的。

3 月 11 日,在本文撰写的四天前,世界卫生组织(W.H.O .)宣布冠状病毒疾病 2019 (NCOV-19)为疫情,其特征是这种新型冠状病毒在全球范围内快速和全球性传播。由于各国政府争相关闭边境,实施接触者追踪,并提高个人卫生意识,以努力控制病毒的传播,不幸的是,由于每个国家实施这些政策的标准不同,在开发和部署疫苗之前,预计病毒的传播仍会增加。

由于预计全球每天的实际病例将会增加,限制诊断的一个重要因素是病毒病理学测试的持续时间,这些测试通常在市中心的实验室进行,需要耗时的精确度。这导致了严重的问题,主要是携带者不能更早地被隔离,因此他们能够在无限制流动的关键时期感染更多的人。另一个问题是当前诊断程序的高成本大规模实施。可以说,最脆弱的是发展中国家偏远地区的人们,他们通常拥有较差的医疗保健和诊断服务。单一感染可能对这些社区有害,获得诊断至少会给他们对抗病毒的机会。

城市和农村地区的差别不仅仅在于人口数量,还在于获得医疗资源的机会。在疫情的这个时代,这种接触的缺乏可能是致命的。(左:高波 via Flickr 。右:danboardviaFlickr)(都被-NC-ND 2.0 授权为 CC)

王等人的一项新研究。艾尔。展示了在计算机断层扫描(CT)扫描中使用深度学习来扫描新冠肺炎的前景,并且它已经被推荐为预先存在的诊断系统的实用组件。该研究在 1,119 次 CT 扫描中使用了初始卷积神经网络(CNN)的迁移学习。该模型的内部和外部验证准确率分别为 89.5%和 79.3%。主要目标是允许模型提取新冠肺炎的放射性特征。

虽然这项研究在他们的模型上取得了惊人的准确性,但我决定使用不同的架构来训练和实现一个模型,希望提高准确性。我决定使用胸片图像(CXR)而不是 CT 扫描,原因有三:

  1. 对人们来说,接受 CXRs 比接受 CT 扫描更容易,尤其是在农村和偏远地区。还会有更多的潜在数据可用。
  2. 在放射科医生和医疗专业人员无法控制病毒的情况下(例如,如果他们自己生病),人工智能系统对于继续实施诊断至关重要。
  3. 当该模型生效时,它可以通过测试初始阴性病例来最小化现有基于实验室的诊断中的假 negatives⁴问题。

与作为诊断来源的 CT 扫描相比,使用 CXRs 的主要障碍是缺乏来自新冠肺炎的可以视觉验证的细节。很难看到新冠肺炎症状,如肺结节,这在 CT 扫描中很容易看到。我想测试一个有足够层数的模型是否能检测出质量较低但更实用的图像中的特征。因此,我的模型是一个关于 Resnet CNN 模型是否可以使用相对便宜的 cxr 有效检测新冠肺炎的概念验证。

新冠肺炎肺部扫描数据集目前是有限的,但我发现的最好的数据集,我用于这个项目,来自新冠肺炎开源数据集。它包括从公开可用的研究中废弃的新冠肺炎图像,以及不同肺炎引起的疾病(如 SARS、链球菌和肺囊虫)的肺部图像。如果你有任何知识库可以接受的合适的扫描图像,以及它们的引用和元数据, 请贡献 来建立数据集,以改善依赖它的人工智能系统。

我只训练我的模型查看 cxr 的后前视图(PA) ,这是最常见的 x 射线扫描类型。我在一台 Resnet 50 CNN model 上使用了迁移学习(我的损失在 Resnet 34 上的几个时代后爆发),我总共使用了 339 张图像进行训练和验证。所有的实现都是使用 fastai 和 Pytorch 完成的。

预期警告:由于缺乏新冠肺炎图像,数据严重失真。数据被随机分成 25%后拍摄。测试集从一开始就设置为 78。图片作者。

在撰写本文时,由于缺乏可用的公共数据,数据严重失真(新冠肺炎有 35 张图像,非新冠肺炎有 226 张图像,包括正常和患病肺的图像)。我决定将所有非新冠肺炎图像组合在一起,因为我只有不同疾病的稀疏图像。在将数据随机分成 25%之前,我继续使用来自这个 Kaggle 数据集的健康肺部的 x 射线图像来增加标记为“其他”的 x 射线扫描的大小。训练集由 196 幅图像组成,验证集由 65 幅图像组成,测试集由 78 幅图像组成(完全由额外的数据集组成)。已验证外部数据集不包含开源数据集中的任何重复图像。所有图像都被调整为 512 x 512 像素,因为根据我构建不同分类器的经验,它的性能优于 1024 x 1024 像素的图像

左:第一次迭代的训练有两个时代。培训损失远远高于验证损失,这是不适应的明显迹象。右图:训练的第二次迭代。所有列都与左边的列相同。我运行了 30 个纪元。图片作者。

使用 fastai 的 fit_one_cycle 策略的实现运行最初的几个纪元表明,拟合不足从一开始就是一个问题。我决定增加历元的数量,并得到一个学习率的范围。在其余的训练中,我采用了一种积极的策略,即训练大量的周期,使用 fastai 的 lrfinder,重新调整学习率,然后继续训练更多的周期,直到训练损失下降到与验证损失相当。

图片作者。

最后,在几十次迭代之后,我设法将训练损失降低到与验证损失相似的水平,同时保持高精度。模型的最终内部验证准确率记录为 93.8%。

左图:模型在验证集上运行良好,有一些假阳性(FP)和假阴性(FN);右图:该模型在测试集上运行,fn 为零,但 FPs 为三。偏斜分布确实会影响真阳性的结果,需要更多的数据来验证。列表示与左混淆矩阵相同的标签。图片作者。

正如上面的混淆矩阵所示,该模型在验证集上的表现非常好,只有两种情况分别出现假阳性和假阴性。转到测试数据,在 78 个图像中,75 个被正确预测,这是 96.2%的外部验证准确性。虽然我们在测试数据中得到三个假阳性,但模型对所有这些情况的预测接近 50%。这表明它混淆了 x 射线图像的一些特征,但它并不能非常决定性地得出对新冠肺炎有利的结论。然而,当它预测出正确的阳性病例时,最令人惊讶的事情发生了。 该模型从未在这些图像上训练过,但仍然成功地以 99%的确定性预测出图像中的肺是新冠肺炎阳性

这是深度学习模型在分类新冠肺炎扫描时前所未有的准确性,但这只是一个明显缺乏 x 射线数据的初步实验,并且尚未得到外部健康组织或专业人士的验证。我将在未来针对假阴性对模型进行调优,尽管我更关心假阳性,目前数据的稀缺阻止了进一步的调优。

同样,该模型是一个概念验证,但如果进一步开发,这将有巨大的潜力来解决现有的有限诊断范围和未来可能的人力短缺。它可以帮助阻止这种疫情的传播,并创造新的方法来防止未来的。

为此,我让我的代码、模型和结果可供研究人员使用。我计划在更多的数据公开后,通过在更平衡的数据集上测试来更新模型。我还计划实现一个 GRAD-CAM 来直观地查看模型关注的特性。我希望我的模型能够在外部得到验证,并且不仅仅是一个宠物项目,尤其是在这个非常紧急的时候。如果有任何问题,或者如果你能帮我联系上某个组织或专业人士,可以让从这个模式中受益并改进这个模式,请随时联系我

  • 我在训练后意识到,很大一部分训练和测试图像来自儿科患者,这可能会影响模型在成人扫描上的性能。但其性能仍需更多数据验证。该模型还可以通过未来的培训进行改进。
  • 我发现其中一个新冠肺炎阳性图像也在训练集中,这意味着结果的准确性是无效的。然而,它仍然给出了其他三个新冠肺炎图像 99%的可能性。
  • 最近,我用细菌性和病毒性肺炎的图片测试了这个模型。 我目前正在等待更多的数据来进一步训练它。
  • 梅奥诊所:假阴性新冠肺炎检测结果可能导致虚假安全感
  • 注:我应该对阳性病例进行上采样,以解决这些图像的缺乏,我应该包括更多细菌性和病毒性肺炎的图像,以使其更加有力。如果你想改进这个模型,请这样做。
  • 注意:由于研究新冠肺炎的放射性标志物是一个活跃的研究领域,还有许多有待发现,我提醒大家不要解读结果,该模型已经确定了新冠肺炎独有的正确特征。

使用深度学习来…生成双关语?

原文:https://towardsdatascience.com/using-deep-learning-to-generate-puns-f000c76026c9?source=collection_archive---------39-----------------------

自然语言处理中最新方法的有趣应用,展示了该领域的发展历程。

自然语言处理(NLP)作为一个领域已经有了前所未有的发展(尤其是在过去两年中,由于 BERT 的出版)。虽然许多研究都集中在具有巨大影响的任务上(例如,问答、文本摘要),但我们应该始终记住,自然语言处理研究也有一些有趣的应用。比如生成双关语。

让我们深入研究一下何等人最近的一篇论文。这为双关语的生成提供了一个非常强大的模型。附言:如果你只是想看看这个模型最终产生的一些双关语,我已经在帖子的结尾包括了作者给出的双关语。

照片由皮埃特罗·詹Unsplash 上拍摄

数据集。首先要做的事情。像任何深度学习问题一样,生成双关语显然需要一个不小的数据集。现在,就我个人而言,我甚至不认为双关语的数据集存在。无论如何,作者发现了一个很好的双关语集合:来自 2017 年 SemEval task 的双关语数据集,包含 1099 个不同的双关语。尽管这只是数据集的一小部分,但它足以训练出一个性能可接受的模型。

昨天我吞下了一些食用色素。医生说我没事,但是我感觉我好像里面染了一点。(“染”vs“死”)

如何生成双关语?因此,对于这项任务,作者决定将重点放在同音字双关上(上面给出的例子)。在同音双关语中,目标是用发音相同但意义不同的替代词(“染色”)来替换双关语(在本例中为“死亡”)。为了产生这些双关语,作者提出了 3 个主要步骤:

  1. 给定一个双关语,从预先存在的句子语料库中找到包含该词的句子。对于这一步,它就像通过 BookCorpus 进行基本搜索一样简单,这是一个流行的数据库,里面有成千上万本书的内容。从包含双关语的句子列表中,该模型选择双关语最接近句尾的句子(这是基于作者的假设,即如果双关语“惊喜”出现得更晚,双关语会更好)。
  2. 用同音异义词替换双关语(这样就产生了双关语)。下一步也是关键的一步是用同音词替换双关语。例如,这里有一些作者在他们的论文中给出的谐音:“和平”和“块”,“面粉”和“花”,“等待”和“重量”。
  3. 从第一个单词中选择一个,用另一个能让双关语有意义的单词替换它。这可能是最重要的一步,因为双关语需要铺垫。以上面给出的例子为例:如果双关语是“昨天我吞下了一些鸡肉”,那就不好笑了。医生说我没事,但是我感觉好像里面染了一点。”如果没有早期提到的“食用色素”,这个双关语就没有什么意义,这也是这一步要解决的问题。通过包含早期的主题词,提供了一些上下文,这将设置后期双关词的揭示。为了做到这一点,作者使用了一个跳格模型来寻找与同音词相关的单词(例如,“屠夫”和“肉”,“船”和“费用”)。

下面是作者给出的这些步骤的示例演练:

产生双关语的主要步骤。注意,在这个例子中,“hare”是原始单词“hair”的同音字替换。图片来源于论文作者何等。

所以在这个例子中,第一步是找到一个包含单词“hair”的句子。该模型识别句子“该男子停下来理发。”接下来,原来的双关语(“头发”)被同音字(“野兔”)取代,产生了更新后的句子“那个人停下来剪了一只野兔。”但是这还没有意义,所以进入第三步。通过将“man”改为“greyhound”插入主题,并给出完整的双关语:

"灰狗停下来剪野兔."'

LOL 好双关。

结果呢?为了了解他们的模型是多么的微不足道,作者召集了土耳其机械工人。对于 150 个不同的双关语对(一个来自人类,一个来自他们的模型),每个双关语要求五个工人从三个方面对双关语进行评级:

  • 成功(是或否)。基本上不管给定的双关语实际上是不是一个双关语。
  • 有趣(从 1 到 5 分)。不言自明:衡量一个双关语有多好笑。在这方面,如果双关语没有任何意义,员工可以表示“不适用”。
  • 语法(从 1 到 5 分)。衡量一个双关语的语法程度。必须确保双关语语法正确!

事实证明,他们的模型相当不错——该模型在 10.7%的双关语的搞笑方面和 8%的双关语的语法方面超过了人类。考虑到生成双关语是一项相当困难的任务,我实际上对计算机能比人更好地生成一个双关语印象深刻,更不用说百分之十了。但无论如何,让我们看看模型错过了点。

模型所犯错误的分类。图片鸣谢论文原作者何等人。

该死,模特把双关语搞砸了!因此,基于作者的模型所犯错误的饼状图,看起来模型所犯的错误很大一部分来自于选择一个糟糕的双关语或不能选择一个相关的主题词来提供双关语的上下文。考虑到这些都是相当困难的问题(让我当场想出一个双关语,我可能会绞尽脑汁),对我来说,模型在这些问题上遇到麻烦是有道理的。

结论。因此,作者做了大量工作,提出了一个相对强大的生成双关语的模型。他们专注于通过用一个给定的句子替换两个单词来产生同音词双关语,其中一个是双关语/同音词,另一个是为双关语提供上下文。他们的结果非常好,并且在展示 NLP 方法的强大方面确实令人鼓舞。

展望未来,我相信研究人员将继续为他们的高性能算法寻找新的有趣的应用!

延伸阅读:

额外有趣的东西(报纸上的双关语列表)。[模型]表明作者明确提到双关语是由模型产生的。[人类]表明作者明确提到双关语是由人类产生的。否则我不确定是模型生成的双关语还是人为生成的双关语。

昨天我不小心吞下了一些食用色素。医生说我没事,但是我感觉我内心有点染(死)了。

老屠夫永远不会死,他们只会面对命运。

谈判者只是一个试图恢复正常生活的女人。

嗯,美食家做到了,他想,这最好是正确的。[型号]

为什么牛奶场会搅动?说得越少越好……[人类]

那是因为谈判者和平地把我的车还给了我。[型号]

生活是一个谜;在这里寻找遗失的和平(片)。[人类]

黄油想知道这两位姑娘是谁,圣粉(花)的新成员。[型号]

贝蒂·克罗克是一个面粉(花)孩子。[人类]

即使从外面看,我也能看出他已经瘦了一些。[型号]

耐心是一种等待的美德。[人类]

使用分布式机器学习对大数据进行高效建模

原文:https://towardsdatascience.com/using-distributed-machine-learning-to-model-big-data-efficiently-546491aec11e?source=collection_archive---------39-----------------------

图像来源

随着分布式计算成为数据科学家越来越受欢迎的技能,在 AWS EMR 集群上运行 Apache Spark 逐渐成为业内处理大数据的常用方式。

PySpark API 允许我们轻松地用 Python 编写 Spark,同时让 Spark 在后台并行计算数据。在本文中,我们将使用来自 Kaggle 的 4g 旧金山自行车共享数据集来实时模拟共享自行车的可用性。

这篇文章将涵盖:

  1. AWS EMR 或本地环境上的火花初始化
  2. 用 Spark 和 Plotly 进行探索性数据分析
  3. 使用 Spark SQL 对数据进行预处理
  4. 建模:在 Spark ML 中使用随机 Fores 回归器
  5. 为优化运行时间配置您的 AWS EMR 集群

1.AWS EMR 或本地环境上的火花初始化

要使用 spark,我们可以在 AWS EMR 集群上运行它,或者如果您只是想试用它,也可以在您本地的 Jupiter 笔记本上运行它。有很多关于如何在 AWS EMR 上设置你的笔记本以使用 PySpark 的文章,比如这篇。EMR 集群配置也将在很大程度上影响您的运行时,我将在最后一部分提到这一点。

2。探索性数据分析

为了预处理数据,我将使用 Spark RDD 操作来执行探索性的数据分析和可视化。

温度对订户的影响比对非订户的影响小

其余的 Spark 预处理代码Plotly 可视化代码可以在 Github repo 上找到,但这里是我们最初探索性分析的图表。

用户倾向于在工作日更频繁地使用共享自行车。

非订户每次出行使用自行车的时间要长得多,这可能是因为订户大多是通勤者,而不是游客。

用户的行程持续时间范围较小,集中在 10 分钟左右。

显然,你可以把共享单车带上火车

最常见的出行路线是通勤者在办公室和地铁站之间使用它。

3。使用 Spark SQL 对数据进行预处理

正如我们在步骤 2 中看到的,使用火花 RDD 进行数据预处理可能很难理解或可视化。这里,我们将使用 Spark SQL,它允许我们从 RDD 创建数据帧。

为此,我们首先创建一个模式,并用列名、数据类型和 nullable 定义 StructField。或者,您也可以在 RDD 上使用 toDF() 让 Spark 推断模式。

weather_df(显示在 Jupyter 笔记本中)

然后,我们可以使用像 select()、agg()、join() 这样的函数来操作 Spark SQL 数据帧,就像处理熊猫数据帧一样。

经过更多的数据预处理后,我们为机器学习模型准备好了数据框架。我们希望通过以下功能预测特定时间内给定站点的可用自行车数量:

○车站信息

○天气状况

○一天的类型

○一天中的某个小时

○该地区的人口

4。建模:使用 Spark ML 中的随机森林回归器

a)特征工程

PySpark 支持许多常用函数来做特性工程,我们将使用 StringIndexer() 来对我们的分类变量 rain_identifier 执行标签编码。

在将特性转换成数字类型后,我们可以使用 VectorAssembler() 将所有特性组合成一个向量列。这对于训练 ML 模型很有用,因为您将在后面的 RandomForestRegressor 中看到,它只接受一个 inputCol 参数。

现在,DataFrame 将我们所有的特征转换成一个向量,还有我们的目标变量:可用自行车的数量。

b)创建模型和模型评估

创建训练和测试集时,调用。cache() 将数据帧缓存到内存中以加速训练过程。(这里有一个关于什么时候在 Spark 中使用缓存的更全面的阅读。)

c)模型调整

Spark ML 还支持使用 CrossValidator()ParamGridBuilder() 进行交叉验证和超参数调优。

我也在全笔记本里玩过 Spark ML 的梯度提升回归器,决策树,线性回归,但是随机森林回归器在这些模型中表现最好。

5。配置您的 AWS EMR 集群以获得最佳运行时间

选择集群节点的实例类型和节点数量可能很棘手,这取决于数据的大小和代码所需的计算能力。根据 AWS 的文档

计算密集型集群可能受益于在高 CPU 实例上运行,这些实例的 CPU 比 RAM 多。数据库和内存缓存应用程序可能会受益于在高内存实例上运行。

规划集群实例的一种方法是用一组有代表性的样本数据运行一个测试集群,并监视集群中节点的利用率。

为了进行简单的比较,我运行了同一个 jupyter 笔记本,其中包括这四个不同 EMR 集群的所有预处理和 ML 建模代码。

我们可以看到,增加节点数量(每个节点代表 EMR 集群下的一个 EC2 实例)并不一定意味着内存优化实例的运行时间更短。

摘要

希望本文对您使用 Spark 处理 Python 中的大数据和大规模运行机器学习模型有所帮助。Spark SQL 和基于数据帧的 Spark ML API 也省去了我们处理 Spark RDD 的麻烦,因为我们可以将数据存储在数据帧中。

如果你对 SF 共享自行车可用性建模的预测结果和关键发现感兴趣,这里有 Github repo 。也可以在 LinkedIn 上给我发消息。

这是三藩市大学 MSDS 697 与黛安·伍德布里奇教授的一个小组项目。小组成员是阿坎卡莎。、刘品言、孙清怡和林海洋。

在 python 中使用 Docopt,这是最用户友好的命令行解析库

原文:https://towardsdatascience.com/using-docopt-in-python-the-most-user-friendly-command-line-parsing-library-5c6aeac14deb?source=collection_archive---------9-----------------------

来源:普里西拉·杜·普里兹Unsplash

在每个项目的运行文件中,将参数解析成 python 文件是必不可少的。本文向您展示了 Docopt 的实践

当你处理一个有很多参数的文件时,总是在 python 文件中改变这些参数是很费时间的,而且对用户也不友好。已经实现了一些库来解决这个问题,称为命令行解析库。在 python 中,它们是三种不同的解决方案: DocoptArgparseClick

在本文中,我将深入探讨 Docopt 的使用,而不是 Argparse 或 Click。我做出这个选择是因为在使用了所有这三种方法之后,我意识到 Docopt 是迄今为止将参数解析到文件中最用户友好的方式。

我展示 Docopt 库的方式是向您展示一个带有 setup.pyrequirements.txt 文件的 docopt.py 文件示例。

### docopt.py file '''
Usage:docopt.py square [options] [operation] [square-option]docopt.py rectangle [options] [operation] [triangle-option]operation:
   --area=<bool>       Calculate the area if the argument==True
   --perimeter=<bool>  Calculate the perimeter if the argument==Truesquare-option:
   --edge=<float>      Edge of the square. [default: 2]rectangle-option:
   --height=<float>    Height of the rectangle 
   --width=<float>     Width of the rectangle '''
from docopt import docoptdef area(args):
   """
   This method computes the area of a square or a rectangle 
   """   
   if bool(args['square']):
      if args['--edge']:
         area = float(args['edge']) ** 2
      else:
         print("You must specify the edge")
   elif bool(args['rectangle']):
      if args['--height'] and args['--width']:
         area = float(args['--height']) * float(args['--width'])
      else:
         print("You have forgotten to specify either the height or       the width or both")
   return area def perimeter(args):
   """
   This method computes the perimeter of a square or a rectangle 
   """
   if bool(args['square']):
      if args['--edge']:
         perimeter = float(args['edge']) * 2
      else:
         print("You must specify the edge")
   elif bool(args['rectangle']):
      if args['--height'] and args['--width']:
         perimeter = float(args['--height']) + float(args['--width'])
      else:
         print("You have forgotten to specify either the height or       the width or both")
   return perimeterdef main():
   args = docopt(__doc__) if args['--area']:
      area(args)
   elif args['--perimeter']:
      perimeter(args)
   else:
      print("command not found")if __name__=='__main__':
   main()

它们是 docopt.py 文件中的两部分:

  • 文档字符串中参数的定义
  • 您想要/需要调用的不同方法

参数定义

在用法部分,你写下你的命令行在终端中的调用格式。因此,如果我想在边长为 5 的正方形上调用面积运算,我将执行以下操作。:

python -m docopt.py square --area=True --edge=5 

如您所见,在“用法”部分,我们可以定义默认值,但是如果我们像使用“ — edge 参数那样在命令行中指定值,这些值将被覆盖。

所以让我们分解命令行:

  • docopt.py :是您想要运行的 python 文件的名称
  • 正方形矩形是我们需要在命令行中给出的两个必要参数,以正确运行它
  • 运算】:由两个不同的参数组成,分别是“—面积”和“—周长”。对于这两个参数,我们需要在命令行中输入的值是一个布尔值。' = < bool >'是可选的,但是给出了用户需要输入哪种元素的提示。
  • 方形选项】:由一个参数组成,默认值为

方法

在这一部分,我将通过''和'周边'的方法。但是在这样做之前,您注意到您需要导入 docopt 包,并且在此之前您需要安装它。然而,暂时忘记安装,我会给你一个干净的方法来做。

您会注意到,在这两种方法中,当我需要来自 docopt 的参数时,我需要将其转换为正确的类型。例如 args['square']是一个布尔值,但我需要显式地将其转换为布尔值。

然后,计算面积和周长的方法是非常基本的方法,但是是可选的方法。

然而,在文件的末尾有两行是正确运行该文件所必需的:

if __name__=='__main__':
   main()

现在我们已经创建了我们的 docopt.py 文件,我们可以创建一个 setup.py 文件,只要一个 requirements.txt 文件,使我们的项目更加用户友好。

一个 setup.py 文件是一个 python 文件,在其中你描述了你的模块向 Distutils 的分布,以便在你的模块上操作的各种命令做正确的事情。在这个文件中,您可以指定要运行项目中的哪些文件,以及运行项目所需的哪些包。

在进入 setup.py 文件之前,您的项目中需要有以下架构:

/docopt-demo
   docopt.py
   requirements.txt
   setup.py

这里有一个关于这个项目的 setup.py 文件的例子:

### setup.py file from setuptools import setupwith open("requirements.txt", "r") as fh:
    requirements = fh.readlines()setup(
   name = 'docopt-demo',
   author = 'JonathanL',
   description = 'Example of the setup file for the docopt demo',
   version = '0.1.0',
   packages = ['docopt-demo'],
   install_requires = [req for req in requirements if req[:2] != "# "],
   include_package_data=True,
   entry_points = {
      'console_scripts': [
         'docopt = docopt-demo.docopt:main'
      ]
   }
)

这是 requirements.txt 文件:

# Argument parser packagesdocopt==0.6.2

解释 setup.py 文件

你必须做的第一个元素是从 setuptools导入设置,但是这个包是 python 自带的,所以如果你的电脑上有 python,它就会存在。

然后,您需要创建一个所有必需包的列表,您将在安装模块的 install_requires 参数中解析这些包。

在设置模块中,您需要了解一些要素:

  • :应该和你所在的文件夹同名,所以对我们来说是‘doc opt-demo’
  • 在' console_script 中,您可以为特定文件指定快捷方式。在我们的例子中,不是在终端中键入:
python -m docopt square --area=True --edge=5

我们只需要输入:

docopt square --area=True --edge=5

事实上,我们将单词' docopt '与操作'doc opt-demo . doc opt:main'相关联,该操作启动 docopt.py 文件中的主函数。

解释要求. txt 文件

在这个文件中,您指定了正确运行项目所需安装的所有包。

您有两种选择来指定版本:

  • 要么你告诉确切的版本,你想使用,你会使用' == '符号
  • 或者您也可以使用像' > = '这样的操作符来指定包的旧版本

结论

在本文中,我将展示如何使用 Docopt python 包,这是一种将参数解析到文件中的非常好的方法。

我希望你在这篇文章中找到了你来这里的目的,并在接下来的图像识别之旅中与我在一起!

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你使用[我的链接](http://If you enjoy reading stories like these and want to support me as a writer consider signing up to become a Medium member. It's $5 a month, giving you unlimited access to stories on Medium. If you sign up using my link, I will earn a small commission and you will still pay $5. Thank you !! https://medium.com/@jonathan_leban/membership)注册,我将赚取一小笔佣金,你仍需支付 5 美元。谢谢大家!!

[## 通过我的推荐链接加入媒体-乔纳森·莱班

阅读乔纳森·莱班的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…

medium.com](https://medium.com/@jonathan_leban/membership)

PS:我目前是 Unity Technologies 的机器学习工程师,刚刚从加州大学伯克利分校的工程硕士毕业,如果你想讨论这个话题,请随时联系我。 这里的 是我的邮箱。

使用实验数据的点状图

原文:https://towardsdatascience.com/using-dot-plots-with-experimentation-data-d588a820235?source=collection_archive---------58-----------------------

这篇文章将展示如何用点状图以一种通俗易懂的方式来解释你的实验发现

数字实验一直是科学领域的事情。尽管实验分析师可能会花大量时间充实假设,并与利益相关者一起构思,但正确地完成实验需要对显著性测试有一个核心和基本的理解。此外,实验的结果必须以一种不会使我们合作的利益相关者困惑——并随之脱离——的方式进行翻译。这时,适当设计的数据可视化是必不可少的。

当我们试图接受或拒绝一个使用多个成功指标的假设时,事情会变得混乱。在我们的实验中,对于给定的变体,我们可能有几个想要评估的参数,但是由于缩放和/或标准化问题,将这些参数呈现在同一个图上可能会有问题。此外,我们可能设计出来的东西可能只有数据人能够理解,而当我们希望在一个组织中向具有不同技术能力的人分享发现时,这远不是更好的选择。一个很好的例子是直方图,它显示了给定指标下我们的实验桶的重叠分布。对我来说,这是一种很好的方式来说明独立集团之间的差异,但我被告知,它们对股东并不友好:

使用这种可视化的另一个缺点是,它将只代表一个度量的分布,当我们可能试图用多个性能测量的结果来评估我们的假设时。当然,我们可以应用一个方面并显示多个发行版,但是这会变得很混乱,这不是我们想要传播给大众的格式。

幸运的是,我在 BBC 的 数据科学团队的一位同事一直在研究一些易于解释的点状图,这些点状图以直观(我认为非常简洁)的方式分解了我们实验变体的性能!(图 1)这些图是用 Python 开发的,最初用于以一种方式说明新冠肺炎的比例症状,这种方式可以让你说“100 个冠状病毒感染者中有 47 个只表现出发烧症状,而不是咳嗽”:

图一。马特·克鲁克的新冠肺炎症状分析的点状图

现在,虽然这些图是在考虑 Covid 的情况下设计的,但在对最近结束的实验进行事后分析时,它们具有很好的实用性。当一个实验暂停时,您可能希望确定在给定的桶中有多少比例的用户触发了某些事件,并在实验条件之间比较这些比例。这些点状图是向利益相关者展示这些信息一种很好的方式。我将分解下面的过程,但在此之前,你需要克隆 中型点阴谋 Github 回购。如果你创建一个新的 Jupyter 笔记本,你可以使用下面的代码;确保您已经将它保存在与克隆的存储库相同的工作目录中。

首先导入必要的包进行分析:

%load_ext autoreload
%autoreload 2import pandas as pd
import os

现在创建一个字典,其中包含您要查看的事件的类,对于这个示例,我们将使用:' signed_in_page_view ',' signed_out_page_view ',' page_view '和' not _ fire '。字典将以类名作为键,并记录触发这些事件的人数。请注意,在激发的事件和事件量方面,您的实验将与此示例不同。以下数据是我们实验的变体 A(对照)的数据:

event_dict_control = {}event_dict_control['signed_in_page_view'] = 2000

event_dict_control['signed_out_page_view'] = 45000

event_dict_control['page_view'] = 45000

event_dict_control['didnt_fire'] = 8000

对变量数据执行相同的操作:

event_dict_variant = {}event_dict_variant['signed_in_page_view'] = 8000

event_dict_variant['signed_out_page_view'] = 35000

event_dict_variant['page_view'] = 75000

event_dict_variant['didnt_fire'] = 10000

查看两个词典:

event_dict_control>>> {'signed_in_page_view': 2000,
 'signed_out_page_view': 45000,
 'page_view': 45000,
 'didnt_fire': 8000}event_dict_variant>>> {'signed_in_page_view': 8000,
 'signed_out_page_view': 35000,
 'page_view': 75000,
 'didnt_fire': 5000}

对课程进行排序:

class_labels = [‘signed_in_page_view’, ‘signed_out_page_view’, 
 ‘page_view’, ‘didnt_fire’]

从 Github 文件夹中的相对路径位置导入点图包:

cwd = os.getcwd()
os.chdir('../src')
from dot_plot_code import create_dot_plot
os.chdir(cwd)

我们现在将渲染我们的标记点图,用于两个对照:

color_dict = {
 ‘signed_in_page_view’: ‘purple’,
 ‘signed_out_page_view’: ‘lightblue’,
 ‘didnt_fire’: ‘pink’,
 ‘page_view’: ‘grey’}dotplot_figure = create_dot_plot(
 class_labels=class_labels,
 data_dict=event_dict_control,
 color_dict=color_dict,
 reversed_rows=[0],
 ignore_labels=False)

和变体:

color_dict = {
 ‘signed_in_page_view’: ‘purple’,
 ‘signed_out_page_view’: ‘lightblue’,
 ‘didnt_fire’: ‘pink’,
 ‘page_view’: ‘grey’}dotplot_figure = create_dot_plot(
 class_labels=class_labels,
 data_dict=event_dict_variant,
 color_dict=color_dict,
 reversed_rows=[0],
 ignore_labels=False)

现在,我们已经成功地绘制了两种实验条件下每 100 人的事件分布图。由此我们可以总结如下:

在控制条件下,每 100 人中触发某个事件的用户数量。

在实验条件下,每 100 人中触发某个事件的用户数量。

可以看出,尽管相同比例的用户根本不触发事件,但在我们的实验条件下(变体),更多的人触发了页面视图事件,这表明我们的变体之间存在变化。类似地,在登录页面视图中有一个明显的差异,这表明我们的实验条件导致更多的用户在整个实验中登录。

当然,这些图表和汇总统计数据不能用来替代显著性检验或其他方法,它们只是一种向利益相关者解释我们实验条件差异的可翻译的方法。理想的情况是结合/在执行方差分析或任何相关的实验显著性测试之后使用这些点状图。此外,通过一个简单的图表,我们可以查看一个存储桶在多个事件中的表现,当考虑多个成功指标时,这为我们提供了一个更加综合的实验功效视图。

用 dotenv 隐藏 Python 中的敏感信息

原文:https://towardsdatascience.com/using-dotenv-to-hide-sensitive-information-in-python-77ab9dfdaac8?source=collection_archive---------14-----------------------

教程| Python | dotenv

隐藏您的密码和 API 令牌,使您的代码更加安全

一只藏起来的猫,就像你的证件一样!|照片由托马斯·博曼斯Unsplash 拍摄

在之前的帖子中,我在脚本中留下了一个不和谐令牌。这不是理想的编码方式,尤其是如果我不想每次共享代码时都重新生成令牌的话。在您的数据科学和编程项目中使用凭证和 API 令牌是不可避免的,但是暴露它们是可以避免的。使用dotenv python 模块可以帮助保护这些敏感信息免受窥探。

在之前的一篇文章中,我使用 python 中的 Discord.py 创建了一个 Discord 机器人。这个例子将在这里继续,但同样的想法可以用在任何使用敏感凭证或 API 令牌的代码中

[## 你好世界!如何使用 Discord.py 制作一个简单的不和谐机器人

使用 Python 在短短 30 分钟内运行您自己的不和谐机器人!

medium.com](https://medium.com/python-in-plain-english/hello-world-how-to-make-a-simple-discord-bot-using-discord-py-c532611681ba)

问题是

您已经完成了全新的 Discord bot,并希望与全世界分享您的代码。很自然,你把它上传到 GitHub,然后把链接发出去。很快机器人停止工作。为什么?您在代码中留下了 API 令牌,有人接管了您的 bot。

您将 API 令牌留在了代码中,有人接管了您的 bot

API 令牌为控制应用程序提供了巨大的能力。对于某些 API,使用情况会受到监控,甚至会对您的使用进行计费!幸运的是,Discord 不会向您收费,但是在您的编码项目中使用凭据时,您仍然需要保证它们的安全。

解决方案

输入环境变量。Jim Medlock 在他的环境变量介绍和如何使用它们中对它们有很好的解释。在那篇文章中,他说:

环境变量 是一个变量,其值在程序外部设置,通常通过操作系统或微服务内置的功能来设置。环境变量由一个名称/值对组成,可以创建任意数量的名称/值对,并在某个时间点提供引用。

我们将使用它们在 GitHub 提交中安全地保留我们的不和谐令牌。同样的过程也可以用于添加到 bot 中的任何其他敏感信息,甚至是您正在处理的其他项目。例如,如果你把你的用户名和密码放在机器人的代码中进行网页抓取,你会想用dotenv来保证它的安全。

装置

让我们从安装模块开始。我们可以使用 python 的包安装程序来完成这项工作,通常称为 pip。

$ pip install python-dotenv

我已经装了,就是这么好!

。环境设置

现在让我们设置我们的.env文件。该文件应始终位于项目文件夹的顶层文件夹或根目录中。在那个位置创建一个新的文本文件,将其重命名为.env。Windows 将要求您确认是否要更改文件类型。单击是。

如果 Windows 不让你命名。env,改为命名为 discord.env

接下来,用文本编辑器打开.env文件。我更喜欢 Atom 或者 Notepad++ ,但是 Notepad 也可以。按照约定环境变量全部大写,单词之间用下划线隔开。我们可以将我们的命名为 TUTORIAL_BOT_TOKEN 并粘贴我们的令牌。保存并关闭文件。

TUTORIAL_BOT_TOKEN=NzUzNzU3Mjg0NTI1NTM5MzQ4.X1q1LA.4xD7lSQrN0EqJivrrogLUScNdfY

添加到。gitignore

现在我的假设是你正在使用 GitHub 作为你的数据科学项目的版本控制。如果你不是,你应该是!如果出现问题,这是回滚代码更改的好方法。GitHub 还支持与其他人的协作。你不希望他们也有你的令牌,所以我们有一些工作要做。如果您没有为项目设置存储库或者不知道如何设置,不要担心,我将在另一篇文章中讨论这个问题。您可以安全地进入下一部分。

GitHub 将保存对代码的所有修改,但是我们需要排除.env文件,因为它包含了我们的令牌。为了确保.env文件没有被提交到存储库中,打开项目文件夹中的.gitignore文件并添加下面一行。

*.env

添加这一行将忽略我们之前创建的.env文件。通过使用星号(*),我们排除了顶级文件夹或根目录中扩展名为.env的所有文件提交到存储库。

加载环境变量

现在我们有了包含我们敏感信息的.env文件,它不会被提交到您的存储库中,我们需要在我们的 python 脚本中实际使用它。

首先我们需要加载dotenvos模块。对于这个例子,我们可以将它们添加到我的 Hello World Discord.py 教程的脚本中。

# Imports
from dotenv import load_dotenv
import os

load_dotenv将用于将.env文件加载到环境变量中。os将用于引用代码中的那些变量。先说load_dotenv。这将加载。环境文件。

# Credentials
load_dotenv('.env')

要使用环境变量,我们可以在 bot 脚本末尾的client.run命令中使用os.getenv

client.run(os.getenv('TUTORIAL_BOT_TOKEN'))

测试代码

现在一切都设置好了,在命令行中导航到 bot 脚本位置,运行脚本来启动 bot。

$ cd path\to\bot
$ python 02_Discord_dotenv.py

您将看到机器人名称和 ID 打印到控制台上。为了彻底起见,请使用 bot 进入服务器并测试!helloworld 命令,应该可以完美运行!

不出所料,该命令工作正常!

结束语

dotenv模块是隐藏敏感信息的好方法。它不必局限于与不和谐机器人一起使用。任何代码使用登录凭证、数据库连接信息、API 令牌或任何您不想与外界共享的东西的地方,请将其放在您的。环境文件。

要了解更多关于dotenv python 模块的信息,请查看 GitHub 知识库,其中包含更多关于该模块的文档。

寻找更多的项目创意?

看看我的另一篇文章,帮助你产生一些想法,在那里你可以应用这里讨论的概念。让您的项目在 GitHub 上脱颖而出,同时保证您的证书安全!

[## 让您的数据科学项目脱颖而出的 6 种方法

提高您的数据科学项目并获得关注的简单步骤

towardsdatascience.com](/6-ways-to-make-your-data-science-projects-stand-out-1eca46f5f76f) [## 通过我的推荐链接加入 Medium-Drew Seewald

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

realdrewdata.medium.com](https://realdrewdata.medium.com/membership)

用 dotenv 隐藏 R 中的敏感信息

原文:https://towardsdatascience.com/using-dotenv-to-hide-sensitive-information-in-r-8b878fa72020?source=collection_archive---------33-----------------------

教程| R | dotenv

隐藏您的密码和 API 令牌,使您的共享代码更加安全

一只藏起来的猫,就像你的证件一样!|图片由 Thomas BormansUnsplash 上拍摄

通常,当学习一个新的包或测试一些代码时,我会发现自己在代码中用纯文本编写了我的凭证和 API 令牌。这不是理想的编码方式,尤其是如果我不想每次共享代码时都重新生成令牌和更改密码。在您的数据科学和编程项目中使用凭证和 API 令牌是不可避免的,但是暴露它们是可以避免的。在 R 中使用dotenv包是保护这些敏感信息免受窥探的一种方式。

问题是

在之前的一个项目中,我使用了[mapsapi](https://cran.rstudio.com/web/packages/mapsapi/vignettes/intro.html)包来访问谷歌地图 API 。我有一个包含兴趣点、城市、州和国家的数据框,我想对其进行地理编码,或者获取其经度和纬度。为了从 Google Maps API 获取这些信息,我需要一个 API 令牌。这个 API 令牌是特定于我的,对于更高数量的 API 调用,您需要付费。

因此,假设您创建了一个类似的项目,并将其上传到 GitHub 。你很快就会看到你的信用卡上有一大笔费用。为什么?您将 API 令牌留在了代码中,有人滥用了您对 API 的访问,并对您的帐户收取了高额费用。

您将 API 令牌留在了代码中,有人对您的帐户收取了高额费用

API 令牌为控制应用程序提供了巨大的能力。对于某些 API,使用情况会受到监控,您将为您的使用付费!记住这一点,在您的编码项目中使用凭据时,确保它们的安全是非常重要的。

一个解决方案

输入环境变量。 Jim Medlock 在他的环境变量介绍和如何使用它们中对它们有很好的解释。在那篇文章中,他说:

一个 环境变量 是一个变量,它的值是在程序之外设置的,通常是通过操作系统或微服务内置的功能来设置的。环境变量由一个名称/值对组成,可以创建任意数量的名称/值对,并在某个时间点提供引用。

在这里,我将使用它们来使我的 Google Maps API 令牌安全地远离我的 GitHub 提交。同样的过程也可以用于添加到代码中的任何其他敏感信息。例如,如果您需要使用您的用户名或密码来访问一个数据库,您会想要使用类似于dotenv包的东西来保证它的安全。

注意:我说解决方案是因为做任何事情都有多种方法。这是我发现对我的项目简单而有用的一个

在开始之前

在我们开始之前,先快速浏览一下我们将使用环境变量改进的代码片段。它加载所需的包,以纯文本的形式定义 API 键,并向 API 发送一个针对places字符向量中每个位置的请求。

library(mapsapi)G_API_KEY <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"geo_obs <- mp_geocode(places, key=G_API_KEY)

我们的目标是消除在这个脚本中保留令牌的需要,但是仍然能够在我们的代码中使用它。

装置

让我们从安装包开始。我们可以使用install.packages()功能来实现。

> install.packages("dotenv")

。环境设置

现在让我们设置我们的.env文件。该文件应始终位于项目的顶层文件夹或根目录中。在那个位置创建一个新的文本文件,将其重命名为.env。Windows 将要求您确认是否要更改文件类型。单击是。

如果 Windows 不让你命名。env,将其命名为 cred.env,类似于

接下来,用文本编辑器打开.env文件。我更喜欢 Atom 或者 Notepad++ ,但是 Notepad 也可以。按照惯例环境变量都是大写的,单词之间用下划线隔开。我们可以将我们的命名为 GOOGLE_MAPS_API 并粘贴我们的令牌。保存并关闭文件。

GOOGLE_MAPS_API=XXXXXXXXXXXXXXXXXXXXXXXXXXXX 

注意:确保在这个文件的末尾创建一个空行,否则 R 在处理这个文件时会有问题

添加到。gitignore

现在这里的假设是,您在数据科学项目中使用 GitHub 进行版本控制。如果你不是,你应该是!如果出现问题,这是回滚代码更改的好方法。GitHub 还支持与其他人的协作。你不希望他们也有你的令牌,所以我们有一些工作要做。如果您没有为项目设置存储库或者不知道如何设置,不要担心,我将在另一篇文章中讨论这个问题。您可以安全地进入下一部分。

GitHub 将保存对代码的所有更改,但是我们需要排除.env文件,因为它包含我们的令牌。为了确保.env文件没有被提交到存储库中,打开项目文件夹中的.gitignore文件并添加下面一行。

*.env

添加这一行将忽略我们之前创建的.env文件。通过使用星号(*),我们排除了顶级文件夹或根目录中扩展名为.env的所有文件提交到存储库。

加载环境变量

现在我们有了包含敏感信息的.env文件,它不会被提交到我们的存储库中,我们需要在 R 脚本中实际使用它。

首先我们需要加载dotenv包。将此添加到示例脚本中。

library(dotenv)

load_dot_env()函数将用于将.env文件加载到环境变量中。该函数将在当前工作目录中寻找.env文件。

load_dot_env()

如果您必须将文件命名为除.env之外的名称,函数调用应该如下所示,用您的文件名替换cred.env

load_dot_env("cred.env")

为了在我们的脚本中使用环境变量,我们可以在我们的mp_geocode()函数中使用Sys.getenv()函数(或者在任何使用环境变量的地方)。

geo_obs <- mp_geocode(places, key=Sys.getenv("GOOGLE_MAPS_API"))

最后要做的事情是删除具有纯文本 API 键的行。之后,最终的脚本将如下所示:

library(mapsapi)
library(dotenv)load_dot_env()geo_obs <- mp_geocode(places, key=Sys.getenv("GOOGLE_MAPS_API"))

结束语

这个包是隐藏敏感信息的好方法。它不必局限于与 Google Maps API 一起使用。任何代码使用登录凭证、数据库连接信息、API 令牌或任何您不想与外界共享的东西的地方,将其放在您的.env文件中。

关于dotenv R 包的更多信息,请查看该包的 GitHub 库。它包含了更多关于这个包的文档。

[## 通过我的推荐链接加入 Medium-Drew Seewald

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

realdrewdata.medium.com](https://realdrewdata.medium.com/membership)

利用动态规划帮助特朗普赢得选举

原文:https://towardsdatascience.com/using-dynamic-planning-to-help-trump-win-the-elections-7b5b34f63961?source=collection_archive---------24-----------------------

Pixabay 上的 fajarbudi86 拍摄的照片

Python 中优化选举宣传的动态规划

说明:2020 年美国大选在本文中仅作为背景。这个故事意在向你展示计算机编程中的这种思维方式。我既不是在表达我的政治观点,也不是在用任何观点进行说服。本文中使用的所有数据都是由假设构成的,无论如何都不一定反映事实。

在我写这篇文章的时候,乔·拜登已经稳操胜券了。我既不是美国人,也没有在美国生活,所以我不会在政治上对此发表评论,尽管我认为大多数阅读我文章的人可能会支持拜登。我哪里知道?就看看加州吧:)

好的。现在我们想象一下,我们被聘为特朗普选举团队的一员。假设离投票开始还有 10 天。大家都知道,最重要的是争取摇摆州。我不太喜欢政治,所以我对这方面的知识有限,但幸运的是,我从维基百科上得到了被认为是 2020 年美国大选摇摆州的 11 个州。

我们就随便编个数据,把所有的州,他们的选举人票数量,以及在这个州推广需要的天数一起放在一个字典里。

states_dict = [
    {'name': 'Maine', 'votes': 5, 'days': 1},
    {'name': 'Nevada', 'votes': 6, 'days': 1},
    {'name': 'Minnesota', 'votes': 10, 'days': 2},
    {'name': 'New Hampshire', 'votes': 4, 'days': 1},
    {'name': 'Michigan', 'votes': 16, 'days': 3},
    {'name': 'Pennsylvania', 'votes': 20, 'days': 4},
    {'name': 'Wisconsin', 'votes': 10, 'days': 2},
    {'name': 'Florida', 'votes': 29, 'days': 5},
    {'name': 'Arizona', 'votes': 11, 'days': 2},
    {'name': 'North Carolina', 'votes': 15, 'days': 3},
    {'name': 'Georgia', 'votes': 16, 'days': 3},
]

假设是,如果我们在相应的州度过该天数,我们将赢得该州。

问题是川普在最后 10 天能拿到多少选举人票最多?所以,如果我们把所有红色州的选票加起来,我们就能估计出特朗普最终会有多少张选举人票。

有几种方法可以解决这个问题。最直观的方式大概就是递归函数了。然而,它绝对不是最好的解决方案。

在本文中,我将提供递归解决方案和动态规划解决方案。将给出解释来说明为什么动态规划比前者更先进。

递归解

Pixabay 上的 tkirkgoz 拍摄的照片

递归解决方案的思想相对简单。也就是说,对于每个州,我们有两个选择——去或不去。

因此,函数将是关于

在接下来的几天里从其他州获得最多的选举人票

如果我们选择去这个州,我们将在剩下的几天里得到选举人票+其他州的最高选举人票。否则,我们应该在接下来的几天里从其他州退回最多的选举人票。

代码如下:

为了防止 gist 有时不能很好地工作,我也将代码简单地发布如下:

def max_vote_recursive(states, days_left, index):
    # Terminating conditions
    if len(states) == 0 or index >= len(states) or days_left <= 0:
        return 0# If we have enough days, go to this state
    votes_if_go = 0
    if states[index]['days'] <= days_left:
        votes_if_go = states[index]['votes'] + max_vote_recursive(states, days_left - states[index]['days'], index + 1)# If we don't go to this state
    votes_if_not_go = max_vote_recursive(states, days_left, index + 1) return max(votes_if_go, votes_if_not_go)

如果我们运行函数为max_vote_recursive(states_dict, 10, 0),我们应该得到 56 张选举人票的结果。

为什么我说这个递归函数不够高效?我可以直观地展示给你看。

我所做的是将最后一个返回语句return max(votes_if_go, votes_if_not_go)改为下面的代码片段。

if votes_if_go > votes_if_not_go:
        print(f'There are {days_left} days left. We should go to {states[index]["name"]}')
        return votes_if_go
    else:
        print(f'There are {days_left} days left. We should not go to {states[index]["name"]}')
        return votes_if_not_go

因此,递归的每一步都将输出还剩多少天以及当前状态。更改之后,通过再次运行该函数,我们将得到如下所示的大量输出日志。

There are 2 days left. We should not go to Georgia
There are 2 days left. We should not go to North Carolina
There are 2 days left. We should go to Arizona
There are 2 days left. We should not go to Florida
There are 2 days left. We should not go to Wisconsin
There are 2 days left. We should not go to Pennsylvania
There are 1 days left. We should not go to Georgia
There are 1 days left. We should not go to North Carolina
There are 1 days left. We should not go to Arizona
There are 1 days left. We should not go to Florida
There are 1 days left. We should not go to Wisconsin
There are 1 days left. We should not go to Georgia
There are 1 days left. We should not go to North Carolina
...

请注意,在最初的几行中,我们已经可以找到一个重复的输出There are 1 days left. We should not go to Georgia。我所做的是将输出放入一个数组,然后计算有多少重复的输出。排序后的结果如下:

('There are 1 days left. We should not go to Georgia', 74),
('There are 2 days left. We should not go to Georgia', 63),
('There are 3 days left. We should go to Georgia', 51),
('There are 1 days left. We should not go to North Carolina', 45),
('There are 2 days left. We should not go to North Carolina', 41),
('There are 4 days left. We should go to Georgia', 40),
('There are 3 days left. We should not go to North Carolina', 35),
('There are 4 days left. We should not go to North Carolina', 29),
('There are 5 days left. We should go to Georgia', 28),
('There are 1 days left. We should not go to Arizona', 24),
('There are 2 days left. We should go to Arizona', 23),
('There are 5 days left. We should not go to North Carolina', 22),
('There are 3 days left. We should not go to Arizona', 21),
('There are 6 days left. We should go to Georgia', 19),
('There are 4 days left. We should not go to Arizona', 18),
('There are 3 days left. We should not go to Florida', 16),
('There are 6 days left. We should go to North Carolina', 16),
('There are 2 days left. We should not go to Florida', 15),
('There are 4 days left. We should not go to Florida', 15),
('There are 5 days left. We should go to Arizona', 14),
('There are 1 days left. We should not go to Florida', 13),
('There are 5 days left. We should go to Florida', 13),
('There are 7 days left. We should go to Georgia', 12),
('There are 6 days left. We should not go to Arizona', 11),
('There are 6 days left. We should not go to Florida', 11),
('There are 7 days left. We should go to North Carolina', 11),
...

请注意,更多的行被截断,但仍然非常明显的是,一些“子问题”重复了很多次。比如下面的子问题,被“计算”了 74 次。

我们还有一天时间,我们应该/可以去乔治亚州吗?

幸运的是,我们只有 11 个摇摆州。如果将这类问题转移到另一个领域,我们可能会有数量巨大的节点。递归函数的缺点是重复计算子问题,这可能使问题在合理的硬件资源下无法解决。

动态规划解决方案

照片由史蒂夫布Pixabay 拍摄

您可能已经意识到递归函数实际上是一种“自顶向下”的解决方案。相反,动态规划指的是以“自下而上”的结构解决问题的方式。它具有以下特点:

  • 要解决的问题必须分成子问题。
  • 在自下而上的推理过程中,我们必须确保每一步(子问题)在给定的条件下都是全局最优的。

我们先来看看代码。

万一要点加载有问题,这里是明码。请将它复制粘贴到您的文本编辑器中,以便进行漂亮的缩进。

def max_vote_dynamic_planning(states, total_days):
    dp_matrix = [[0 for days_left in range(total_days + 1)] for index in range(len(states) + 1)]for index in range(1, len(states) + 1):
        for days_left in range(1, total_days + 1):
            if states[index-1]['days'] <= days_left:  # If we have enough days left
                votes_if_go = dp_matrix[index-1][days_left - states[index-1]['days']] + states[index-1]['votes']
                votes_if_not_go = dp_matrix[index-1][days_left]
                # Save the maximum votes into cache
                dp_matrix[index][days_left] = max(votes_if_go, votes_if_not_go)
            else:  # We don't have any days left
                dp_matrix[index][days_left] = dp_matrix[index-1][days_left]

    return dp_matrix[-1][-1]max_vote_dynamic_planning(states_dict, 10)

然后矩阵计算如下,我们能得到的最大选举人票就是最后一个值——56。

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
 [0, 6, 11, 11, 11, 11, 11, 11, 11, 11, 11],
 [0, 6, 11, 16, 21, 21, 21, 21, 21, 21, 21],
 [0, 6, 11, 16, 21, 25, 25, 25, 25, 25, 25],
 [0, 6, 11, 16, 22, 27, 32, 37, 41, 41, 41],
 [0, 6, 11, 16, 22, 27, 32, 37, 42, 47, 52],
 [0, 6, 11, 16, 22, 27, 32, 37, 42, 47, 52],
 [0, 6, 11, 16, 22, 29, 35, 40, 45, 51, 56],
 [0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56],
 [0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56],
 [0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56]]

在这里,想法如下:

  • 初始化一个二维矩阵,使用状态数作为行,天数作为列。
  • 从第一个状态(索引= 1)开始,剩余天数为 1。
  • 如果我们去目前的州,我们应该把这个州的选举人票和剩下的日子里我们能得到的最大票数加起来。剩余天数应该是当前剩余天数减去我们在当前状态下需要提升的天数。

对于这一点,让我们假设我们正在计算第二个州,内华达州,还有 2 天(见dp_matrix[2][2])。如果我们选择去内华达州,我们会得到 6 张选举人票,但我们会用 1 天时间来得到它们。所以,我们还有 1 天时间。因为我们还剩 1 天,我们可以从前一个州缅因州得到的最多票数是 5 。因此,该步骤的优化结果是 11 票。

  • 如果我们选择不去当前状态,那么我们应该只使用我们已经为先前状态优化的最大票数。

你迷惑了吗?:)我举个例子,动态规划是怎么知道什么时候不应该去状态的。

假设我们正在计算明尼苏达州,还有 2 天(dp_matrix[3][2])。如果我们选择去明尼苏达州,我们会得到1016】张选票。然而,在那之后,我们还剩下2–2 = 0天。另一方面,如果我们选择不去明尼苏达州,我们还有 2 天,我们可以从前两个州获得的最高票数是 11

因此,随着 for 循环的进行,对于每一个单独的循环,我们可以确保它是到目前为止优化的。这也是为什么你可以看到矩阵是水平和垂直排序的。因此,全局优化数应该在右下角,即 56。

简单的比较

Pixabay 上的免费照片拍摄的照片

我不应该只谈论动态规划有多有效。让我展示一些证据。

这是递归函数的运行时间。

这是一个动态规划解决方案。

在这种特殊情况下,动态规划大约快 10 倍。如果我们有更多的节点和可能性的问题,差异将是巨大的。

摘要

cocoparisiennePixabay 上拍摄的照片

在这篇文章中,我提出了一个在有限的几天内优化选举宣传的问题。我们可以很轻松的用递归函数解决问题,但是事实证明效率不高,因为有太多的子问题被计算了多次。

不是使用“自上而下”的方法(递归),而是使用“自下而上”的方法解决问题的动态规划。它从最小的子问题开始,并确保每一步都在当前条件下给出优化结果,因此下一步将建立在当前步骤的基础上。因此,它没有重复计算,算法复杂度小得多。

在给定的问题中,表明动态规划解比递归函数解快 10 倍。

一个小笑话

照片由 www_slon_picsPixabay 上拍摄

当你告诉特朗普我们可以用 10 天时间拿到 56 张选举人票的时候,他好像挺开心的。

他问你:“好。那么,我们的行程是什么?哪些是我们应该去的州。”

你:“嗯,我暂时没有那个信息。让我调整一下我的代码……”

特朗普:“不,让我来。相信我。没有人比我更了解 Python!”

😃

[## 通过我的推荐链接加入 Medium 克里斯托弗·陶

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@qiuyujx/membership)

如果你觉得我的文章有帮助,请考虑加入灵媒会员来支持我和成千上万的其他作家!(点击上面的链接)

使用 face-api.js 构建一个在人脸图像上叠加一个遮罩的应用程序

原文:https://towardsdatascience.com/using-face-api-js-to-build-an-app-that-overlays-a-mask-on-face-images-9de9b4848c09?source=collection_archive---------31-----------------------

构建应用程序教程

下面是我如何制作一个应用程序,自动为你的个人资料照片添加遮罩。#马斯肯

来自 Pexels 的蒂姆·莫斯霍尔德的照片

你听说了吗? CDC医务总监现在建议并鼓励每个人在公共场合外出时戴口罩。不幸的是,戴口罩仍然会带来很多耻辱。“如果人们认为我生病了怎么办?”“如果他们冲我大喊大叫让我呆在家里怎么办?”这些想法会阻止一个人戴上面具,即使这有助于保护自己和他人。为了帮助宣传戴面具是明智和正确的事情,并让它变得足够普遍,从而成为一种新的社会规范,我决定制作一个应用程序,自动为你的个人资料照片添加面具。我是这样做的。

https://maskonme.herokuapp.com/待建 app 截图

首先,计划

我首先想到了应用程序的核心功能。这包括:

  1. 用户上传自己的照片或粘贴照片链接
  2. 网站检测照片中的人脸及其地标
  3. 网站在鼻子、嘴和下巴区域覆盖了一个面具
  4. 用户下载最终图像。

在谷歌了一番之后,我有了两个选择:1)使用 Flask & OpenCV 用 Python 开发一个应用,或者 2)使用 face-api.js 用 JavaScript 开发一个独立的应用。尽管我更喜欢 Python,但我还是决定使用 face-api.js & JavaScript,因为它可以在现代浏览器上运行,不需要后端服务器来承担繁重的工作。另外, Vincent Mühler 已经有了一个很棒的人脸和地标检测的例子,可以轻松覆盖步骤 1 和 2。

左)face-api.js 的人脸&地标检测 app 右)幽灵面具

接下来,我做了一个快速搜索,看看是否有任何在脸上覆盖面具的例子,并找到了使用 face-api.js 的https://ghost-masks . netlify . app,我认为只需要一些调整就可以完成第 3 步。

把所有的放在一起

faceLandmarkDetection 演示中,我花了一些时间来理解 face-api.js 是如何工作的。一些基础工作包括用<script src=”face-api.js”></script>在 head 部分加载 face-api.js,并找到加载输入图像和绘图覆盖的部分。

<div style="position: relative" class="margin">      
    <img id="inputImg" src="" style="max-width: 800px;" />      
    <canvas id="overlay" />    
</div>

此外,这里是调用faceapi来检测输入图像inputImgEl中的人脸并保存结果的部分。

async function **updateResults()** {
...
const results = await **faceapi.detectAllFaces**(inputImgEl, options).withFaceLandmarks()
...
}

我还在《幽灵面具》教程的 maskify.js 中指出了面具是如何被操纵和调整以放置在人脸之上的。它使用 face-api.js 中的方法来获取地标的坐标。例如,要得到鼻子,你可以运行const nose = landmarks.getNose();或者使用const jawline = landmarks.getJawOutline();. 得到下颌线,见这里的原始代码。利用这个,我能够快速制作出一个原型,它将检测人脸,并用一个面具覆盖它。点击这里查看完整代码!

[## jcheong0428/maskon

这个应用程序把一个面具放在你的脸上。申请可从 https://maskonme.herokuapp.com face-API . js 获得…

github.com](https://github.com/jcheong0428/maskon)

不幸的是,我很快意识到这个自动覆盖的面具不会是完美的。面部轻微的旋转和歪斜会导致稍微不准确的下颌线,这会使面具向某个方向倾斜太多。因此,我决定添加一些控件来移动遮罩。

还不错,但肯定可以使用一点手动调整。下颌线的估计并不是 100%准确。

事实证明,手动调整无论如何都是必要的,因为地标可能不会总是检测到下巴的整个轮廓。

因此,我决定添加按钮,这将改变小增量的面具的位置。

例如,我添加了一个向右移动蒙版的按钮

<button class=”waves-effect waves-light btn” onclick=”moveright_mask();”>Zoom In</button>

由函数定义

function moveright_mask() {
    const myNode = document.getElementById("maskdiv").children;
    for (var i = 0; i < myNode.length; i++) {
      var tableChild = myNode[i];
      // Do stuff
      var left = parseFloat(tableChild.style.left)+2;
      tableChild.style.left = `${left}px`;
    };
  }

这将更新maskdiv.中的掩码

亲自尝试一下或观看视频:

后续步骤

这是一个非常有趣的项目,可以在疫情开始的时候分散我的注意力。当我建造这个的时候,我最终也找到了 https://socialdistancing.works/的,它实际上做了同样的事情,除了它纯粹是手动调整。 MaskOnMe 的另一个很好的功能是它还可以同时检测多张脸,这样你就可以发布和每个戴面具的人的合影了!

当我有时间的时候,我的下一步是在一个 AR 版本上工作,其中一个将覆盖一个来自网络摄像头的面具!感谢您的阅读,如果您想了解更多关于人脸图像处理的内容,请随时查看我的其他帖子。

[## 如何从任何 Youtube 视频中提取面部表情、头部姿势和凝视

使用 Google Colab & OpenFace 从 Youtube 视频中提取面部特征的教程,无需安装一个…

towardsdatascience.com](/how-to-extract-facial-expressions-head-pose-and-gaze-from-any-youtube-video-2aa6590c2bb6) [## 量化时间序列数据之间同步性的四种方法

用于计算同步指标的样本代码和数据,包括皮尔逊相关、时滞交叉相关…

towardsdatascience.com](/four-ways-to-quantify-synchrony-between-time-series-data-b99136c4a9c9) [## 通过我的推荐链接加入 Medium 金贤昌博士

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

jinhyuncheong.medium.com](https://jinhyuncheong.medium.com/membership)

利用脸书的预言家预测超过 30000 件沃尔玛产品的销售(Kaggle 铜牌)

原文:https://towardsdatascience.com/using-facebooks-prophet-to-forecast-sales-of-over-30000-wallmart-products-kaggle-bronze-medal-c47aba5a5bc2?source=collection_archive---------31-----------------------

用脸书预言家预测沃尔玛的销售。亨特·哈里特在 Unsplash 上的照片

在这篇文章中,我将解释我是如何使用脸书的开源预测模型 Prophet 在 T4 M5 预测大赛上获得铜牌的。

什么是脸书先知?

很久以前我就想尝试一下脸书的先知。引用脸书的文档:“Prophet 是一种基于加法模型预测时间序列数据的程序,其中非线性趋势符合每年、每周和每天的季节性,加上假日效应。它最适用于具有强烈季节效应的时间序列和几个季节的历史数据。Prophet 对缺失数据和趋势变化具有稳健性,通常能很好地处理异常值。

简而言之,它是一个自动化的时间序列生成器。由于在这场 Kaggle 比赛中有超过 30,000 个时间序列,这个自动化方面真的很棒:数据太大了,无法单独查看每个时间序列。

如果您想跟随完整的笔记本,这里有两个链接:

卡格尔竞赛和数据

M5 预测竞赛为我们提供了沃尔玛的分层销售数据。目标是预测美国三个州(加利福尼亚州、德克萨斯州和威斯康星州)商店未来 28 天的日销售额。数据包括项目级别、部门、产品类别和商店详细信息。此外,我们还有解释变量,如价格、促销、星期几和特殊事件。

销售数据

我将销售数据放在每列一个产品和每行一天的格式中。

训练数据包含每个产品/商店组合的 30490 列,行是日期:

测试数据的格式与列车数据相同,但包含连续的 28 天:

在 Prophet 中添加假日和事件数据

我准备了假期和事件数据,以符合 Prophet 的假期论点。格式必须是固定列假日ds 的数据帧。在 ds 中,你给出了这个假期发生的日期的列表,包括过去和将来(直到你的预测发生)。

假日的 Kaggle 数据看起来像这样,每天一天的格式。

为了将该数据传递给 Prophet,必须对其进行重新格式化,以仅包含节假日,并为每个节假日列出该节假日发生的日期:

在 Prophet 中添加更多独立变量

还提供了价格数据。我没有把它包括在内,尽管在 Prophet 中添加更多数据是可能的。我相信它可以取得更好的结果,但由于时间限制,我没有尝试

脸书先知超参数

我用不同的方法对超参数做了两次尝试:第一次尝试使用默认的超参数,第二次尝试使用小的优化。我很想用更大的优化做第三次尝试,但是我没有计算时间来运行 30,000 次。

1.具有所有默认超参数的 Prophet

用 Prophet 运行 30,000 个时间序列花了我大量的时间(我有 6 个 Kaggle 笔记本,每个运行 5000 个时间序列,仍然花了我几个小时)。第一次提交使用了 Prophet 的所有默认设置。

如何使用 Prophet 的基础知识:

  • 使用假期数据(以准备好的形式)初始化 Prophet
  • 让先知适应训练数据
  • 制作一个未来的数据框架
  • 使用 Prophet 对未来数据框架进行预测

然后,我们可以将此应用于所有超过 30000 个时间序列,其中包括使用 imap,如下所示:

第一次提交给我的分数是 0.83(加权均方根误差)。虽然它在排行榜上是最差的 30%,但我对它的第一个结果很满意。

2.具有超参数调谐的 Prophet

我使用超参数调整进行了第二次提交。已经花了几个小时来运行默认值,并且有许多超参数和设置可以在 Prophet 中修复。

要在 Prophet 中调谐的超参数

经过一些尝试后,以下超参数似乎有所不同:

  • 每周季节性的傅立叶顺序
  • 月度季节性的傅立叶顺序
  • 年度季节性的傅立叶顺序
  • 季节性 _ 先验 _ 标度
  • 假日 _ 先验 _ 标度
  • 变点 _ 先验 _ 标度

对所有这些进行调优会使代码太慢。作为一个解决方案,我通过网格搜索只对 4 种不同的组合进行了调整,如下所示:

Prophet 中的交叉验证

Prophet 中有一种交叉验证的方法。然而,我更喜欢使用如下的列车验证测试方法:

  • 拟合训练数据的所有超参数组合
  • 测量验证数据的误差并选择最佳模型
  • 使用最佳模型的超参数对添加到验证数据的训练数据重新拟合模型
  • 使用此模型对测试数据进行预测

使用超参数构建一个模型的代码:

在一列上进行预测的代码,包括训练-验证-测试方法:

然后我使用和上面写的一样的 imap 方法来拟合所有的预测。

结论

网格搜索法让我有了显著的提高,新的分数是 0.68。这种方法用相对少的时间让我在比赛中获得了铜牌(5558 名中的第 501 名)。

记得看一下完整的笔记本:带默认值的* 先知笔记本 和带超参数调优的 先知笔记本 *

当然还有改进的空间,但我已经很乐意分享我所能做到的。希望这对你也有用。感谢阅读,不要犹豫,继续关注更多!

使用 FaceNet 在 Android 设备上进行人脸识别

原文:https://towardsdatascience.com/using-facenet-for-on-device-face-recognition-with-android-f84e36e19761?source=collection_archive---------7-----------------------

📱移动机器学习

利用 Android 的 FaceNet 和 Firebase MLKit 的强大功能。

哈里·坎宁安Unsplash 上拍摄的照片

随着对许多人进行即时识别和分类的需求增加,对人脸识别系统的需求与日俱增。无论是办公室的考勤系统,还是手机摄像头中的简单人脸检测器,人脸检测系统无处不在。对于边缘设备,这种需求甚至更大,因为它们有许多用途,如监控人群、机场乘客、公交车站等。

今天,我们将创建一个类似的人脸识别应用程序,完全从零开始。但是等等,有些特别的东西!

想象一下,你正在办公室里使用人脸检测系统。对于你办公室的 10 名员工来说,这个系统运行得非常好。但是,现在你的一个朋友来到了你的办公室。该系统可能无法识别您的朋友,因为它没有被编程这样做。所以,对于每一个新员工,你都需要修改系统并重新编程。

如果,

你不需要重新训练系统!为什么你不能保存这 10 名员工和你朋友的照片,而应用程序也能立即识别你的朋友呢?无需重新编写应用程序或任何其他系统,一切都可以在 Android 上运行!当你有新员工时,继续在单独的文件夹中添加他们的图像,应用程序就可以识别他们了。

直到故事结束,我们将创建这样一个应用程序!

GitHub 项目->

[## Shu bham 0204/face recognition _ With _ FaceNet _ Android

存储你想认识的人的图像,应用程序将使用这些图像对这些人进行分类。我们…

github.com](https://github.com/shubham0204/FaceRecognition_With_FaceNet_Android)

作者的项目/博客。

先决条件

在我们的应用程序中,我们将使用 CameraX、Firebase MLKit 和 TensorFlow Lite。如果你以前没有使用过这些库,一定要看看它们。

FaceNet 上的一点

1.将 Keras 模型转换为 TFLite 模型

FaceNet Keras 型号在n yoki-MTL/Keras-FaceNet回购上有售。下载完.h5模型后,我们将使用tf.lite.TFLiteConverter API 将我们的 Keras 模型转换成 TFLite 模型。

将 Keras 模型转换为 TFLite。

2.使用 CameraX 设置预览和 ImageAnalyser

为了实现一个实时的摄像机提要,我们使用 CameraX。我使用了官方文件中的代码。接下来,我们创建一个实现了ImageAnalysis类的FrameAnalyser类,它将帮助我们检索相机帧并对它们进行推理。

正在设置 FrameAnalyser 类。

我们所有的分类代码都将来自于analyze方法。首先,使用 Firebase MLKit,我们将得到相机帧中所有面的边界框(一个Bitmap对象)。我们将创建一个FirebaseVisionFaceDetector,它在一个FirebaseVisionInputImage对象上运行人脸检测模型。

实现 FirebaseVisionFaceDetector。

3.使用 FaceNet 生成人脸嵌入并进行比较。

首先,我们将使用我们的 FaceNet 模型生成人脸嵌入。在此之前,我们将创建一个助手类来处理 FaceNet 模型。这个助手类将,

  1. 使用我们从 Firebase MLKit 获得的边界框(如Rect)裁剪给定的相机帧。
  2. 使用标准化的像素值将此裁剪图像从Bitmap转换为ByteBuffer
  3. 最后,使用 TF Lite Android 库提供的Interpreter类将ByteBuffer提供给我们的 FaceNet 模型。

在下面的代码片段中,可以看到封装了上述所有步骤的getFaceEmbedding()方法。

为 FaceNet 实现一个助手类

现在,我们有了一个类,它将返回给定图像中所有人脸的 128 维嵌入。我们回到一个FrameAnalyseranalyze()方法。使用刚刚创建的 helper 类,我们将生成人脸嵌入,并将它们与我们已经拥有的一组嵌入进行比较。

在此之前,我们需要获得一组预定义的嵌入,对吗?这些嵌入指的是我们需要认识的人。因此,该应用程序将读取用户设备内部存储中的images文件夹。如果用户想要识别两个用户,即 RahulNeeta ,那么他/她需要在images文件夹中创建两个单独的目录。然后,他/她必须将 RahulNeeta 的图像放在各自的子目录中。

我们的目的是读取这些图像,并产生一个HashMap<String,FloatArray>对象,其中键(String)将主题的名字像拉胡尔尼塔和值(FloatArray)将相应的脸嵌入。通过查看下面的代码,您将对这个过程有所了解。

读取和生成设备存储器中存在的图像的嵌入。

下一步是将嵌入与合适的度量进行比较。我们可以使用 L2 范数或余弦相似性度量。使用metricToBeUsed变量选择 L2 范数或余弦相似度。我们计算每张图片的分数。然后我们计算每个用户的平均分数。平均分最好的用户就是我们的产出。

来自 README.md 的快照。

然后将predictions数组提供给boundingBoxOverlay类,该类绘制边界框并显示标签。在BoundingBoxOverlay.kt班。这里,我们使用两个Matrix来转换输出并显示在屏幕上。对于前置摄像头,我们必须翻转边界框的坐标,否则我们将在覆盖图中看到边界框的镜像。

显示边界框和标签。

结果呢

我试着用这个应用程序识别杰夫·贝索斯和埃隆·马斯克的脸,

应用程序的运行。

而且,我已经把图像存储在我的内部存储器中,

文件结构

更多资源

结束了

我希望你喜欢这个故事。感谢阅读!

使用 faiss 在多维空间中搜索

原文:https://towardsdatascience.com/using-faiss-to-search-in-multidimensional-spaces-ccc80fcbf949?source=collection_archive---------24-----------------------

如何围绕 faiss 构建服务并避免问题?

2019 年秋天, Avito 的自动审核团队推出了一项基于faiss 库的虚假广告检测服务。它帮助我们检测一个图像是否已经在另一个广告中使用过,即使它被以任何方式修改过——模糊、裁剪等。

我想谈谈我们在实施这项服务的过程中遇到的问题以及我们解决这些问题的方法。

在本文中,我将主要关注技术方面,假设您对多维空间中的搜索有所了解。

问题是

当我接到开发图像检索系统的任务时,我首先想到的是它的规格和局限性:

  1. 条目的数量。从一开始,我们有大约 1.5 亿个载体,现在有超过 2.4 亿个。
  2. 搜索时间限制。在我们的例子中,第 95 百分位为 300 毫秒。
  3. 内存限制。该指数必须足够小,以适应普通服务器,包括未来两年的预期增长。
  4. 可维护性。因为我们所有的服务都是在 Kubernetes 上实现的,所以我真的不想构建一个需要在物理硬件上运行的系统。
  5. 可以添加图像,但不能删除或修改。

我们系统的架构

因为我们需要足够快地在数亿个向量中搜索,穷举搜索不是一个选项——我们需要一个在 RAM 中的索引。为了适应那里的向量,它们需要被压缩。

一旦索引在 RAM 中,为了防止丢失,我们偶尔会制作备份副本并存储在外部。备份应该不需要将内存大小增加一倍,即使是暂时的,因为我们谈论的是几十千兆字节。

剩下的就是提供水平缩放。在我们的情况下,最简单的解决方案是相同的最终一致的索引。那么服务实例不需要知道彼此的存在,数据库将是唯一的同步点。

我们的服务是用 Python 3.7 编写的,PostgreSQL 用于存储向量,并且选择了一个 MinIO 作为备份存储。Faiss 作为一个索引库起着关键作用。

为了与外部世界进行交互,我们使用 avio http 框架,这是我们围绕 aiohttp 的内部包装器。

由于 asyncio 不能很好地处理我们备份所需的 fork 调用,并且在异步服务中调用长时间阻塞操作是不好的做法,所有与索引的交互都被转移到一个单独的进程中,我们使用多处理与它进行交互。管道

选择索引结构

为了压缩,我们决定使用乘积量化。这种方法允许我们将原始向量压缩到 64 字节。

产品量化允许我们将向量分割成多个部分,并建立独立的聚类。这些数字来自克里斯·麦考密克的一篇文章

为了加快搜索过程,我们选择了基于倒排文件HNSW 的混合方法。

左图来自红点游戏,右图来自于的文章。a .马尔科夫,D. A .亚舒宁

结果,我们得到了一个索引结构,用 faiss 的术语来说就是用神秘的线来描述:IVF262144_HNSW32,PQ64。这意味着我们为 262144 个簇创建一个反向文件,其中最接近的簇将使用具有 32 个邻居的 HNSW 来选择,并且所有向量都使用乘积量化方法压缩到 64 字节。

这里有链接到 faiss 的开发者提供的一个索引选择指南和一个基准测试。

看基准测试结果要注意的:

  1. 对索引进行一次查询以搜索 10,000 个向量,并给出每个向量的计时。
  2. 显示了 16 个 OpenMP 线程的时间。如果只使用一个线程进行搜索,时间会长 16 倍左右,因为 faiss 库内部的并行性是由#pragma omp parallel for 提供的。

估计内存成本

用相对值来估计内存开销是最合适的——每个向量的字节数。通过这种方式,我们可以识别内存泄漏(如果发生的话),并轻松地将它们与插入导致的有机增长区分开来。

存储器主要用于存储向量,在 IVF262144_HNSW32,PQ64 的情况下,每个向量为 80 字节:

  • 64 字节存储描述向量的代码。
  • 8 个字节存储 id(存储为 int64)。
  • 8 个字节来存储指向簇内向量的指针。

可以使用以下公式计算相对内存成本:

*int(faiss.get_mem_usage_kb() * 1024 / index.ntotal)*

预先计算的距离表会消耗一些内存,但是当估计的大小超过 2Gb 时,它不再自动计算。它的大小可以估计为 nlist × pq。M × pq.ksub × float。在我们的例子中,262,144 × 64 × 256 × 4 ≈ 17Gb,其中 pq。m 是乘积量化分量的数量,pq.ksub 是 256,因为可以用一个字节来描述这么多的簇。

估算内存成本要注意什么。以上数据仅为静态索引:如果添加和删除条目,内存消耗至少要乘以 2。我怀疑这是由于倒排文件中向量的动态内存分配,但我还没有找到支持这一假设的证据。如果你知道答案,请在下面的评论中分享。

推出服务后每个矢量的字节数的收敛:

如果您正在处理的问题不涉及删除和修改向量,您可以将它们中的大部分移动到静态索引归档中,只在较小的索引中插入新的向量,不时地将其与较大的索引合并,并向两个索引发送搜索查询。基本上,经典的信息检索方法。

查询并行化

尽管在 faiss 中使用了 OpenMP,但是发送到大型集群的任何长查询都会在相当长的时间内阻塞索引。为了避免系统不规则,我们使用ThreadPoolExecutor(faiss 是友好的,释放 GIL)。

nginx 原创图

为了让 faiss 在多线程环境中正确运行,需要做两件事。首先,不允许并行执行修改操作(添加、删除)和任何其他操作。其次,在执行读取查询期间限制 OpenMP 线程的数量,以使线程总数不超过指定的 CPU 内核限制。否则,性能损失是必然的。

把写负载和读负载分开最方便的方法是 RWlock ,它允许很多读者进入临界区,但只能有一个写者。对于 Python,我推荐这个包。使用 faiss.omp_set_num_threads 函数可以调整正在运行的 OpenMP 线程的数量。

为了获得最佳性能,以最大化每秒查询次数的方式选择每个线程的批处理大小是有意义的。我们使用的批量大小为 5。

值得注意的是,使用多线程时,内存消耗会略有增加。好像是个 bug(这里是各自的问题)。

有关多线程支持的更多信息,请查看这篇 faiss wiki 文章

带宽和操作时间

新的向量被添加到数据库中,每个实例每 5 秒钟从数据库中独立地读取这些向量,并以 10,000 个为一批(或者自上次更新以来累积的更小的一批)插入到索引中。索引中的插入速度高到足以让数据库成为瓶颈:大约每分钟 80 万个向量。

当我们优化延迟时,一个 20 核实例能够处理单个图像的 20 个最近邻居的多达 150 rps 的搜索,如果我们优化吞吐量,则大约 550 qps。负载伸缩非常简单:只需添加更多的实例,因为它们之间互不了解。

索引备份

因为索引在 RAM 中,所以如果机器停机,它可能会丢失。为避免此问题,请定期将其转储到外部存储。为此,我们使用 MinIO。

最方便的方法是创建一个 fork 并使用 Copy-on-Write 来获得一个索引的副本,该副本在内存消耗和对操作时间的影响方面几乎是免费的。然后将该索引保存到磁盘并上传到存储器。这里,我建议从静态索引大小开始——在我们的例子中,每个向量大约 80 字节。

因此,在启动应用程序时,您只需要从存储库中加载最新的副本,并从数据库中插入缺失的数据。

使用 GPU

我们还没有在产品中使用 GPU 搜索,但我做了一些测量来了解操作时间的比率。

数据: SIFT1M ,维度 128。
查询:我们正在为 10,000 个具有不同 nprobe 值的向量寻找 100 个最近邻。

你要注意的:

  1. 索引必须完全适合 GPU 内存。虽然有很多方法可以使用多个 GPU 进行搜索,但我认为这不是最好的方法,因为这样的解决方案更难维护。
  2. 平面索引在 GPU 上运行得更快,但它们仅适用于相对少量的数据(在 128 的维度上多达数千万个向量)。
  3. 使用 PQ64 索引时,GPU 只有在轮询大量集群时才有优势。

结论

Faiss 可能是当今最好的近似搜索开源工具,但像任何复杂的工具一样,它需要时间来适应。

尽管 faiss 文档非常好,但是最好还是通过不时地检查代码来学习它。您还可以通过 issues 向开发人员发送问题。通常,他们的反应相当快。

如果你正在考虑在预定的机器上部署这种系统,我建议看看 JD.com 的 vearch 系统。他们已经完成了大部分的脏活累活,并把他们的解决方案开源,尽管文档仍然非常简陋。

使用 FCNN 感受野进行物体检测

原文:https://towardsdatascience.com/using-fcnn-receptive-fields-for-object-detection-8101d7acecff?source=collection_archive---------44-----------------------

从 PyTorch 中预先训练好的图像分类器中创建一个物体检测器

目标检测的任务是检测和空间识别(使用边界框等)。)图像中的各种对象,而图像分类告诉图像是否包含某些对象,而不知道它们的确切位置。

在这篇文章中,我描述了一种在 PyTorch 中从预训练的图像分类模型创建对象检测模型的方法。

尽管物体检测和图像分类是计算机视觉的两个不同类别,它们的训练方法也完全不同,但我们将在这篇文章中看到,它们的功能有一些重叠,我们可以利用它们来达到我们的目的。

这篇文章中提出的想法和实现是使用 CNN 感受域反向传播全卷积网络的概念构建的。

如果你对上面的一些术语不确定,我推荐你去看看下面的LearnOpenCV.com的博客,其中T21 很好地介绍了这些概念。

任意大小图像上的全卷积图像分类

使用反向投影的 CNN 感受野计算

事实上,即使您已经非常熟悉这些术语,我仍然强烈建议您浏览一下上面的帖子,因为我所展示的实现扩展了上面的引用,并且为了简洁起见,我没有捕捉已经有了更好解释的细节。

虽然你可能会发现使用这种方法的结果不太匹配流行的对象检测器的性能,如 YOLO ,但是,这个练习的目的是描述一种方法,其中预训练的图像分类器可以用来创建对象检测器,而无需对带注释的边界框进行任何显式训练。

设定期望后,让我们开始吧。

一点背景

在这个练习中,我使用了 PyTorch 中提供的经过预训练的 Resnet-18 模型的稍微修改的版本。

具体来说,使用的模型是 Resnet-18 的变体,其中模型的最终(也是唯一的)全连接(或线性)层被 2D 卷积层取代,从而将模型转换为全卷积神经网络(FCNN)。

全卷积 Resnet18 模型架构如下所示:

FCNN Resnet-18(图片由作者使用 Netron 获得)

作为前面提到的博客中提出的概念的快速回顾,FCNN 将任意大小的图像作为输入,并将包含模型对图像中检测到的对象的预测的激活/响应图作为输出。

使用反向传播算法,我们计算该响应图的感受野,并找出被检测物体在原始图像中的位置。

正如你现在所知道的,神经网络中特征图(或层)中像素的感受野代表影响其值的来自先前特征图的所有像素。这是一个有用的调试工具,可以了解网络在给出某个预测时在输入图像中实际看到了什么。

下图说明了这一概念

CNN 感受野(图片由【洪诗】杜转自本文)

在以前的帖子中,我们只关注具有最大得分/概率的结果类别(我们毕竟只是想要一个图像分类器),这次我们将更深入地提取所有模型的预测,并计算每个相应的感受域,这将为我们提供边界框。

我们开始吧

我们的程序开始于我们获得 FCNN Resnet-18 模型在输入图像上预测的反应图之后,使用的是我在本文开头提到的这篇博客中捕获的步骤。我不会在这篇文章中重复这些步骤,因为这将是重复的努力,而且没有必要让这篇文章变得更长。

您应该记得,FCNN 的响应图形状为[1 x 1000 x n x m],其中 n 和 m 取决于输入图像和网络本身的宽度和高度。1000 对应于为 Resnet18 定型的 ImageNet 数据库的 1000 个类。

使用该响应图上的最大运算,我们从模型中获得顶部[n×m]预测或得分图,其可以被解释为在通过大小为 224×224(原始网络的输入图像大小)的滑动窗口获得的图像上的[n×m]个位置上执行的推断。

对于得分图中的每一个预测,我们都有概率(由 Softmax 层给出)和类别(来自 ImageNet)。

以下是形状[3 x 8]的分数图示例,其中突出显示了顶部预测:

得分图示例(图片由作者提供)

我们现在将检查这些[n×m]预测中的每一个,如果它的概率高于预定的阈值,则使用反向传播来计算它的感受野。

在这些感受野的每一个上,我们将应用 OpenCV 图像阈值和轮廓操作来获得边界框。

整个管道总结如下图所示:

FCNN 物体探测管道(图片由作者提供)

下面是实现的一个片段。从这里下载完整代码

以下是我们取得的成果示例:

样品结果(图片由 Sidekix 媒体Unsplash 上拍摄)

处理重叠边界框

需要注意的重要一点是,网络可能会多次检测到同一个对象(概率各不相同)。这导致重叠边界框的问题,即使在标准对象检测算法中这也是典型的。

为了解决这个问题,我们使用了非极大值抑制方法。我用过 imutils 包提供的函数object _ detection . non _ max _ suppression()。然而,可以使用实现相同目的的任何其他方法。

感受野计算选项

现在我想谈谈一个有趣的设计选择。

对于任何检测到的对象类别,其相应的感受野可以使用以下任一方法计算:

-最大激活像素

-净预测

不用说,根据我们上面的选择,最终的边界框会有很大的不同。

同样,相应的细节很好解释本博客

但是,我将对它们进行简要总结,并对这两种方法产生的结果进行比较。

选项-1: 为类别的最大激活像素计算的感受野,仅查看响应图中的一个像素,并提供原始图像中最大化该像素的区域的边界框。

在某些图像(例如低分辨率图像)上,该选项很好地隔离了类别的每个单独实例,如下图所示:

适当的分割(图片来源:Emmer &公司)

但是,在某些图像(例如高分辨率图像)中,这可能会返回一个边界框,该边界框仅包围对象的一部分,而不是整个对象,例如,参见下图:

不完全检测(由安德烈斯·达利蒙提Unsplash 上拍摄)

选项-2: 为净预测计算的感受野考虑了类别的相应响应图中的每个[n×m]像素,并提供了原始图像中使该类别的净预测最大化的区域的边界框。

和以前一样,这种方法的结果随着所用图像的不同而不同。

对于某些图像,它返回整齐地包围整个对象的边界框,如下图所示:

正确检测边界框(照片由 Andrés DallimontiUnsplash 上拍摄)

但是,在某些图像上,获得的边界框可能会将类别的几个实例封闭在一起,如下所示:

分割不当(图片来源:Emmer & Co. )

结论是什么?

如您所见,对于两个选项中哪一个会产生更好的结果,并没有明确的结论。

我建议读者使用他们自己的图像数据集进行进一步的实验,并获得更广泛的想法,了解什么对他们有效,什么对他们无效。

正如我最初提到的,这篇文章的目的不是介绍一种鲁棒的对象检测方法,而是探索如何使用一个特定的模型来达到另一个目的。

在机器学习中,模型的创建者发现他们的创造即使在从未被训练过的任务中也表现良好,这并不罕见。

带着这样的想法,我想结束这个博客。

我希望你喜欢它,我很高兴听到你可能遇到的任何进一步的想法和发现。

从这里下载完整的代码。

使用 leav 生成带有自定义工具提示的 Choropleth 地图(Python)

原文:https://towardsdatascience.com/using-folium-to-generate-choropleth-map-with-customised-tooltips-12e4cec42af2?source=collection_archive---------10-----------------------

用数据标签在地图上显示香港选民和人口数据

cyda 拍摄

Folium 是在交互式地图上可视化数据的一个非常好的软件包。在这个演示中,使用了香港选民统计人口数据。数据经过预处理后存储在 Consolidated Data.xlsx 中。随意摆弄数据集=)

可以直接从我的 Github repo 中克隆文件。

包裹

薄层

功能

folium建立在 Python 生态系统的数据优势和leaflet.js库的映射优势之上。用 Python 处理你的数据,然后通过folium在活页地图上可视化。

示范

  1. 用不同的图块生成简单的地图
  2. 准备定制的数据标签并在悬停时显示在地图上

数据

Consolidated Data.xlsx

照片由 cyda 拍摄

任务 1:用不同的图块生成简单的地图

hk_geo = r’hkg_adm1.geojson’
voter_proportion = data[['Eng_name','Proportion']]map1 = folium.Map([22.38, 114.15], tiles='cartodbpositron', zoom_start=10)tiles = ['stamenwatercolor', 'cartodbpositron', 'openstreetmap', 'stamenterrain']for tile in tiles:
    folium.TileLayer(tile).add_to(map1)

choropleth = folium.Choropleth(
    geo_data = hk_geo,
    name = 'choropleth',
    data = voter_proportion,
    columns = ['Eng_name', 'Proportion'],
    key_on = 'feature.properties.name_1',
    fill_color = 'YlGn',
    fill_opacity = 0.7,
    line_opacity = 0.2,
    legend_name = '2019 年選民登記比例', # Voter Proportion (%) in 2019
    highlight = True
).add_to(map1)folium.LayerControl().add_to(map1)# Display Region Label
choropleth.geojson.add_child(
    folium.features.GeoJsonTooltip(['name_1'], labels=False)
)map1

逐步解释:

hk_geo = r’hkg_adm1.geojson’

要在地图上绘制数据,需要一个地图 json 文件来定位位置。本演示中使用的 json 文件是从这个站点下载的。

这定义了底图的初始设置。

tiles = ['stamenwatercolor', 'cartodbpositron', 'openstreetmap', 'stamenterrain']for tile in tiles:
    folium.TileLayer(tile).add_to(map1)

Folium 支持在同一个地图中显示不同的地图框,下面是一些常见的地图框。您可以在每张图片的标题中找到瓷砖类型。

folium.LayerControl().add_to(map1)

此代码将图层选择按钮(显示在右上角)添加到地图,如果此代码丢失,地图将显示最后添加的图块,并且图块样式不能更改。

tiles = ' cartodbpositron ',照片由 cyda 拍摄

tiles=“雄蕊水彩”,照片由 cyda 拍摄

tiles='openstreetmap ',照片由 cyda 拍摄

tiles='stamenterrain ',照片由 cyda 拍摄

choropleth = folium.Choropleth(
    geo_data = hk_geo,
    name = 'choropleth',
    data = voter_proportion,
    columns = ['Eng_name', 'Proportion'],
    key_on = 'feature.properties.name_1',
    fill_color = 'YlGn',
    fill_opacity = 0.7,
    line_opacity = 0.2,
    legend_name = '2019 年選民登記比例', # Voter Proportion (%) in 2019
    highlight = True
).add_to(map1)

叶。Choropleth 根据数据“比例”将彩色层添加到地图中。“Eng_name”是要与 geojson 文件的位置进行映射的数据的关键字。请确保 json 文件中位置名称的拼写与数据中的关键字相同。

照片由 cyda 拍摄

任务 2:准备定制的数据标签并在悬停时显示在地图上

要向地图添加自定义数据标签,使用follow . features . geojsontooltip。通过使用此代码,带标签的值将被追加到 geojson 文件中。

照片由 cyda

首先,您需要研究 json 文件的结构,并将工具提示数据添加到“属性”中。上面的脚本还允许您找出要在leav 的 key_on 中使用的映射键。Choropleth

key_on='feature.properties.name_1',

在工具提示中,我想用中文显示地区名称和数据值。运行代码后,您将看到附加的工具提示数据。

# prepare the customised text
tooltip_text = []
for idx in range(len(data2)):
 tooltip_text.append(data2[‘Chi_name’][idx]+’ ‘+ 
 str(int(round(data2[‘18–35 (Proportion in 2019)’][idx]*100)))+’%’)
tooltip_text# Append a tooltip column with customised text
for idx in range(len(tooltip_text)):
    map_data['features'][idx]['properties']['tooltip1'] = tooltip_text[idx]

预期产出:

照片由 cyda

hk_geo = map_data
voter_proportion = data2[['Eng_name','18-35 (Proportion in 2019)']]map2 = folium.Map([22.38, 114.15], tiles='cartodbpositron', zoom_start=10)choropleth = folium.Choropleth(
    geo_data=hk_geo,
    name='選民比例',
    data=voter_proportion,
    columns=['Eng_name','18-35 (Proportion in 2019)'],
    key_on='feature.properties.name_1',
    fill_color='YlOrBr', # ‘BuGn’, ‘BuPu’, ‘GnBu’, ‘OrRd’, ‘PuBu’, ‘PuBuGn’, ‘PuRd’, ‘RdPu’, ‘YlGn’, ‘YlGnBu’, ‘YlOrBr’, and ‘YlOrRd’.
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='2019 年 18-35 歲選民 vs 18-35 歲人口比例',
    highlight=True
).add_to(map2)folium.LayerControl().add_to(map2)
choropleth.geojson.add_child(
    folium.features.GeoJsonTooltip(['tooltip1'], labels=False)
)map2.save('map2.html')
IFrame('map2.html', width=700, height=450)

绘图脚本类似于任务 1 中显示的脚本。在leav . features . geojsontooltip中,请选择您存储工具提示数据的变量名称。在我的例子中,它是“tooltip1”。 labels = False 隐藏变量名(即‘tool tip 1’)不与数据标签一起显示。

cyda 拍摄的照片

cyda 拍摄的照片

也有一些替代方法将数据标签添加到绘图中,但我发现这种方法更方便。如果你知道添加标签的其他方法,请随意评论。希望你喜欢我的文章=)

要了解更多关于使用 leav 的信息,请前往官方 Github 资源库

如果你觉得我的文章有用,请在我的 linkedIn 页面上为我的技能背书,鼓励我写更多的文章。

原载于cydalytics.blogspot.com

使用免费 API 从公共 IP 地址获取地理位置信息

原文:https://towardsdatascience.com/using-free-api-to-get-geolocation-information-from-public-ip-address-8fe23a88df9b?source=collection_archive---------36-----------------------

为娱乐而创作的项目

这篇文章演示了我使用免费 API 创建的一个项目。最初,目的是理解和学习更多关于 API 调用的知识。我在一家从事地理空间信息的公司实习。我在那里工作时得到了灵感。我将尝试简单地解释我使用的代码和方法,并从潜在的商业角度进行解释。同样,这是免费的,不需要额外的验证。我们开始吧!

这个项目的商业视角是确定人们访问网站的区域,基本上是受众细分。例如,一个电子商务网站想知道纽约有多少人在浏览他们的网站。因此,假设我是那家电子商务公司的老板,我在网上销售游戏设备,我想知道大概有多少人从城市或世界各地访问我的网站。我只有消费者的公开 IP 地址。现在,我想知道我的消费者来自哪个州和国家。这个 API 将从我的消费者的公共 IP 地址给我经纬度。从那里,我可以使用 geopy (名字)来获得地址,使用派生纬度和经度。很简单,对吧。既然思考和识别资源的工作已经完成,让我们开始学习一些 python 代码吧!

等一下,我没有所有消费者的公共 IP 地址,我只是在想象拥有一个游戏小工具公司。不用担心,Python 可以支持我的想象力,我们看看如何。

*import* random*import* socket*import* structsocket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))

如果您在一个 Jupiter 笔记本中运行上面的行,您将看到下面的输出。

好的,所有这些都很好,但是一次查找一个用户将会很耗时并且很困难,让我们制作一个自动化的 python 脚本,它将生成 100 个像这样的随机 IP 地址,查找它们的位置并将结果保存在一个 CSV 文件中。我在开发这个项目时使用了 VScode,但为了简单和易用,我将在最后提供一个包含源代码的 colab 笔记本。另外,我也会在最后给出 GitHub 的链接。

为此,我需要导入以下库。请求,json 是 API 调用所需要的,global_land_mask 用于验证纬度和经度(后面会讨论)。

import requestsimport pandas as pdimport numpy as npimport jsonimport randomimport socketimport structfrom geopy.geocoders import Nominatimimport osfrom global_land_mask import globe

如果您在导入库时遇到任何错误,只需在终端中使用以下命令。

pip install <library>  #(pip install global-land-mask)
or
conda install <library>

下面是生成 100 个随机 IP 地址的函数。

def getting_ip_address():
    """This function returns a list of random IP address"""
    new, explored=[],[]
    i=0
    while i<100:
        ip = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
        if ip in explored:
            continue
        else:
            new.append(ip)
        i+=1
    new = pd.DataFrame(new, columns=['ip'])
    return new

我还创建了一个存储生成的 IP 地址的列表,该列表稍后将用于检查该 IP 地址之前是否已经创建过。我已经把最终的名单转换成了熊猫的数据框架,因为和熊猫一起工作我会更舒服。现在,下一步将是识别和获取他们的纬度和经度信息。

def getting_ip(row):
    """This function calls the api and return the response"""
    url = f"[https://freegeoip.app/json/{row](https://freegeoip.app/json/{row)}"       # getting records from getting ip address
    headers = {
        'accept': "application/json",
        'content-type': "application/json"
        }
    response = requests.request("GET", url, headers=headers)
    respond = json.loads(response.text)
    return respond

这个函数获取每个 IP 地址,并向 API 发送请求,以获取经度和纬度信息以及其他信息。下面给出了一个位置的输出示例。

输出是 JSON 格式的,保存为 pandas 数据报中的一列。之后,从该列中可以推导出纬度、经度和时区等信息,如下所示:

def get_information():
    """This function calls both api and add information to the pandas dataframe column"""
    new = getting_ip_address()
    new['info'] = new['ip'].apply(lambda row: getting_ip(row))
    new['time_zone'] = new['info'].apply(lambda row: row['time_zone'])
    new['latitude'] = new['info'].apply(lambda row: row['latitude'])
    new['longitude'] = new['info'].apply(lambda row: row['longitude'])
    new['on_land'] = new.apply(lambda row: globe.is_land(row['latitude'],row['longitude']),axis=1)
    new = new[new['latitude']!=0]
    new = new[new['on_land']==True]
    new['address'] = new.apply(lambda row: getting_city_nominatim(row),axis=1)
    return new

这是主要的信息提取函数,它调用两个 API 并处理它们的信息。首先,记住上面显示的生成 100 个随机 IP 地址的函数,调用该函数并返回一个包含 IP 地址的数据帧。之后,使用 lambda 函数为每个 IP 地址调用 API,并将结果存储在名为“info”的列中。之后,从该列中为每个 IP 地址导出时区、纬度和经度。现在,它涉及到之前提到的 global_land_mask 库。我想检查一下纬度和经度是属于陆地还是海洋。这个 globe.is_land(latitude,longitude)为保存在另一个名为 on_land 的列中的每一行返回布尔值 True 或 False,稍后在 dataframe 上进行一个简单的过滤,以删除任何纬度是否正好为 0 以及纬度和经度是否在海洋中。然后调用 geopy Nominatim API 来获得更深的地址。调用 geopy API 的函数如下所示:

def getting_city_nominatim(row):
    """This function calls the geopy api and return the json address output"""
    try:
        lat = row['latitude']
        lon = row['longitude']
        geolocator = Nominatim(user_agent="my-application")
        location = geolocator.reverse(f"""{lat,lon}""")
        address = location.raw['address']
        return address
    except:
        print('timeout')

该函数使用从早期 API 调用中获得的纬度和经度来调用 geopy Nominatim。在“192.168.10.111”返回纬度和经度之前,相同的 IP 地址显示为一个示例,下图显示了一个使用该纬度和经度的示例 nominatim 调用。

现在,我已经准备好了所有的构建模块,我需要将结果保存在 CSV 中。其功能如下所示:

def append_to_existing_df(new):
    """This function appends the new ip addresses to the dataframe"""
    if os.path.isfile(f'{os.path.abspath("")}\location_of_ip_address.csv'):
        new.to_csv('location_of_ip_address.csv', mode='a', header=False,index=False)
    else:
        new.to_csv('location_of_ip_address.csv', mode='w', header=True,\
                   columns=['ip','info','time_zone','latitude','longitude','address'],index=False)def deleting_duplicate_entries():
    """This function makes sure there are no duplicate ip addresses saved in the csv file"""
    df = pd.read_csv('location_of_ip_address.csv')
    df.sort_values('ip',inplace=True)
    df.drop_duplicates(subset='ip',keep='first',inplace=True)
    df.to_csv('location_of_ip_address.csv',index=False)

上面的第一个函数查找是否存在 CSV 文件,如果该文件存在,则将结果追加到现有文件中,如果该文件不存在于该目录中,则创建一个新的 CSV 文件。os.path.abspath(" ")返回 python 文件所在的当前目录的路径。和 os.path.isfile()检查文件是否存在。在保存它们并多次运行程序后,我注意到几个 IP 地址包含相同的内容,因为最初的重复检查只针对 100 个随机生成的 IP 地址。但是现在,我已经运行该程序多次,同一个 IP 地址在多个地方被发现。为了消除这种情况,后来添加了一个 add on 函数,该函数读取 pandas 数据帧中的整个 CSV 并删除重复值,然后将文件保存回来。

现在,我们已经准备好了所有的函数,下面的代码块显示了调用之前创建的所有函数的主函数。

def main():
    """main function"""
    new = get_information()
    append_to_existing_df(new)
    deleting_duplicate_entries()if __name__ == '__main__':
    main()

运行后,这会创建一个 CSV,其结果如下所示:

这是我创建的项目。我只是概述了我是如何想出这个主意,创造材料,以及自动化一个过程的。我没有深究太多的技术细节。我打算为此单独写一篇文章。这可以进一步改进和修改,例如,进一步自动化该过程,以便它在每天的特定时间运行。这里显示的源代码在 colab 中给出。另外,在 GitHub 中也有。

非常感谢您阅读这篇文章。这是我第一次为媒体写作,我会努力提高我的写作水平,并经常发表一些项目和想法。

利用 GBIF 数据和 GeoPandas 绘制生物多样性趋势图

原文:https://towardsdatascience.com/using-gbif-data-and-geopandas-to-plot-biodiversity-trends-6325ab6632ee?source=collection_archive---------42-----------------------

基于 Python 的生物多样性数据热图

我在的上一篇文章中介绍了 GBIF 以及如何使用 Python 代码通过 API 访问这一优秀的生物多样性数据源,在这篇文章中,我将展示几种不同的方法来绘制之前下载的生物多样性数据。这将是一篇代码量很大的文章,主要使用 GeoPandas,并以复制我在论文中使用的图形而告终。然而,我使用了 R 和 ArcGIS 的组合来创建该图,现在我只使用 Python!希望这将是一个关于 GeoPandas 制图的有用教程,geo pandas 是处理地理空间数据的一个很好的工具!

如果您对不同地区之间的生物多样性变化感兴趣,地图是可视化空间差异的一种很好的方式。在进化生物学中,知道不同的生物多样性“热点”在哪里,也就是相对高生物多样性的区域,通常是很有趣的。这些热点可以代表高物种形成率的区域,或者如果你正在观察一个单一的生物群体,它们可能代表它们的起源区域。良好的生物多样性可视化对生物学家非常有用!

创建高质量的地图是复杂的,当我作为一名研究生第一次处理这个问题时,我非常依赖于预先存在的软件和图书馆中非常有用的 GIS 帮助台。现在我有了更多的编程技能,用 Python 重做这个问题真的很有趣。在 Python 中,有一些很好的处理地理空间数据的包。如果你正在处理栅格数据(基于像素的数据),一定要看看栅格。不过在这个问题中,我发现使用包 GeoPandas 更容易,它是基于非常有用的 Pandas DataFrame 构建的,但是使用 shapely 库合并了一个几何特性。就像 Pandas 中有数据框和系列一样,GeoPandas 中有地理数据框和地理系列,允许您将其他要素与地理数据相关联。几乎任何可以用熊猫做的事情都可以用地质公园来做。

绘制生物多样性地图的一种方法是通过一些现有的地理划界,如国家。这些被称为 choropleth 地图,有一些不同的软件包可以制作它们。这在 GeoPandas 中非常容易,它基于 matplotlib 进行绘图。这是我们首先要做的。

通过全国范围的物种计数绘制地图(Choropleth 地图)

从物种数据、原产国和地理坐标的数据框架开始,获得每个国家的物种数量是一个简单的聚合函数。我建议在你的数据集中添加一列,使用三个字母的 ISO A3 代码来代表每个国家,这样在以后加入数据框时事情会简单得多。你可以查看我的 GitHub(这篇文章底部的链接)来了解细节。

除了物种数据框之外,您还需要一个地理数据框作为底图进行绘制。我使用了来自 NaturalEarth 的中等分辨率的乡村形状文件。在下面的代码中,我展示了如何合并两个数据帧,然后绘制 choropleth 图(图 1)。

import pandas as pd
import matplotlib.pyplot as plt
import geopandas as gpd*#Read back in csv file*
df = pd.read_csv("../data/Ayenia_cleaned_dataframe.csv")*#Create a new dataframe with just number of species per country and #the country code*
df_plot = pd.DataFrame(df.groupby('country')['species'].nunique().sort_values(ascending=False))*#Load a country map*
countries = gpd.read_file("../data/ne_50m_admin_0_countries/ne_50m_admin_0_countries.shp", encoding = 'UTF-8')*#Only keep the relevant columns* countries = countries[['NAME', 'ISO_A3', 'CONTINENT', 'geometry']]*#Restrict countries to only North and South America*
countries = countries[(countries['CONTINENT'] == 'North America') | (countries['CONTINENT'] == 'South America')]*#Join with the species count dataframe*
countries = countries.merge(df_plot, 
                            how = 'outer', 
                            left_on = 'ISO_A3', 
                            right_on = 'code')*#Drop the ISO code column we merged on*
countries.drop('code', axis = 1, inplace = True)#*Fill species count with 0 for countries that weren't in the species #count dataframe*
countries['species'].fillna(value = 0, inplace = True)#*Drop any remaining NaN (one small island country wasn't in the #basemap file, not a big deal here)*
countries.dropna(inplace = True)#*Now plot the choropleth with GeoPandas and matplotlib*fig, ax = plt.subplots(1, 1, figsize= (10, 10))countries.plot(column='species',
               ax = ax, 
               legend = True,
               cmap = 'YlOrRd',
               legend_kwds={'label': "Number of Species", 
                            'shrink': 0.5}) ax.set_facecolor("lightblue")
plt.xlim((-130, -30))
plt.ylim((-60, 50))
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.title(r"Number of $Ayenia$ Species by Country", 
          fontdict = {'fontsize': 'x-large'})
plt.tight_layout()

图一。每个国家的叶蝉物种数量分布图。

虽然 choropleth 显示了物种多样性在 Ayenia 的位置,但它还不够具体。其中一些国家非常大(如巴西、美国),因此了解这些物种在这些国家的什么地方会更有意思。此外,通过给整个国家着色,地图可能会产生误导。例如,在南美洲的阿根廷, Ayenia 只限于该国北部地区(零下 30 度以上),而在美国, Ayenia 只出现在该国最南部地区。我想更真实地展示这些植物是在哪里发现的。

通过网格单元绘制生物多样性图

对于下一张地图,我想计算地图上每 1 个方格中可以找到的物种数量。这将绘制一张更详细的地图,显示艾叶尼亚的生物多样性。为了做到这一点,我必须完成以下步骤:

  1. 使用 GeoPandas 将物种数据框中的坐标更改为 shapely 对象
  2. 在我们感兴趣的区域内创建 1 格网像元的地理数据框架
  3. 计算每个网格单元内有多少物种
  4. 绘制网格

下面是我为每个步骤编写的代码:

#*1\. Import species data into a GeoDataFrame*
species_gdf = gpd.GeoDataFrame(df, 
              geometry=gpd.points_from_xy(df['decimalLongitude'], 
                                          df['decimalLatitude']),  
              crs = "EPSG:4326")*#2\. Create GeoDataFrame with 1 degree grid cells
#Make list of 1 degree grid cells with shapely polygons* from shapely.geometry import Polygonlong_range = list(range(-130, -29))
lat_range = list(range(-60, 51))poly_list = []for x in long_range:
    for y in lat_range:
        new_poly = Polygon([(x, y), 
                            (x + 0.99999, y), 
                            (x + 0.99999, y + 0.99999), 
                            (x, y + 0.99999)])
        poly_list.append(new_poly)#*Make GeoDataFrame from list of polygons, making sure that the #coordinate reference system aligns with your data points DF*
grid_df_1d = gpd.GeoDataFrame(geometry = poly_list, 
                              crs = species_gdf.crs)#*Add a column of cell numbers for visualization purposes*
grid_df_1d['cell_no'] = list(range(0, len(grid_df_1d)))

此时,我们有了一个包含感兴趣范围内非重叠 1 格网单元的地理数据框架(图 2)。您可以轻松地修改此代码,使其具有更小或更大的网格单元大小(比如 0.5 或 10),并根据您的需要覆盖更大或更小范围的地球部分。我建议让它尽可能的小,因为这下一步不是很有效率,在我的笔记本电脑上运行需要 30 多分钟。

图二。研究区域内 1 格网单元的范围。为了说明的目的,示出了单元号。

#*3\. Calculate the number of species in each grid cell* #*Make a dictionary to store a list of each grid cell's species*grid_species = {}for x in list(range(0, len(grid_df_1d))):
    grid_species[f'{x}'] = []#*For each species in the species dataframe, determine whether or not #it's within the bounds of the polygons in the grid dataframe. If it #is, add that species to the list in the grid_species dict (This step is very slow and inefficient! Does anyone know of a better way??)*for ind_s, val_s in species_gdf.iterrows():
    for ind_g, val_g in grid_df_1d.iterrows(): 
        if val_s['geometry'].within(val_g['geometry']):
            grid_species[f"{val_g['cell_no']}"].append(val_s['species'])#*Now count the number of unique species in each list and save in a #new dictionary and then add data to the grid dataframe*import numpy as np
grid_counts = {}for k, v in grid_species.items():
    grid_counts[k] = len(np.unique(v))grid_df_1d['species_count'] = np.zeros(len(grid_df_1d))
for k, v in grid_counts.items():
    grid_df_1d.loc[int(k) ,'species_count'] = v#*Drop cells that don't have any species in them*
grid_df_1d_nozero = grid_df_1d.drop(grid_df_1d[grid_df_1d['species_count'] == 0].index, axis = 0)#*4\. Plot the grid*fig, ax = plt.subplots(1, 1, figsize = (10, 10))
countries.plot(ax = ax, color = 'whitesmoke')
grid_df_1d_nozero.plot(column = 'species_count', 
                       ax = ax, 
                       legend = True, 
                       cmap = 'YlOrRd',
                       legend_kwds={'label': "Number of Species", 
                                    'shrink': 0.5})
ax.set_facecolor('lightblue')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.title(r"Biodiversity of $Ayenia$", 
          fontdict = {'fontsize': 'x-large'})
plt.xlim((-120, -30))
plt.ylim((-40, 40))
plt.tight_layout()

图 3。横跨北美和南美的每 1 个网格单元中的叶虫种类数。

现在,我们有了一个更好的表现,可以看出艾叶尼亚在哪里生长,在哪里不生长,哪里有更多的物种,哪里有更少的物种。在图 3 中,你可以看到墨西哥,尤其是墨西哥南部,是 T2 艾尼亚多样性的热点。在巴西南部以及巴拉圭、玻利维亚和阿根廷北部附近地区,还有几个热点。这些地区都有相似的栖息地,包括季节性干燥的热带森林或稀树草原。在巴西和阿根廷的中部有几个网格单元,它们的物种数量非常高,但这些可能不是真实的,因为我们数据中的一些标本具有模糊的位置数据,并且在该国中部给出了 GIS 坐标。以后我得用手移除它们。

我希望这些代码对学习如何绘制生物多样性地图有用。如果任何人有兴趣将其应用于他们自己的有机体群体,并需要帮助来运行它,我很乐意帮助你!

这是我在 Jupyter 笔记本上的 GitHub 页面的链接。

使用 GCP 人工智能平台笔记本作为可复制的数据科学环境

原文:https://towardsdatascience.com/using-gcp-ai-platform-notebooks-as-reproducible-data-science-environments-964cba32737?source=collection_archive---------43-----------------------

使用预配置的云托管虚拟机解决 Python 和 Jupyter 笔记本电脑的再现性问题。

作者:爱德华·克鲁格道格拉斯·富兰克林

照片由 Michael Dziedzic 在 Unsplash 上拍摄

在本文中,我们将介绍如何设置一个云计算实例,以便在有或没有 Jupyter Notebook 的情况下运行 Python。然后,我们展示如何将该实例连接到 Github,以实现流畅的云工作流。

我们利用云计算实例来获得灵活的 Python 和 Jupyter 环境,同时保持企业数据科学平台的可再现性。

这些人工智能平台笔记本电脑配置了许多数据科学和分析包,包括 NumPy、Pandas、Scikit-learn 和 TensorFlow。通常,我们不鼓励使用臃肿的虚拟机。然而,我们的分析机器上的包膨胀不是一个大问题,因为我们只保存结果(模型、数据、报告)供以后使用。只需要这个结果和运行我们的模型所需的几个包就可以让我们忽略 VM 上的大量包。

例如,在这篇中型文章中,我们将 NLP 模式推送到云中,而不必担心依赖性。

[## 使用 Docker、GCP 云运行和 Flask 部署 Scikit-Learn NLP 模型

构建一个服务于自然语言处理模型的应用程序,将其容器化并部署的简要指南。

towardsdatascience.com](/deploy-a-scikit-learn-nlp-model-with-docker-gcp-cloud-run-and-flask-ba958733997a)

请注意,AI 平台笔记本已经安装了 GCP 服务的所有客户端包,并且已经过认证,可以轻松访问同一个 GCP 项目中的任何内容。此外,这个平台不仅为我们提供了对 Jupyter 笔记本的访问,还提供了 Python 控制台和 CLI,我们可以在其中运行 BASH 命令。

获得 GCP 账户

谷歌的人工智能平台笔记本电脑为数据科学家和机器学习开发人员提供了一个 JupyterLab 和 Python 环境,用于实验、开发和将模型部署到生产中。用户可以创建运行 JupyterLab 的实例,这些实例预装了常见的软件包。

在我们可以建立一个人工智能平台笔记本之前,我们必须建立一个帐户和账单,不要担心新用户会获得 300 美元的免费积分!

访问 GCP 人工智能平台,点击“转到控制台”

请务必单击下面的“启用 API”来访问笔记本。

启用 API

一旦我们有帐单设置,我们可以开始一个项目。

启动您的第一个 GCP 人工智能平台笔记本实例

现在我们需要选择运行虚拟机的硬件。如果您要进行测试,请确保设置尽可能便宜的机器!

启用 API 后,弹出选项将变为如下所示,单击“转到实例页面”开始。

单击转到实例页

“实例”页面可能会让您在其他时间选择“启用 API”,请务必这样做。然后点击“新建实例”按钮,选择“Python 2 和 3”

笔记本实例

这将打开一个选项菜单,您可以在其中输入您想要使用的区域。请注意,不同的地区可能有不同的定价。一旦您选择了一个区域,您将需要单击“定制”并选择具有最少 RAM 的机器以获得最低的成本。在我们的示例中,它是具有 3.75GB 内存的“n1-standard-1”虚拟机。

这个实例只会在运行时产生费用,并且可以随时轻松暂停!如果需要,您可以在实例暂停时使用下面的下拉菜单更换硬件。

选择低成本的机器

现在我们可以使用 SSH 将我们的 VM 连接到 GitHub,这样我们就可以轻松地将pushpull连接到我们的存储库。

设置 SSH

请注意,每个实例只需这样做一次。

用 ssh 连接到 GitHub

  1. 通过运行ssh-keygen生成一个 ssh 密钥,并通过将它们留空并按回车键来接受默认值。该命令在~/.ssh/id_rsa生成文件,您需要将这些文件输入 GitHub。

ssh-keygen

2.将您的公钥复制到剪贴板。一种方法是通过运行cat ~/.ssh/id_rsa.pub将公钥文本返回到您的控制台,显示其内容,然后用鼠标和键盘进行复制。

用猫拿到我们的钥匙

3.前往 github.com 并登录。

4.点击右上角的个人资料图片,然后点击“设置”

5.在左侧,点按“SSH 和 GPG 密钥”

6.在右上角,单击“新建 SSH 密钥”

7.标题随便你怎么定。“标题”是您的选择,但它将帮助您识别该授权授权的计算机。将复制的密钥粘贴到“密钥”栏,然后按“添加 SSH 密钥”

8.回到您的计算机,运行eval ssh-agent -s来启动您的 ssh 验证代理。

步骤 8 和 9 添加我们的 ssh-key

9.运行ssh-add添加您的私钥,以便代理可以验证公钥。

10.设置您的 git 配置,以便 GitHub 通过运行git config --global user.email you@email.comgit config --global user.name username知道您是谁,其中电子邮件和用户名是您的 GitHub 帐户附带的。

现在,您可以git clone任何您有权访问的存储库到虚拟机上,对代码进行更改,并将它们推回存储库!

结论

我们已经讨论了如何设置一个云计算实例来运行 Python、BASH 和 Jupyter 笔记本,以及如何将该实例连接到 Github 来实现一个简单而安全的云工作流。

这个工作流程非常棒,因为它是如此的可复制!像这样使用虚拟机的团队将会遇到更少的“它在我的机器上工作”的错误。使用 ssh 连接云虚拟机和我们的远程存储库提供了一个安全的连接来保护您的数据。此外,如果您想在昂贵的硬件上运行代码,您不必购买硬件!相反,运行您需要的东西并暂停您的实例以节省成本。

我们希望这份指南对你有所帮助,并且你的编码技能和我们一样高!

使用遗传算法建立交易策略

原文:https://towardsdatascience.com/using-genetic-algorithms-to-build-stock-trading-strategies-d227951d3df0?source=collection_archive---------8-----------------------

展示遗传算法找到创造性解决方案的能力

马腾·戴克斯Unsplash 上拍摄的照片

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

遗传算法经常被忽略,作为另一种未能有效收敛的无监督学习算法。这是部分正确的,因为遗传算法不使用偏导数,因此训练算法不太直接。然而,遗传算法允许使用传统的基于梯度的优化来制定不可能的解决方案。

为什么会这样呢?如果基于梯度下降的模型从局部最小值开始,它将被永久卡住,因为每一侧上的点的梯度会更高。然而,由于遗传算法更具随机性,它最终会找到解决方案。

此外,神经网络需要具有明确定义的损失函数的标记数据。遗传算法只需要数据,可以巧妙地设计一个定制的损失函数(通常称为遗传算法的适应度函数)来优化某些特征。这种灵活性使得遗传算法从其余的无监督算法中脱颖而出。

遗传算法是如何工作的?

遗传算法是对该问题的一种强力攻击形式。唯一的区别是使用进化的概念来加速这个过程。遗传算法是这样工作的:

  1. 生成一个代理。它包含一组与神经网络兼容的权重。
  2. 计算代理的适应值
  3. 重复多次,直到你有一个代理“人口”
  4. 按适合度对人群进行排序,适合度最好的代理位于列表顶部。
  5. 一起“繁殖”顶级特工。
  6. 重复进行,直到达到满意的损失值。

通过改变适应度函数和输入数据,遗传算法可以应用于几乎每个问题!

当不应使用遗传算法时:

对于回归和分类等基本任务,使用基于梯度的优化器训练的深度神经网络总是比遗传算法收敛得更快。

遗传算法并不擅长寻找模式,而是有着广阔的视野,去寻找尚未被考虑的解决方案。

将遗传算法应用于股票交易:

股票交易是复杂的,因为不同来源的噪音冲淡了数据背后的真实模式。使用遗传算法就像走捷径一样;忽略了模式识别和复杂分析。它只是测试不同的策略,并找到交易证券的最佳策略。如果遗传算法不能找到一个好的解决方案,这只能是一个(或两个!)的这两个原因:

  1. 遗传算法没有被训练足够长的时间

遗传算法是一种蛮力算法,需要很长时间来缩小结果范围。这是一个很大的障碍,因为计算能力必须非常高才能克服这个问题

2.损失函数有问题

这个就常见多了。评估网络的损失函数并没有封装损失函数的目标。

代码:

免责声明:此代码改编自此处的代码,使其兼容优化创建交易策略的神经网络。

步骤 1|先决条件:

import random
import numpy as npdef sigmoid(x):
    return 1/(1+np.exp(-x))

这些是项目最基本的依赖项。为了使学习过程更好,您可以为您合适的数据集添加更多的激活函数。

第 2 步|代理:

class Agent:
            def __init__(self,network):
                class neural_network:
                    def __init__(self,network):
                        self.weights = []
                        self.activations = []
                        for layer in network:
                            if layer[0] != None:
                                input_size = layer[0]
                            else:
                                input_size = network[network.index(layer)-1][1]
                            output_size = layer[1]
                            activation = layer[2]
                            self.weights.append(np.random.randn(input_size,output_size))
                            self.activations.append(activation)
                    def propagate(self,data):
                        input_data = data
                        for i in range(len(self.weights)):
                            z = np.dot(input_data,self.weights[i])
                            a = self.activations[i](z)
                            input_data = a
                        yhat = a
                        return yhat
                self.neural_network = neural_network(network)
                self.fitness = 0
            def __str__(self):
                    return 'Loss: ' + str(self.fitness[0])

每个代理都有自己的一组权重,这些权重是根据神经网络设置的架构随机生成的。论证网络是一个列表,看起来像这样:

network = [[5,10,sigmoid],[None,1,sigmoid]]

列表中的每个术语都描述了神经网络中的一层。每个嵌套列表中的第一项是该层的输入神经元的数量,第二项是输出,第三项是要应用于该层的激活函数。

步骤 3|生成代理:

def generate_agents(population, network):
            return [Agent(network) for _ in range(population)]

这用于生成代理,如运行遗传算法的方法的步骤 1 中所详述的。

第 4 步|计算健康度:

def fitness(agents,X):
            neutral_range = 0
            for agent in agents:
                profit = 0
                qty = 10
                yhat = agent.neural_network.propagate(X[1:])
                for i in range(len(yhat)):
                    if 0.5 + neutral_range > yhat[i] and yhat[i] > 0.5 -neutral_range:
                        yhat[i] = None
                    elif 1-yhat[i] > yhat[i]-0:
                        yhat[i] = 0
                    elif 1-yhat[i] < yhat[i]-0:
                        yhat[i] = 1

                correct_trades = []
                for i in range(1,len(X)):
                    if X[i][3] > X[i-1][3]:
                        correct_trades.append(1)
                    elif X[i][3] < X[i-1][3]:
                        correct_trades.append(0)
                for i in range(len(yhat)):
                    if yhat[i]:
                        if correct_trades[i] == yhat[i]:
                            profit += abs(X[i+1][3] - X[i][3])*qty
                        elif correct_trades[i] != yhat[i]:
                            profit -= abs(X[i+1][3] - X[i][3])*qty
                agent.fitness = profit
            return agents

这个适应度函数是一个交易模拟,它使用过去的数据来计算模型的利润。每个代理通过输出一个 sigmoid 值生成一个交易列表。然后将这些值四舍五入到最接近的值。如果该值为 0,模拟将卖出股票。如果该值为 1,模拟将买入股票。

然后,它通过比较最佳交易和实际交易来计算利润。

第 5 步|代理选择:

def selection(agents):
            agents = sorted(agents, key=lambda agent: agent.fitness, reverse=False)
            print('\n'.join(map(str, agents)))
            agents = agents[:int(0.2 * len(agents))]
            return agents

这段代码本质上是根据代理的适应值对它们进行排序,从最低值到最高值。记住,适应值越低越好。然后,它将代理列表缩短为仅前 20%的代理。

第六步|代理交叉:

def unflatten(flattened,shapes):
            newarray = []
            index = 0
            for shape in shapes:
                size = np.product(shape)
                newarray.append(flattened[index : index + size].reshape(shape))
                index += size
            return newarray

        def crossover(agents,network,pop_size):
            offspring = []
            for _ in range((pop_size - len(agents)) // 2):
                parent1 = random.choice(agents)
                parent2 = random.choice(agents)
                child1 = Agent(network)
                child2 = Agent(network)

                shapes = [a.shape for a in parent1.neural_network.weights]

                genes1 = np.concatenate([a.flatten() for a in parent1.neural_network.weights])
                genes2 = np.concatenate([a.flatten() for a in parent2.neural_network.weights])

                split = random.randint(0,len(genes1)-1)child1_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())
                child2_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())

                child1.neural_network.weights = unflatten(child1_genes,shapes)
                child2.neural_network.weights = unflatten(child2_genes,shapes)

                offspring.append(child1)
                offspring.append(child2)
            agents.extend(offspring)
            return agents

这是程序中最复杂的部分。在交叉过程中,从选定的代理中随机选择两个父代理。权重的嵌套列表被简化为一个长列表。分裂点是随机选取的。这决定了子对象将由每个父对象的多少权重组成。然后重新格式化该子代的权重,并将该子代添加到代理列表中。

第七步|突变:

def mutation(agents):
            for agent in agents:
                if random.uniform(0.0, 1.0) <= 0.1:
                    weights = agent.neural_network.weights
                    shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
                    randint = random.randint(0,len(flattened)-1)
                    flattened[randint] = np.random.randn()newarray = []
                    indeweights = 0
                    for shape in shapes:
                        size = np.product(shape)
                        newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
                        indeweights += size
                    agent.neural_network.weights = newarray
            return agents

遗传算法在技术上是“盲目的”,因为它们无法洞察由损失函数绘制的表面看起来是什么样子。这意味着遗传算法很容易卡在某个点上。突变允许轻微的、随机的、不频繁的变化来搅动锅,使系统脱离局部最小值。

第八步|执行程序:

def split_sequences(sequences, n_steps):
    X, y = list(), list()
    for i in range(len(sequences)):
        end_ix = i + n_steps
        if end_ix > len(sequences)-1:
            break
        seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
        X.append(seq_x)
        y.append(seq_y)
    return np.array(X), np.array(y)
np.random.seed(0)            
X,y = split_sequences(np.random.randn(1000,1),5)
X = np.reshape(X,(X.shape[0],X.shape[1]))
network = [[5,10,sigmoid],[None,1,sigmoid]]
ga = genetic_algorithm
agent = ga.execute(100,100,10000,X,network)

为了执行这个计划,我们需要数据。出于某种原因,SSL 证书不允许我从 yfinance 或 alphavantage 获取财务数据。我还不能运行这个程序,所以我只是使用随机 numpy 数组作为数据。

如果你想使用你自己的财务数据,这个概念就是用一组收盘价数据把它分成 n 个大小的块。

为了再现性,这个程序将设置自己的种子,使随机性固定。这是为了减少随机性,允许调整参数,如人口规模,时代和神经网络架构。这里更有趣的参数是输入网络的每个数据块的长度。调整这个参数可以找到程序运行的最佳时间。

结论:

正如我在自己的许多其他文章中所说的,这仅仅是用这个概念可以做什么的框架。你可以做无数的事情来改进我的程序。以下是其中的几个例子:

  1. 记录过去代理的生成

通过记录过去代理的生成,它可以防止程序重新生成以前已经生成的代理。

2.将该程序链接到一个纸交易账户

羊驼是使用纸面交易组合的明显选择。它可以快速进行交易,便于分析程序的性能。

3.创建更多贸易类型

这是三个中最难的。添加不同的交易,如止损和买入卖出交易,允许更微妙和分层的交易策略,将不同的交易类型相互叠加,以实现利润最大化。

我的链接:

如果你想看更多我的内容,点击这个 链接

使用遗传算法来安排时间表

原文:https://towardsdatascience.com/using-genetic-algorithms-to-schedule-timetables-27f132c9e280?source=collection_archive---------5-----------------------

遗传算法如何应用于解决时间表问题

遗传算法是一种受自然选择过程启发的进化算法。我在之前的文章 遗传算法介绍中已经介绍过遗传算法——包括示例代码 。如果你还没看过,就去看看吧!

[## 遗传算法简介—包括示例代码

遗传算法是一种受查尔斯·达尔文的自然进化理论启发的搜索启发式算法。这个…

towardsdatascience.com](/introduction-to-genetic-algorithms-including-example-code-e396e98d8bf3)

最近我注意到,我留下的一个关于使用遗传算法安排时间表的回复对很多人真的很有用。因此,我想把这个回复扩展成一篇文章,这样对读者会更有用。让我们开始吧。

什么是遗传算法?

引用我以前的文章,

遗传算法是一种受查尔斯·达尔文的自然进化理论启发的启发式搜索。这种算法反映了自然选择的过程,即选择最适合的个体进行繁殖,以产生下一代的后代。

如果你想了解更多关于遗传算法的知识,你可以阅读我的文章 遗传算法简介—包括示例代码 在这里我用示例解释了每个阶段。

总而言之,遗传算法的 5 个主要阶段是:

  1. 原始群体
  2. 适应度函数
  3. 选择
  4. 交叉
  5. 变化

下图显示了一般遗传算法的工作流程图。

通用遗传算法如何工作的流程图(图片由作者提供)

制定时间表

时间表中,我们必须为我们计划的活动分配时间,并有序地协调资源,以便我们可以在不违反任何约束的情况下获得预期的结果。例如,学校时间表将协调学生、教师、教室、科目和时间段。一个考试时间表将协调学生、监考人员、科目、考场和时间段。公共交通时刻表将协调交通方式(公共汽车、火车等)。)、路线、到达时间和离开时间,以便乘客计划行程。

遗传算法可以应用的一个非常流行的场景是在安排时间表的过程中。

图片由Bruno/德国来自 Pixabay

示例场景

假设你正试图为一所大学的不同学生群体制定一份每周课程表。我们必须安排课程,制定时间表,这样课程之间就不会有冲突。在这里,我们的任务是寻找最佳的时间表安排。

创造染色体

一个个体由一组被称为基因的参数(变量)来表征。基因连成一串形成染色体(解)。我们将我们的解决方案建模为染色体。

在我们的示例场景中,对于学习不同模块的学生,染色体将是不同的 讲座 。考虑到我们必须协调学生小组、模块、演讲厅、一周中的日期和时间。你可以把一节课描述成,

**<Module, Student Group, Day, Location, Lecture Time>**

您可以将讲座作为二进制模式编码到染色体中。

您可以为每个实体中的每个值指定二进制值。您可以随意更改编码模式。下面给出了一个你可以对讲座进行编码的例子。

获取模块列表并分配二进制值。

Data Mining - 0000,
Machine Learning - 0001,
Biology - 0010,
...

获取学生组列表,并给出二进制值。

STG0 - 00000, 
STG1 - 00001, 
STG2 - 00010, 
STG3 - 00011,...

类似地,你可以在讲座中为每个实体提出编码方案。

下面给出了一个讲座的示例编码。

**<Data Mining, STG3, Monday, Hall D, 8.00AM>**Data Mining - 0000
STG3 - 00011
Monday - 000
Hall D - 1010
8.00AM - 1000Chromosome - **00000001100010101000**

单个的位被称为基因。这条染色体有 20 个基因。

创建初始群体

不同的学生群体在一周内上不同的课。因此,你必须想出不同的类组合,并创建初始种群。你可以决定人口数量(班级数量)。

**<Data Mining, STG3, Monday, Hall A, 8.00AM>
<Machine Learning, STG2, Tuesday, Hall B, 8.00AM>
<Computational Biology, STG8, Tuesday, Hall A, 10.00AM>
...**

如前所述,你必须将这些类别编码到染色体中。

提出一个评估函数

例如,您可以将评估函数公式化为学生组 的 班级冲突数的倒数。冲突的数量越少,讲座就越合适。你可以选择一个合适的评价函数。

现在,您可以执行交叉和变异操作,以最大化每个讲座的适应值。

结束

当群体达到最大适应值时,您可以终止该过程。在我们的例子中,这是讲座冲突最少的时候。

图片由Bruno/德国来自 Pixabay

最后的想法

我在这篇文章中描述的方法是非常基本的,这样你就可以大致了解如何对时间表问题建模。如果你想阅读更多,你可以阅读许多的研究文章,其中提出了新的和改进的方法来使用遗传算法解决时间表问题。我看到的一些文章列举如下。

遗传算法并不是我们解决课程表问题的唯一方法。还有许多进化算法,如基于种群的搜索算法蚂蚁算法蜜蜂算法

希望你对现实世界的问题有了一个基本的概念,我们可以用遗传算法来解决它。请在回复部分告诉我你的想法。

不要忘记看看这个令人敬畏的遗传算法的实现,在【https://github.com/memento/GeneticAlgorithm】mem ento 制作的每一代基因库的可视化。

[## 纪念品/遗传算法

重构和改进来自 Vijini Mallawaarachchi 女士(@Vini2)的一个要旨我还把@liangyihuai 的编辑带进了…

github.com](https://github.com/memento/GeneticAlgorithm)

感谢您的阅读!

干杯!

使用遗传算法训练神经网络

原文:https://towardsdatascience.com/using-genetic-algorithms-to-train-neural-networks-b5ffe0d51321?source=collection_archive---------5-----------------------

图像来源

许多人使用遗传算法作为无监督算法,来优化特定环境中的代理,但是没有意识到将神经网络应用到代理中的可能性。

什么是遗传算法?

遗传算法是一种学习算法,它使用的思想是,交叉两个好的神经网络的权重,会产生更好的神经网络。

遗传算法如此有效的原因是因为没有直接的优化算法,允许可能有极其不同的结果。此外,他们通常会提出非常有趣的解决方案,这些方案通常会对问题提供有价值的见解。

它们是如何工作的?

生成一组随机权重。这是第一个代理的神经网络。在代理上执行一组测试。代理会收到基于测试的分数。重复几次,创建一个群体。选择人口中前 10%的人进行杂交。从前 10%中选择两个随机亲本,并且它们的权重是交叉的。每次交叉发生时,都有很小的变异机会:这是一个不在双亲权重中的随机值。

随着代理慢慢适应环境,这个过程会慢慢优化代理的性能。

优点和缺点:

优势:

  • 计算不密集

没有线性代数计算要做。唯一需要的机器学习计算是通过神经网络的正向传递。因此,与深度神经网络相比,系统要求非常广泛。

  • 适合的

人们可以适应和插入许多不同的测试和方法来操纵遗传算法的灵活性。人们可以在遗传算法中创建一个 GAN,方法是让代理传播生成器网络,测试作为鉴别器。这是一个至关重要的好处,它使我相信遗传算法的使用在未来将会更加广泛。

  • 可理解的

对于正常的神经网络来说,算法的学习模式充其量也是神秘的。对于遗传算法来说,很容易理解为什么会发生一些事情:例如,当遗传算法处于井字游戏环境中时,某些可识别的策略会慢慢发展。这是一个很大的好处,因为机器学习的使用是利用技术来帮助我们获得对重要问题的洞察力。

缺点:

  • 需要很长一段时间

不幸的交叉和突变可能会对程序的准确性产生负面影响,因此使程序收敛更慢或达到某个损失阈值。

代码:

现在你已经对遗传算法及其优势和局限性有了相当全面的了解,我现在可以向你展示这个程序了:

import random
import numpy as np

这只是这个程序的两个依赖项。这是因为实现的神经网络基础设施是我自己创建的简单版本。要实现更复杂的网络,可以导入 keras 或 tensorflow。

class genetic_algorithm:

    def execute(pop_size,generations,threshold,X,y,network):
        class Agent:
            def __init__(self,network):

这是“genetic_algorithm”类的创建,它包含了与遗传算法以及它应该如何工作有关的所有函数。主函数是 execute 函数,它将 pop_size、generations、threshold、X,y、network 作为参数。pop_size 是生成种群的大小,generations 是历元的术语,threshold 是您满意的损失值。x 和 y 用于标记数据的遗传算法的应用。对于没有数据或数据未标记的问题,可以删除 X 和 y 的所有实例。网络是神经网络的网络结构。

class neural_network:
                    def __init__(self,network):
                        self.weights = []
                        self.activations = []
                        for layer in network:
                            if layer[0] != None:
                                input_size = layer[0]
                            else:
                                input_size = network[network.index(layer)-1][1]
                            output_size = layer[1]
                            activation = layer[2]
                            self.weights.append(np.random.randn(input_size,output_size))
                            self.activations.append(activation)
                    def propagate(self,data):
                        input_data = data
                        for i in range(len(self.weights)):
                            z = np.dot(input_data,self.weights[i])
                            a = self.activations[i](z)
                            input_data = a
                        yhat = a
                        return yhat
                self.neural_network = neural_network(network)
                self.fitness = 0

该脚本描述了每个代理的神经网络的权重初始化和网络传播。

def generate_agents(population, network):
            return [Agent(network) for _ in range(population)]

该函数创建将被测试的第一个代理群体。

def fitness(agents,X,y):
            for agent in agents:
                yhat = agent.neural_network.propagate(X)
                cost = (yhat - y)**2
                agent.fitness = sum(cost)
            return agents

因为我使用的例子利用了标记数据。适应度函数仅仅是计算预测的 MSE 或成本函数。

def selection(agents):
            agents = sorted(agents, key=lambda agent: agent.fitness, reverse=False)
            print('\n'.join(map(str, agents)))
            agents = agents[:int(0.2 * len(agents))]
            return agents

这个函数模仿了进化论中的选择理论:最优秀的生存下来,而其他的则任其自生自灭。在这种情况下,他们的数据会被遗忘,不会被再次使用。

def unflatten(flattened,shapes):
            newarray = []
            index = 0
            for shape in shapes:
                size = np.product(shape)
                newarray.append(flattened[index : index + size].reshape(shape))
                index += size
            return newarray

为了执行交叉和变异功能,权重需要展平和取消展平为原始形状。

def crossover(agents,network,pop_size):
            offspring = []
            for _ in range((pop_size - len(agents)) // 2):
                parent1 = random.choice(agents)
                parent2 = random.choice(agents)
                child1 = Agent(network)
                child2 = Agent(network)

                shapes = [a.shape for a in parent1.neural_network.weights]

                genes1 = np.concatenate([a.flatten() for a in parent1.neural_network.weights])
                genes2 = np.concatenate([a.flatten() for a in parent2.neural_network.weights])

                split = random.ragendint(0,len(genes1)-1)child1_genes = np.asrray(genes1[0:split].tolist() + genes2[split:].tolist())
                child2_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())

                child1.neural_network.weights = unflatten(child1_genes,shapes)
                child2.neural_network.weights = unflatten(child2_genes,shapes)

                offspring.append(child1)
                offspring.append(child2)
            agents.extend(offspring)
            return agents

交叉函数是程序中最复杂的函数之一。它产生两个新的“子”代理,它们的权重被替换为两个随机产生的父代理的交叉。这是创建权重的过程:

  1. 拉平父母的权重
  2. 生成两个分割点
  3. 使用分割点作为索引来设置两个子代理的权重

这就是智能体交叉的全过程。

def mutation(agents):
            for agent in agents:
                if random.uniform(0.0, 1.0) <= 0.1:
                    weights = agent.neural_network.weights
                    shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
                    randint = random.randint(0,len(flattened)-1)
                    flattened[randint] = np.random.randn()newarray = [a ]
                    indeweights = 0
                    for shape in shapes:
                        size = np.product(shape)
                        newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
                        indeweights += size
                    agent.neural_network.weights = newarray
            return agents 

这就是变异函数。展平与交叉功能相同。不是分割点,而是选择一个随机点,用一个随机值替换。

for i in range(generations):
            print('Generation',str(i),':')
            agents = generate_agents(pop_size,network)
            agents = fitness(agents,X,y)
            agents = selection(agents)
            agents = crossover(agents,network,pop_size)
            agents = mutation(agents)
            agents = fitness(agents,X,y)

            if any(agent.fitness < threshold for agent in agents):
                print('Threshold met at generation '+str(i)+' !')

            if i % 100:
                clear_output()

        return agents[0]

这是 execute 函数的最后一部分,它执行所有已定义的函数。

X = np.array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]])
y = np.array([[0, 1, 1, 0]]).T
network = [[3,10,sigmoid],[None,1,sigmoid]]
ga = genetic_algorithm
agent = ga.execute(100,5000,0.1,X,y,network)
weights = agent.neural_network.weights
agent.fitness
agent.neural_network.propagate(X)

这执行整个遗传算法。对于网络变量,每个嵌套列表保存输入神经元数目、输出神经元数目和激活函数。执行函数返回最佳代理。

我的链接:

如果你想看更多我的内容,点击这个 链接

使用 GeoPandas 和 Folium 过度设计犹太法律中的一个问题

原文:https://towardsdatascience.com/using-geopandas-and-folium-to-over-engineer-a-question-in-jewish-law-b9b33c4909d2?source=collection_archive---------19-----------------------

借助计算地理学解决一个古老问题的尝试

今年是犹太人的普林节,庆祝挫败了古波斯针对犹太人的种族灭绝阴谋。根据你所在的地方,普林节是在 3 月 10 日,或者“蜀山节”是在 3 月 11 日。

这意味着这个古老的节日增加了时间和空间的维度。正是在这样的时候,我喜欢我的职业,数据科学,像过去的拉比和圣贤一样质疑一切。

哈尔布拉查高等犹太法学院的拉比埃利泽·梅拉梅德解释说,某些地方之所以会在一天后庆祝这个节日,是因为一些城市有城墙,而另一些没有。如果一座城市在约书亚·本·嫩时期建有城墙,那么这座城市会庆祝书珊节(后来的日子),否则这座城市会庆祝常规的普林节。

从表面上看,这似乎是一个相对容易的城市之间的划分,但仔细观察,许多问题出现了。

  1. 我们怎么知道约书亚·本·嫩时期存在的有城墙的城市就是今天以同样的名字命名的城市呢?Lod 就是这种情况。
  2. 这座在约书亚·本·嫩时代被城墙包围的城市已经失去了它的犹太社区和历史记录。
  3. 一个历史上存在于约书亚本嫩时期的城市,历史上有城墙,但不知道它是否在奇迹发生时有城墙。希伯伦就属于这一类

唯一一个仍然只庆祝舒山节的城市实际上是耶路撒冷。然而,我们的问题仍然没有完全回答,因为城市的边界在过去的 2000 年里呈指数增长。还有其他城市被认为可能是庆祝蜀山普林节的地方,所以在我们的实验中,我们也将解决它们。

今天的耶路撒冷被认为是约书亚本嫩时代的同一实体吗?

要回答这个问题,我们可以从《塔木德经》中找到一些具体的城市划分规范。“拉比耶霍舒亚·本·利瓦伊说:一座城市,和所有毗邻它的定居点,以及所有能与它一起被看见的定居点,即,能从有城墙的城市被看见的,都被认为是像这座城市一样的”(Megila 3,2)。

但是什么是相邻的呢?仅仅是密集的连片建筑吗?拉比·梅拉梅德澄清道邻里之间少于 141 肘(67.8 米)的差距足以将他们视为一个邻里。

所以现在我们有了一个关键变量,来过度设计犹太律法的问题。

有许多方法可以用数据来解决这个问题,但我认为最简单的方法是使用 OpenStreetMap 下载以色列的所有空间建筑数据,并将建筑的边界扩展 67.8 米,统一重叠部分,并找到包含耶路撒冷古代部分的多边形。

让我们开始吧:

导入必要的包后,我创建了一个可能的蜀山普林节城市(以及耶路撒冷)的字典,然后将它转换成笛卡尔参考系中的地理数据框架。我通过简单地使用谷歌地图来找到疑似城墙城市的可能历史中心地理坐标。

**import** **geopandas** **as** **gpd**
**import** **folium**
**import** **pandas** **as** **pd**
**from** **folium.plugins** **import** LocateControl
**from** **shapely.geometry** **import** Point
**from** **shapely.ops** **import** cascaded_union
walled_cities_dict = {"Tiberias":[32.775011, 35.545584],"Hebron":[31.524723, 35.110460],"Nablus":[32.213117, 35.284881],
                      "Jaffa":[32.053705, 34.750625],"Lod":[31.955268, 34.896203],"Gaza":[31.504135, 34.464426],
                      "Zefat":[32.966292, 35.494927],"Acre":[32.920831, 35.069042],"Haifa":[32.800833, 35.019167],
                      "Beit Shean":[32.499474, 35.498450],"Jericho":[31.870487, 35.440522],
                      "Beer Sheva":[31.237182, 34.793101],"Ramla":[31.924759, 34.872939],"Tyre":[33.274876, 35.193785],
                      "Sidon":[33.562539, 35.369619],"Damascus":[33.511125, 36.301019],"Izmir":[38.418095, 27.139753],
                      "Baghdad":[33.350506, 44.401307],"Jerusalem":[31.777055, 35.234268]}
walled_cities_gdf = gpd.GeoDataFrame()
walled_cities_gdf['City'] = list(walled_cities_dict.keys())
walled_cities_gdf['geometry'] = list(walled_cities_dict.values())
walled_cities_gdf['geometry'] = walled_cities_gdf['geometry'].apply(**lambda** x: Point(x[1],x[0]))
walled_cities_gdf.crs = {'init':'epsg:4326'}
walled_cities_gdf = walled_cities_gdf.to_crs(epsg=3857)

我在以色列这里下载了建筑足迹的 shapefile:

我们在质心中旋转多边形(以使其更容易计算)并将多边形缓冲 67.8 米。

buildings = gpd.read_file('/home/arieh/Downloads/israel-and-palestine-latest-free.shp/gis_osm_buildings_a_free_1.shp')
buildings = buildings.to_crs(epsg=3857)
buildings_cent = buildings.copy()
buildings_cent['geometry'] = buildings_cent['geometry'].centroid
buildings_cent['geometry'] = buildings_cent['geometry'].buffer(67.8)

然后,我们将其全部转换为一个多面,删除重叠面之间的边界,然后将该多面转换回地理数据框。这样做是为了方便我们在它和城墙城市地理数据框架之间执行空间连接。

all_union = cascaded_union(buildings_cent['geometry'])
all_union_polys = []
**for** poly **in** all_union:
    all_union_polys.append(poly)
max_buffers_gdf = gpd.GeoDataFrame()
max_buffers_gdf['geometry'] = all_union_polys
max_buffers_gdf['id'] = range(len(all_union_polys))
max_buffers_gdf.crs = buildings_cent.crs
shushan_extents = gpd.sjoin(walled_cities_gdf,max_buffers_gdf)
max_buffers_gdf_subset = max_buffers_gdf[max_buffers_gdf['id'].isin(shushan_extents['index_right'])]
max_buffers_gdf_subset = max_buffers_gdf_subset.to_crs({'init':'epsg:4326'})
Shushan_Zones = pd.merge(max_buffers_gdf_subset,shushan_extents[['id','City']],on = 'id')

现在,我们只剩下可能庆祝蜀山普林节的最大范围。

然后,我们可以使用 leav 将我们计算的蜀山普林节区域转换成交互式网络地图。

x1,y1,x2,y2 = Shushan_Zones['geometry'].total_bounds
m = folium.Map(tiles='openstreetmap')
LocateControl().add_to(m)
m.fit_bounds([[y1, x1], [y2, x2]])
folium.GeoJson(Shushan_Zones,name='Shushan Zones').add_to(m)
folium.LayerControl().add_to(m)
m.save('local_files/Shushan_Zone_Tool.html')

互动网络应用位于这里

来自网络应用的一些截图显示了蜀山区的高级地图:

蜀山区高级地图

一张放大的照片显示了耶路撒冷的细节:

放大到耶路撒冷

嗯,那还行…但是杰里科和希伯伦呢?

希布伦

杰里科

这里没有太多的成功,因为有几个建筑足迹的工作。

所以我们有了它,就像物理肘杆如何帮助过去的圣人提出犹太律法的解决方案,所以我们也可以利用今天技术时代的工具和逻辑,并以可扩展和计算的方式应用它们!

这个项目的一个更好的版本将使用对象检测来描绘建筑物,并以光栅聚焦的方式创建区域。我想我们会把改造工作留到明年在耶路撒冷(zone)进行!

在这里找到笔记本,在这里找到 HTML 网页应用

使用 GeoPandas 进行空间可视化

原文:https://towardsdatascience.com/using-geopandas-for-spatial-visualization-21e78984dc37?source=collection_archive---------22-----------------------

将您的数据带入现实世界

维尔莫索夫在 Freepik 上的照片

最近,我在做一个项目,试图建立一个模型来预测华盛顿金县——西雅图周围的地区——的房价。在查看了这些特征之后,我想要一种基于位置来确定房屋价值的方法。

数据集包括纬度和经度,很容易通过谷歌搜索它们来查看房屋、它们的街区、它们离水的距离等。但是对于超过 17000 次的观察,这是一个愚蠢的任务。我必须找到更简单的方法。

我以前只用过一次地理信息系统(GIS ),但不是用 Python。所以我做了我最擅长的事情:我谷歌了一下,然后遇到了这个叫做 GeoPandas 的神奇包。我将让 GeoPandas 团队总结他们所做的工作,因为他们可以说得比我好得多。

GeoPandas 是一个开源项目,旨在简化 python 中地理空间数据的处理。GeoPandas 扩展了 Pandas 使用的数据类型,允许对几何类型进行空间操作。几何运算由 shapely 完成。GeoPandas 进一步依赖 fiona 进行文件访问,依赖 descartesmatplotlib 进行绘图。—来自 GeoPandas 网站的描述(2020)

这让我大吃一惊,我想要的真的只是最基本的功能。我将向您展示如何运行这段代码,并做我所做的事情——在地图上绘制精确的点。

除了基本的 pandasmatplotlib,你还需要几个包和一些文件。它们包括:

  • geo pandas——让这一切成为可能的包
  • 造型优美的——用于处理和分析平面几何对象的软件包
  • 笛卡尔 —用 Matplotlib 更好地集成了形状优美的几何对象。不是每次都需要它,但我导入它只是为了安全
  • 任何。shp 文件——这将是情节的背景。我的将有国王县,但你应该可以从任何城市的数据部门找到一个。不要从中删除任何文件。压缩文件。总会有东西坏掉。

关于 shapefiles 的更多信息可以在这里找到,但是总而言之,这些不是普通的图像。它们是一种矢量数据存储格式,包含与位置相关的信息—坐标和其他信息。

首先,我导入了我需要的基本包,然后是新包:

多边形特征帮助我将我的数据与我制作的地图相匹配。

接下来,我加载我的数据。这是基本的熊猫,但对于那些新的,在报价的一切是我不得不访问住房记录文件的名称。

随着所有包的导入和数据的准备,我想看看我将要绘制的地图。我是通过找一个金县政府网站做的形状文件做到的。他们已经完成了土地勘测和编目的所有艰苦工作——不使用他们免费提供的服务是不礼貌的。加载到形状文件中很容易,与用熊猫加载到 csv 文件中相当。

如果你想看看数据,你可以打开它。King County shape 文件只是一个与他们的学区、几何坐标和面积相匹配的位置数据框架。但是最好的部分是当我们绘制它的时候,是的,我们必须绘制它。这不是一个你可以随便调用的图像——它将内置坐标,因此我们的数据可以像五年级(x,y)图表上的一个点一样放置。

使用下面的代码(注意我是如何编辑它的,就像我编辑图形一样):

我的输出如下所示:

作者提供的图片

在我们开始添加我们的住房数据之前,我们应该充分利用形状文件。我们来看看文件。

如您所见,该县被划分为几个学区,每个学区都有一个用作边界的形状。我们现在将尝试绘制形状文件,并使用提供的数据对区域进行注释,如下所示:

列表——左边、右边、中间——是在地区名称的位置上反复试验的结果。一些重叠或需要被操纵,以便它们不会偏离它们的实际区域太远。

为了清晰起见,我将颜色贴图改为 gist_earth 。接下来,我使用 NAME 系列中的条目遍历每一行,并将标题放在多边形中明确的一个点上。我根据我之前列的名单排列了这些名字。这是我们的输出:

金县的学区。作者提供的图片

每个区域代表金县的一个学区。这与我找到的关于该县二十个学区的数据相符。我从来没有真正想过一个县的大小和形状,所以我谷歌了一下,只是为了确定一下。

来源:谷歌地图

看起来谷歌地图上的图像是我拼图的完美洞口。从这里开始,只需要格式化我的数据以适应形状文件。我通过初始化我的坐标系统并使用我房子的纬度和经度创建适用的点来做到这一点。

如果你要查看几何体中的一个条目,你只会得到它们是形状优美的物体。它们需要应用于我们的原始数据帧。下面,你可以看到我做了一个全新的数据框,内置了坐标系统,旧的数据框,以及由房屋的纬度和经度的交叉点创建的点的添加。

这是我们绘制房屋图之前的最后一步。现在,我们把它们放在一起。

在上面的代码中,步骤包括:

  1. 调用对象进行绘图。
  2. 绘制金县形状文件。
  3. 绘制包括几何点在内的数据。
    这包括制作标记、选择特征以及为图例添加标签。
  4. 添加图例、标题和轴标签。

对每幅图都执行了这些步骤。

我们的产出:

这是一个伟大的产品,但我们的目标是从这个可视化学习一些东西。虽然这提供了一些信息,如远离该县东部的异常值,但它没有提供太多其他信息。我们必须使用参数。让我们试着按价格分割数据。这些是标价低于 75 万美元的房子。

价格低于 75 万美元的房子。作者提供的图片

现在我们用图表表示大于或等于 750,000 美元的房屋。

价格在 75 万美元以上的房子。作者提供的图片

无论是位置还是数量都有很大的区别。但这不是目的,我们还可以将它们一层一层地堆叠起来。我们将在便宜的基础上做昂贵的,因为它更稀缺。

并排比较。作者提供的图片

这幅地图画的图画很有趣。金县有太多的房子低于我们设定的标准。大多数价格较低的房子比价格较高的房子更靠近内陆。

如果放大,更贵的房子点缀在水边。他们也更集中在西雅图市中心。有几个物理异常值,但趋势是明确的。

总的来说,可视化已经完成了它的工作。我们已经根据地图上的房子做了一些决定。价格较高的房屋聚集在市中心周围,散布在普吉特湾周围。他们在数据中也是少数,这可能有助于预测房价。价格较低的房子数量更多,位置也各不相同。这将有助于进一步的 EDA。

如果你想联系更多关于这个技术的讨论,你可以在 LinkedIn 上找到我。如果你想看看代码,看看我的 Github

来源

使用 GitHub 动作加速数据科学项目中的 CI/CD

原文:https://towardsdatascience.com/using-github-actions-to-speed-up-ci-cd-in-data-science-projects-77d9b1c88228?source=collection_archive---------29-----------------------

照片由扬西·敏Unsplash 上拍摄

随着关于云计算的最新进展,实现工具变得更加必要,这些工具同时是可扩展的,并且确保执行的可再现性。考虑到这一需求,一些工具应运而生,如 Docker ,它们允许创建应用程序的“配方”,确保同一应用程序的不同版本平等运行。

与虚拟机(VM)不同,虚拟机(VM)通过虚拟机管理程序提供基础架构并模拟处理器和内存,Docker 在整个容器中共享这些资源,使开发人员可以更少地关注基础架构,而更多地关注应用程序的开发。尽管如此,项目和应用程序的容器化减轻了“它在我的机器上运行”的表述,因为它试图确保独立于开发者选择的平台,Docker 容器总是以相同的方式执行。

鉴于容器化的好处不仅限于应用程序的开发,还可以用于其他领域,许多数据科学家开始使用 Docker 来容器化他们的分析、模型训练、仪表板和 API,这不仅可以使项目的交付更容易(因为它减少了出现错误的可能性),还可以确保一旦发现结果,就可以再次获得。

CI/CD —持续集成和持续部署

尽管数据科学家生成的许多见解和机器学习模型是有价值的,但当项目被困在不能被其他人使用的个人机器中时,它们无法为它们所插入的业务增加价值。因此,为了确保任何修改都被识别,并且其结果被扩展到其他团队,有持续集成和持续部署(CI/CD)的过程,这允许在项目的初始版本中测试和部署过程的自动化。

许多人可能熟悉 CI/CD 的概念,但是,用于此流程的许多工具都是付费的(例如 JenkinsCircleCITravisCI ),因此仅限于以下人员使用:

  • a)愿意支付这些工具的价格;
  • 或者 b)在已经开发了 CI/CD 循环的公司工作。

GitHub 和 GitHub 操作

GitHub 是一个知名的代码版本平台,拥有超过 4000 万用户和超过 1 亿个存储库,是一个巨大的开源代码来源,可供世界各地成千上万的人使用。

GitHub 于 2019 年创建了 GitHub Actions 工具,旨在支持开源项目的创建,并允许其用户抽象涉及 CI/CD 的流程。它允许用户定义的工作流的自动化,以帮助集成测试、拉请求的验证和许多其他特性。此外,用户使用的动作数量与日俱增,因为许多公司都在寻求开发工具来帮助用户社区。许多这些行动已经允许集成许多流行的工具,如 Docker,AWS CloudFormation,Terraform,以及许多其他可以在这里找到的工具。

尽管 GitHub Actions 只对非私有存储库免费使用,但在考虑使用任何 GitHub 企业工具之前,在私有项目中可以利用不同级别的使用。这打开了一扇门,使得许多开发开源项目的人可以测试他们的工具,并以更加自动化和可扩展的方式传播他们的发现。

Docker 登录& Docker 构建和推送

与 GitHub 操作一起使用的工具之一是存储库中的登录操作,它允许存储 Docker 映像(如 AWS 的 Docker HubECR、GCP 的GCR),以及构建这些映像,而无需占用用户的机器。考虑到这些,在 CI 工作流文件中声明了两个操作,可以在这些链接中找到: docker/login-actiondocker/build-push-action

将数据科学融入 CI/CD 流程

数据科学领域充满了不同的框架、依赖项和不同的语言,可以根据数据科学家的需求和能力来使用,但其中一个共同的事实是,它们都有可能被容器化过程封装,这有助于确保项目的可重复性。

考虑到这一点,我用来部署 GitHub Actions 自动化工具的例子涉及到使用 R 的 Shiny 库开发一个 web 应用程序。然而,同样的工作流实现可以用于部署使用 Python 的 FastAPI 开发的 API,即,或任何其他可以封装在 Docker 容器中的框架。

项目可以在这里找到:paeselhz/ghActionsDockerShiny。我不会详细介绍应用程序的开发,因为我使用的例子相对简单,没有详细的开发。本文的重点是项目的容器化,以及构建图像并将其存储在 Docker Hub 中的工作流自动化,以供进一步下载。

创建 Dockerfile 文件

对于那些熟悉 Docker 文件及其语法的人来说,它的执行与使用 Docker 在本地开发、构建和运行的项目中的预期是一样的。在其中,我们声明了将用于进一步安装库和依赖项的基本映像,以及项目的配置、文件复制和其他通常可以添加到 Dockerfile 中的步骤。

FROM rocker/shiny:4.0.0RUN apt-get update \
  && apt-get install -y \
    libxml2-dev \
    libglpk-dev \
  && install2.r \
    --error \
    dplyr \
    shiny \
    purrr \
    highcharter \
    shinyWidgets \
    shinycssloaders \
    devtools \
    xml2 \
    igraph \
    readr

RUN R -e "devtools::install_github('wilsonfreitas/rbcb')"COPY . /srv/shiny-serverRUN chmod -R 777 /srv/shiny-server

这个脚本位于项目根目录中,负责收集已经安装了 Shiny 及其依赖项的映像,并安装将由 r。

创建工作流文件

为了让 GitHub Actions 知道工作流自动化需要采取哪些步骤,有必要在项目中创建一个文件,该文件位于。github/workflows/main.yml,文件语法与任何 YAML 文件相同,易于编码。如果用户不想在本地完成这个过程并提交更改,GitHub 本身有一个在线代码编辑器来创建工作流。

在这个文件中声明了一些步骤,例如工作流的名称、将用于部署工作流执行的触发器以及它将负责执行的作业。文件的名称和触发器部分是高度可定制的,用户可以以多种方式对其进行更改,此外,在作业部分,需要几个步骤来完成作业:登录 Docker Hub,配置 BuildX(将用于构建映像的工具),配置 QEMU(将允许多平台构建的工具),将构建的映像部署到 Docker Hub,注销并清理机器以确保没有进程仍在运行。

# Setting up a Workflow to work with Github Actionsname: ci# Controls to when trigger the GH Action
# Below are configurations to the following triggers:
# - commits at master branch
# - tag commits at the project
# - scheduled to run at 01:00GMT
# The user can also configure triggers at pull requests
# as well as remove branches from triggering GH Actions
on:
  push:
    branches: [ master ]
    tags: [ '*.*.*' ]
  schedule:
    - cron: '0 1 * * *'# Below there is the job configuration to build the image
# and push it to a DockerHub repository
jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v2
      -
        name: Prepare
        id: prep
        run: |
          DOCKER_IMAGE=<USER_NAME>/<REPOSITORY_NAME>
          VERSION=noop
          if [ "${{ github.event_name }}" = "schedule" ]; then
            VERSION=nightly
          elif [[ $GITHUB_REF == refs/tags/* ]]; then
            VERSION=${GITHUB_REF#refs/tags/}
          elif [[ $GITHUB_REF == refs/heads/* ]]; then
            VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
            if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then
              VERSION=edge
            fi
          fi
          TAGS="${DOCKER_IMAGE}:${VERSION}"
          if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
            MINOR=${VERSION%.*}
            MAJOR=${MINOR%.*}
            TAGS="$TAGS,${DOCKER_IMAGE}:${MINOR},${DOCKER_IMAGE}:${MAJOR},${DOCKER_IMAGE}:latest"
          elif [ "${{ github.event_name }}" = "push" ]; then
            TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
          fi
          echo ::set-output name=version::${VERSION}
          echo ::set-output name=tags::${TAGS}
          echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
      -
        name: Set up QEMU
        uses: docker/setup-qemu-action@v1
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      -
        name: Login to DockerHub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.prep.outputs.tags }}
          labels: |
            org.opencontainers.image.title=${{ github.event.repository.name }}
            org.opencontainers.image.description=${{ github.event.repository.description }}
            org.opencontainers.image.url=${{ github.event.repository.html_url }}
            org.opencontainers.image.source=${{ github.event.repository.clone_url }}
            org.opencontainers.image.version=${{ steps.prep.outputs.version }}
            org.opencontainers.image.created=${{ steps.prep.outputs.created }}
            org.opencontainers.image.revision=${{ github.sha }}
            org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}

工作流代码几乎没有外部依赖性,假定 Docker 图像名称及其标签的创建在该代码中,然而,它需要一对秘密来登录 Docker Hub,在这种情况下,需要 Docker 使用的用户名和一个令牌来登录 Docker Hub ( 可以在这里生成)。有了用户名和令牌,用户只需进入他们的存储库,在“设置”选项卡中,在“机密”子页中添加令牌,如下图所示:

通过这些步骤,项目应该能够使用 GitHub 动作来执行,从而允许构建、测试和部署过程的自动化。

在本文使用的示例中,最终的映像可以在 Docker Hub 这里找到,并通过运行以下命令进行本地测试:

` docker run-p 3838:3838 lhzpaese/GH actions _ docker _ shiny:latest '

来源:

本文中使用的许多项目和工具的参考都是以超链接的形式随文本一起添加的。然而,我想离开 Docker 网上研讨会,它是这个项目的灵感来源,由 metcalfc 主持,他在会上介绍了用于集成 GitHub Actions 和 Docker 的工具。

感谢您的关注,如有任何问题或建议,请随时通过https://www.linkedin.com/in/lhzpaese/paeselhz 联系我。

使用 Github 页面创建全局 API

原文:https://towardsdatascience.com/using-github-pages-for-creating-global-api-76b296c4b3b5?source=collection_archive---------17-----------------------

这篇文章是关于创建一个全球托管的静态 API 并将其用于你的网站的 API。

所以,我意外地发现,当我们访问任何一个 GitHub Pages 站点时,我们可以返回 JSON 响应,而不是标准的 HTML。所以,我想与其创建一个静态网站,为什么不做一些开发的乐趣;)并为我需要的数据创建一个 REST API。

例如,如果您在终端上键入以下内容

**curl https://gauravghati.github.io/apis/home.json**

给出以下结果:

{
    "name": "Gaurav Ghati",
    "phone": "+91 9067365762",
    "mail": "gauravghati225@gmail.com",

    "bio": "MY BIO",
    "fun": "FUN STATEMENT",

    "github": "https://github.com/gauravghati/",
    "twitter": "https://twitter.com/GauravGhati/",
    "linkedin": "http://linkedin.com/in/gauravghati/",

    "aboutme": {
        "para1": "This is Paragraph 1",
        "para2": "This is Paragraph 2"
    },

    "volunteer": {
        "tfi": {
            "heading": "Teach For India",
            "description": "description of TFI"
        },
        "pisb": {
            "heading": "PICT IEEE Student Branch",
            "description": "description of PISB"
        }
    }
}

我在我的网站上使用了这个 JSON API,现在每次当网站加载时,它都会请求 Github pages 链接,从那个 URL 获取数据,并在网站上显示获取的数据!

现在,每当我想编辑我网站上的任何数据时,我只需更改 JSON 文件并将它们推送到我的 Github 页面 repo ,这些更改就会自动显示在主网站上。

可以查看网站的代码,这里。在姜戈。

现在,如何创建自己的 API?

通过使用 Github,我们可以在那里托管我们的 JSON 文件数据,因为它是一个静态站点,唯一的限制是它只支持 GET 请求。

设置 Github 页面

在 Github 上创建一个新的存储库,存储库名称为"Github _ username . Github . io"之后,您的新 GitHub 页面库就准备好了。该存储库的 URL 将是

https://github _ username . github . io/

准备 JSON 文件

克隆上述存储库,或者如果您已经在本地保存了 JSON 文件,运行该文件夹中的 git init 并添加上述远程存储库,然后提交并推送您的。json 文件。将文件推送到那里后,您可以在点击 URL 时访问 JSON 响应:

https://github _ 用户名. github.io/json_file.json

通过 JSON API 访问数据

我们点击 URL 后得到的数据是 JSON 对象的形式。我们可以在呈现 HTML 页面时直接将它发送到前端。

当你的后端在 python 中,即在 Django 或 Flask 中,你可以访问 JSON 文件的数据,如下所示:

import urllib.request, jsongithub_link = "https://github_username.github.io/json_file.json"with urllib.request.urlopen(github_link) as url: 
   json_data = json.loads(url.read().decode())print(json_data)

这段代码将输出所有 JSON 数据。

如果您想要使用 JQuery 获取 JavaScript 中的数据,您可以访问 JSON 文件的数据,如下所示:

let github_link = ‘https://github_username.github.io/jsonfile.json';$.getJSON(github_link, function(data) {
     //data is the JSON string
     console.log(data);
});

这就是你如何使用 Github 页面来创建全局 API。

感谢您的阅读,您可以通过 LinkedInTwitter 或我的作品集与我联系

使用 Google BigQuery 分析 EBI 小分子数据库的数据

原文:https://towardsdatascience.com/using-google-bigquery-to-analyze-data-from-the-ebi-small-molecules-database-2dcabdc73bc9?source=collection_archive---------32-----------------------

分析欧洲生物信息学研究所 CheMBL 数据集

曼努埃尔·盖辛格在的照片

CheMBL 是一个生物活性分子的药物发现数据库。它汇集了化学、生物活性和基因组学信息,目标是帮助将基因组信息转化为有效的新药。在这篇文章中,我们将对 CheMBL 数据库中的数据样本进行探索性的数据分析。

要访问这些数据,您需要创建一个 Google Cloud 帐户,安装 Google BigQuery 并生成认证凭证。操作说明可以在这里找到。

我们开始吧!

首先,让我们导入必要的 google 身份验证和 BigQuery 包:

from google.oauth2 import service_account
from google.cloud import bigquery

接下来,我们需要导入熊猫并将列设置定义为“None”。我们这样做是为了显示所有的列,因为 pandas 默认情况下会截断它们。

import pandas as pd
pd.set_option("display.max_columns", None)

接下来,我们定义密钥路径、客户端凭证和客户端对象:

key_path = "key_path/key.json"credentials = service_account.Credentials.from_service_account_file(
    key_path,
    scopes=["[https://www.googleapis.com/auth/cloud-platform](https://www.googleapis.com/auth/cloud-platform)"],
)client = bigquery.Client(
    credentials=credentials,
    project=credentials.project_id,
)

接下来,我们定义数据集引用,其中我们传入数据库的名称“ebi_chembl”和项目的名称“patents-public-data”:

dataset_ref = client.dataset("ebi_chembl", project="patents-public-data") 

我们可以看看有空的桌子。共有 229 张桌子,但我们将查看前 10 张桌子的名称:

columns = [x.table_id for x in client.list_tables(dataset_ref)][:10]
print(columns)

让我们看看列表中的第一个元素“action_type”:

table_ref = dataset_ref.table('action_type')
table = client.get_table(table_ref)

我们可以将结果存储在数据帧中,并打印前五行:

df = client.list_rows(table).to_dataframe()
print(df.head())

我们可以从 python 中的 collections 模块导入 counter 方法来分析动作类型的分布:

from collections import Counter
print(Counter(df['action_type'].values))

让我们看看接下来的十张表,看看是否有更详细的信息:

columns = [x.table_id for x in client.list_tables(dataset_ref)][10:20]
print(columns)

让我们看一下“化验分类”表:

table_ref = dataset_ref.table('assay_classification')
table = client.get_table(table_ref)df = client.list_rows(table).to_dataframe()
print(df.head())

让我们用计数器来看看“l1”最常见的 3 个值:

from collections import Counter
print(Counter(df['l1'].values).most_common(3))

对于“l2”:

from collections import Counter
print(Counter(df['l2'].values).most_common(3))

我们也可以将这些输出可视化。让我们定义一个函数,它将类别字符串作为输入,并显示三个最常见值的条形图:

def plot_most_common(category):
    bar_plot = dict(Counter(df[category].values).most_common(3))
    plt.bar(*zip(*bar_plot.items()))
    plt.show()

现在让我们用“l1”调用函数:

plot_most_common('l1')

和“l2”:

plot_most_common('l2')

在这篇文章中,我们讨论了如何使用 Google BigQuery 访问欧洲生物信息学研究所 CheMBL 数据库。该数据库包含化学,生物活性和基因组信息,可以帮助小分子药物的发现。我们对 action_type 和 assay_classification 表进行了一些基本分析,并生成了一些可视化结果。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

使用谷歌位置历史分析健身房访问

原文:https://towardsdatascience.com/using-google-location-history-to-analyze-gym-visits-a8c7c18e0871?source=collection_archive---------28-----------------------

Sebastian Hietsch 在 Unsplash 上拍摄的照片

除了搜索目的地之外,谷歌地图还有其他用途吗

我 2019 年的新年决心之一是更经常地去健身房。作为一个分析数据集的狂热爱好者,我决定利用我一直在收集的位置历史,调查一下去年我在这项工作中的一致性和成效。对于那些不知道的人来说,谷歌记录了你访问过的所有地方(当然,前提是你给予许可!).自从 2013 年我拥有第一部 Android 手机以来,我已经为谷歌分配了存储我的位置历史的权限,因此检索 JSON 中的数据并不困难。使用 Python 和 Matplotlib 来分析数据,我决定看看我去年在遵守决心方面有多成功。

起初,这些数据对我来说没有多大意义。但是一个小小的研究让我了解了错综复杂的数据。这些数据包含了我自 2013 年以来去过的所有地方的纬度、经度和日期。此外,它还包括几个描述“精度”、“速度”、“高度”、“航向”、“垂直精度”的栏目,这些在当前的上下文中对我没有用。因此,第一项任务是把数据整理成我能理解的东西。

来自 JSON 的原始数据帧

除了将“timestampMs”转换为相应的日期和时间,我还删除了“精度”、“速度”、“高度”、“航向”和“垂直精度”列,以创建一个经过处理的数据帧。第二个挑战是瞄准我的健身房的坐标来过滤数据帧。我不得不承认这是分析中最具挑战性的部分之一。纬度和经度是地理坐标系统的一部分,用来确定地球上任何地方的位置。

在谷歌地图上看到的健身房位置

我的健身房由北纬 50° 06 ' 53.5 "和东经 8° 41 ' 12.9 "描述。但是,使用它们,我无法从我的位置历史记录中筛选出与我的健身房相对应的数据点。为了找到一个大概的经纬度范围进行过滤,我把目光对准了谷歌为我 12 月 6 日的锻炼记录的坐标。

为我在 2019 年 12 月 6 日的锻炼处理数据帧

使用与我的健身房相对应的坐标范围,以及相应的日期和时间信息,我终于可以开始了。使用一些数据预处理技巧,为了方便起见,我添加了一些列,这将使分析变得稍微容易一些。使用“日期”列,我将日期和月份信息添加到数据框中。我还添加了我在坐标对上花费的时间,以分钟为单位。

在研究数据以获得进一步的见解之前,我必须解决某些障碍:

  1. 并非所有数据都是 100%准确的,因为收集的数据取决于几个因素,包括但不限于持续的互联网连接和手机 GPS 定位。当然没有办法克服这个丢失数据的问题。
  2. 数据点有可能会受到附近地区的影响。例如,我在体育馆下面的地铁站等火车也可能被错误地解释为我在体育馆度过的时间。为了解决这个问题,我简单地排除了坐标对持续时间少于 15 分钟的数据点。

现在我已经获得了我需要的必要信息,即日期和时间,我可以更深入地研究这些数据了。

使用熊猫图书馆的 unique(),我确定在去年的 365 天中,我去了健身房 119 次。更深入地说,我决定调查每次锻炼需要多长时间,并研究一天中的时间、一周中的日子和一个月中的趋势。

18:00 似乎是我最喜欢的锻炼时间

那么我在健身房锻炼的平均时间是多少呢?一天中的大部分时间,我要么在大学,要么在语言学校,要么在工作,这可能解释了为什么我锻炼的高峰时间会在 17:00 或 18:00 左右。在假期里,也有几天我会稍微早一点去健身房。还有一个异常点,我 00:00 在体育馆。

一周中的哪一天我更有可能去健身房?星期二!。令人惊讶的是,我最自由的日子是我很少去健身房的日子。然而,作为辩护,大多数周末也是我计划旅行的日子。然而,事后看来,人们可以得出这样的结论:随着时间的推移,我的动力会逐渐减弱。

2019 年每周每天的健身房访问量

查看我每月的访问量也发现了一些有趣的事实。十月是访问量最大的月份。

2019 年每月健身房访问量

然而,当它们被绘制成每次访问所花费的平均时间时,表格完全翻转过来,其中七月名列榜首。七月是夏天来临的时候,宜人的天气加上即将到来的暑假增加了在健身房锻炼的动力。虽然我在 10 月份去健身房的次数增加了,但我忙于应付工作和学校,没有时间在健身房呆很长时间(也降低了温度!).由于考试带来的压力,二月和三月不见了。

2019 年每月在健身房花费的平均时间

健身房的订阅费是每月 15€,去年 119 次,每次大约花费我 2€。不管有没有商业意识,收益远远超过了成本,所以对我来说,这是值得的。

从 2018 年的 20 天到 2019 年的 119 天,我认为去年是一个很大的进步。我后悔没有通过一个应用程序来跟踪我的成长,这可以给我一个关于我的习惯如何影响我的整体健康的整体信息。然而,我已经开始采取措施来整合它们,并希望能够在 2020 年给你一个更好的画面。感谢您的阅读。如果你有任何问题,你一定可以联系我。

大规模使用谷歌趋势

原文:https://towardsdatascience.com/using-google-trends-at-scale-1c8b902b6bfa?source=collection_archive---------20-----------------------

https://trends.google.com/trends/explore?geo=US&q =古驰米兰,时尚

当你的问题对他们面向公众的网站来说太大时,如何使用谷歌趋势

谷歌趋势是公众对一个话题的兴趣的有力衡量标准,换句话说,就是它的炒作;然而,它的设置方式使得它很难在最简单的应用程序之外使用。在这篇文章中,我们深入研究了 Google Trends 到底在测量什么,并探索了当你的问题超过了面向公众工具的五个主题限制时如何使用它。

谷歌趋势到底衡量什么?

Google Trends 给你一个在一段时间内给定搜索词的搜索量的标准化测量。来自他们的常见问题:

每个数据点除以它所代表的地理和时间范围的总搜索次数,以比较相对受欢迎程度。否则,搜索量最大的地方总是排名最高。

然后,根据一个主题在所有主题的所有搜索中所占的比例,将结果数字在 0 到 100 的范围内进行缩放。— 谷歌趋势常见问题解答

也就是说,对于你搜索中的每一个词,谷歌会找到你的词在每个地区和时间段的搜索量相对于该地区和时间段所有搜索量的比例。它将所有这些指标合并成一个单一的受欢迎程度指标,然后对你的主题进行分级,因此最大的指标被设置为 100。

简而言之:谷歌趋势并不能准确地告诉你你的主题出现了多少次搜索,但它确实给了你一个很好的代理。

是什么让谷歌趋势变得棘手?

尽管 Google Trends 可以方便地快速把握互联网的脉搏,但服务本身的结构使大规模应用变得困难,原因有二:

1.五趋势极限

目前,面向公众的谷歌趋势网站不允许超过五个词的查询。如果你想探索任何真实世界的问题,这显然是有限制的。例如,在民主党初选的早期,你不可能轻易地比较所有主要候选人的受欢迎程度。

2.相对度量

对这个限制的明显反应是,“好吧,我就用多重查询”。但是正如我们前面提到的,所有的搜索都被缩放到你的查询中最高容量的主题,所以如果两个不同的查询没有相同的最大主题,它们是不可比较的。

怎样才能一次看五个以上的趋势?

最后一句话是关键:

如果他们没有相同的最大话题

只要两个查询共享相同的最受欢迎的主题,它们将以相同的方式缩放,因此趋势将是可比较的。接下来,如果你想在谷歌趋势中比较五个以上的主题,你只需要在每个搜索中包含一个控制主题。

例如,在最近的一个项目中,我和我的团队想要比较网飞和亚马逊内容的受欢迎程度。我们浏览了两个平台的最佳内容列表,并找出了每个标题相对于单词“法国”的谷歌趋势指标。

我们到底是如何决定使用“法兰西”这个词的?

这有点像菜谱:你想要比你感兴趣的所有条款都更稳定、更安全的东西。

利用领域知识,我知道哪些图书会有特别高的搜索量,所以我寻找一个比我们最受欢迎的图书更受欢迎的词,但不要太受欢迎以至于我会丢失关于不太受欢迎的图书的任何真实信息。例如,“比特币”的搜索量如此之高,以至于许多标题被归一化为零(见我这里的意思)。

我可以通过编程来实现吗?

是的。

对于流媒体项目,我使用了 Python 库 pytrends ,这是 Google Trends 的一个非官方 API。下面您可以找到一个快速片段来帮助您开始:

import pytrends kw_list = ##list of topics I wanted to search
trends = dict()for i in kw_list:
   ##build out query
   pytrends.build_payload([i,'France'], cat=0,timeframe='today 5-y')

   ##save trend to dictionary
   trends[i] = pytrends.interest_over_time()[i]

您应该注意到,如果您尝试对多个查询执行此操作,您将会相对较快地达到 Google 的 DoS(拒绝服务)限制,因此您会希望在每个查询之间暂停循环几秒钟。

总而言之:

  1. 谷歌趋势很好地代表了公众利益
  2. 要使多个查询具有可比性,请包含一个在每次搜索中最大的控制项
  3. 使用 pytrends 来自动化该过程

使用这种方法,您现在可以回答一些大问题,如:与竞争对手的产品相比,我的产品有多受欢迎,最近的广告活动对不同产品类别的兴趣有没有不同的影响…

总之:五个字的限制不再是你的了。

在 R 中使用梯度推进机器进行分类

原文:https://towardsdatascience.com/using-gradient-boosting-machines-for-classification-in-r-b22b2f8ec1f1?source=collection_archive---------30-----------------------

机器学习中的分类方法

背景

分析目的:

了解推动学生成功的因素,以便开放大学能够分配资源来提高学生的成功

数据集描述:

开放大学学习分析数据集是一个公开可用的数据集,包含七个选定课程(模块)的课程、学生及其与 VLE 互动的数据。

由于所有数据表的唯一标识符是学生 ID、模块和课程描述,因此数据在这一级进行聚合。分析中使用了以下变量:

变量名称变量类型变量名称变量类型id _ 学生唯一标识符/主键 id 段分类代码 _ 模块分类段分类代码 _ 演示分类前一次尝试的次数数字学习者分类学分分类区域分类残疾分类最高教育分类最终结果数字总和加权分数数字平均模块长度数字平均提交持续时间数字平均比例内容访问数字平均日期注册数字修整评估类型分类

方法学

数据转换

  • 基于唯一标识符(学生 ID、课程和代码描述)组合数据集
  • 在唯一标识符级别聚合变量
  • 将名义变量类型从字符更新为因子

预测值:

  • 代码模块
  • 代码呈现(更改为 1 = B 和 2 = J),
  • 性别、地区、最高教育程度、IMD 级别、年龄级别、先前尝试次数、学习学分、
  • 平均提交持续时间(平均为单个代码演示可以有多个提交持续时间不同的评估),
  • 平均模块长度,
  • 被访问内容的平均比例(基于“ou”内容和资源/测验/词汇表的总点击数除以每个代码描述的总点击数),
  • 平均注册日期和
  • 修剪评估类型(因为一个代码描述可以有多个评估和多个相同类型的评估。确定每门课程的评估类型和描述是否能推动学生取得成功是很重要的)

排除变量:学号和加权总分

排除原因:学号——识别信息,加权分数总和——与最终结果相关

定义“成功”目标变量

  • 响应变量:成功(“通过”或“区别”=“是”、“失败”或“撤回”=“否”

不使用“最终结果”作为成功因素的原因:数据集中有不成比例的“通过”记录,因此降低了预测“失败”、“撤回”和“区别”的模型准确性。因此,响应变量是二进制的。

  • 检查将输入模型的每个变量的空值、缺失值和异常值

为什么我们要检查丢失的数据?—如果大部分数据缺失/为空,样本的代表性不足以提供准确的结果。当缺失/空数据由于有效原因而缺失时,情况并非如此。

数据设置

将数据分为名义预测值和二元预测值的训练集和测试集,以测试模型的准确性

为什么我们要将数据分为训练集和测试集—模型是在训练集上训练的。在测试集上测试模型的准确性,以确定模型在预测它没有“看到”的数据的成功与否方面的能力。

数据建模

  • 分析方法:梯度推进机(GBM)
  • 也可以使用其他方法,但模型精度足够好——分布式随机森林(DRF)和广义线性模型(GLMNET)
  • 使用混淆矩阵(即正确预测的比例)和曲线下面积(与随机机会相比,我们做得有多好)检查模型的准确性

输出

  • 最佳预测值(按可变重要性分数)

数据 ETL

所有数据探索、转换和最终数据集的加载都在 Alteryx 中完成,以避免编写代码并测试其基本数据转换步骤的功能,这些步骤通常在 R 中执行,如 joins、mutate 和 group by。

首先,我通过 ID 评估(主键)连接了三个数据集— assessments.csv、studentAssessments.csv 和 courses.csv。

接下来,我设计了两个特性:加权分数和提交持续时间。日期通常没有意义,除非它们被转换成有用的特征。课程描述只有两个类别,为了便于参考,我将其转换为 1 和 2。

下一步是添加学生与虚拟学习平台(VLE)互动的数据。为了便于分析,进行了一些功能工程,例如创建一个名为 activity_type_sum 的新变量,将活动类型中的类别数量减少为两大类——内容访问和浏览。这样做的原因是粒度类别只会产生更多的特征,并减少每个类别的观察数量。点击次数由活动类型特征相加。还计算了与浏览和内容访问相关的活动占总活动的比例。这是创建与另一个要素相关并按总活动比例缩放的要素的好方法,从而确保所有学生都按其活动类型以相似的比例表示。

使用 student_id、code_module 和 code_presentation 作为主键将块 1 连接到块 2。结果输出如下所示。

上面的输出(块 3)使用 student_id、code_module 和 code_presentation 将学生注册数据连接起来,以显示 data_registered 字段。

date_unregistered 字段被忽略,因为它有许多缺失值。此外,未注册字段单元格为空的学生将撤回作为其最终结果的值。这个变量是我们的目标/响应变量。因此, date_unregistered 字段似乎是 final_result 的代理度量,因此从我们的分析中排除该变量是有意义的。

如上图,对于给定的 id_student、code_module、code_presentation,重复 module_presentation length、proportion_content、date_registration。由于我们希望拥有唯一的记录,我们可以将数据汇总如下:

  • 使用总和总结加权分数
  • 平均提交持续时间
  • 平均模块呈现(您也可以使用其他聚合,如最小值、最大值和中值)
  • 比例 _ 内容 _ 访问的平均值
  • 平均日期 _ 注册

数据现在处于学生标识、代码模块、代码演示和评估类型级别;但是,目标变量—最终结果—处于学生标识、代码模块和代码演示级别。因此,需要进一步汇总这些数据。

先看学生信息。这里的唯一记录是 id_student、code_module、code_presentation。
因此,我们需要后退一步,总结出一个 student_id、code_module 和 code_presentation 来代表个人进行的所有评估。我们仍将使用之前的汇总公式。

通过这样做,我们有 8 种独特的评估类型,学生可以针对给定的代码模块和代码演示进行评估。评估类型不重复(仅微调),因此如果学生参加了 3 次 TMA,则不会反映出来,如下所示。

可以创建一个变量来计算每种评估类型的评估数量,但它会包含许多缺失值,因为并非所有评估都具有所有三种评估类型。
现在,我们准备好加入学生信息数据,输出如下所示。

现在,我们有 18 列。
我们被告知,如果在二月和十月进行演示,演示可能会有所不同。我们将假设每年没有不同(即 2013 年 b 与 2014 年 b 相同)。因此,我们将把 code_presentation 作为二进制变量重新编码为 1 代表 B,2 代表 J。

最终输出如下所示。

终于到了一些数据探索的时候了。

探索性数据分析

分类变量可以用条形图表示,其中 y 轴是给定类别出现的频率。例如,在下面的图表中,我们可以看到最常使用的代码模块是 FFF ,其次是 BBB。有七个唯一的代码模块,没有缺失值。

对于连续变量,可以使用五点汇总法,对于分类变量,可以使用模式进行数字汇总,如下所示。

从下面的总结中我们可以看出,更常见的学生是苏格兰男性学生,他们没有残疾,Imd_band 在 20%到 40%之间,最终结果是典型的及格。

现在,我们可以开始对数据集建模了。

机器学习模型

我们被要求帮助开放大学更好地理解学生的成功。我们将假设学生的成功是通过最终结果来衡量的,其中通过和优秀是“成功”的指标,撤回和失败是“不成功”的指标。
对于独立变量,我们将使用之前表格中的所有变量,除了 weighted_score。这是因为加权分数决定了给定学生的最终成绩。因此,它与最终结果高度相关(多重共线性),因此将被排除。
学生证是识别信息,因此不会被用作预测因素。

GBM(梯度增强模型)被用作选择的模型。这种类型的模型创建了一系列弱学习器(浅树),其中每个新树都试图提高前一个树的错误率。最终的树是具有最低错误率的树。这是一种集成机器学习方法,因为创建了几棵树来提供最终结果。然而,不像在 randomForest 中,这些树是串行创建的,而不是并行创建的。此外,这些树不是独立的,而是依赖于前一个树的错误率,而后面的三个树将更努力地改善对更困难情况的预测。这是由一个称为学习率的参数控制的。

该模型运行了 500 轮(500 棵树),树的最小和最大深度为 4。通常,拥有非常深的树并不好,因为这会导致算法试图解释数据集中的每个观察值的过度拟合,因为它增加了树的深度,导致叶子包含非常少量的符合给定规则的观察值。

我们可以从上面的输出中看到,该模型的 RMSE(均方根误差)值为 0.55,这是相当高的。它特别不擅长预测区分失败,这可能是由于数据集的不平衡,从我们的探索性数据分析中我们知道通过是最常见的最终结果。

为了解决这一不平衡问题,目标变量被重新定义为“成功”(优秀和及格)和“失败”(不及格和退出)。组合类别来处理不平衡数据集是很常见的。其他方法是欠采样(即减少最频繁类的实例数量)或过采样(即为非频繁类创建人工观察)。

模型重新运行,输出如下。在这里,我们可以看到平均每类误差显著下降。曲线下面积(AUC)是另一个准确性指标,它告诉您模型在正确分类病例方面有多好(即最大化真实阳性率(TPR))。AUC 越高,模型越精确。因为 AUC 是在 0 和 1 之间测量的,所以 0.87 的 AUC 是相当好的。

分类问题中常用的另一个度量是 F1 分数,它是精确度和召回率的调和平均值。这两个指标都旨在最大化 TPR,同时最小化假阴性率(召回率)或假阳性率(精确度)。真正的肯定是成功或失败被正确分类。一个假阴性是成功被贴上失败的标签。一个假阳性是当一个失败被贴上成功的标签。F1 分数要高,精度和召回率都要高。

混淆矩阵表明总错误率为 17.11%,这主要是由模型在成功分类方面的良好程度决定的。该模型不太擅长对故障进行分类,错误率为 39.07%。同样,这可能是由于“遍数”过多地代表了数据。因此,应谨慎对待结果,并使用更平衡的数据集重新运行模型。

现在,让我们通过查看变量重要性列表来看看成功或失败的主要预测因素。

  • 前 3 个变量是代码模块、trimmed_assessment_type 和平均提交持续时间。
  • 预测给定学生是否会取得成功的最后 3 个变量是残疾状况、平均注册日期和性别。
  • 注意:由于代码模块和代码表示是唯一标识符的一部分,它们应该被排除在分析之外。然而,由于一些课程在二月和十月的介绍可能不同,两个变量都保留在模型中。排除这些变量可能会提高准确性或使其他变量变得更“重要”。

现在,让我们来看看最重要的预测信息,以便更好地理解这个模型。下面的堆积条形图显示了课程模块和 final_result 的记录比例。我们可以推断,与其他课程相比,学生更有可能成功完成 AAA、EEE 和 GGG 课程。

  • 从上表中,我们可以看到,如果考试是给定课程和演示文稿的唯一评估,那么成功率是 100%。
  • 如果只有计算机标记的评估构成了课程的组成部分,那么不及格率和退学率就非常高。调查为什么将 CMA 作为演示评估的一部分会导致成功率下降是很有趣的。

上面的直方图显示了成功和失败的平均提交持续时间。

似乎当学生成功时,他们更有可能在评估提交日期后的 10 天(+/-)内提交作业。

包扎

机器学习被用来快速识别学生成功的主要因素。

模型改进的建议包括:

  • 使用平衡数据集
  • 包括数据集中资源分配的替代措施
  • 按课程类型统计评估数量,并作为
    功能进行演示
  • 删除相互关联的分类变量(即使用卡方独立性检验)

希望您现在对利用 GBM 解决分类问题、分类问题的缺陷(即不平衡数据集)以及各种准确性指标的使用有了更好的理解。

我的 git 资源库中提供了所有 R 代码的参考:https://github.com/shedoesdatascience/openlearning

基于结构化文档的图卷积神经网络信息抽取

原文:https://towardsdatascience.com/using-graph-convolutional-neural-networks-on-structured-documents-for-information-extraction-c1088dcd2b8f?source=collection_archive---------5-----------------------

将结构化文档转换为图形的代码,以及对 GCN 实现的全面调查,以对其中的文本实体进行分类

虽然像 CNN 这样的深度学习解决方案可以有效地捕获欧几里得空间中的数据模式,但越来越多的应用程序以图形的形式表示数据,缺乏网格状的规律性。由于图形可能是不规则的,它们可能具有可变大小的无序节点,并且每个节点可能具有不同数量的邻居,导致诸如卷积之类的数学运算难以应用于图形域。

这种非欧几里德数据的一些例子包括:

  • 蛋白质-蛋白质相互作用数据其中分子之间的相互作用被建模为图形
  • 引文网络其中科学论文是节点,引文是单向或双向边
  • 社交网络,网络上的人是节点,他们的关系是边

本文特别讨论了在发票和账单等结构化文档中使用图形卷积神经网络(GCNs ),通过学习文本实体之间的位置关系来自动提取有意义的信息。讨论了多种方法以及将文档转换为图形的方法,因为文档本身没有类似图形的布局。

什么是图?

图表表示为:

在哪里,

现在让我们,

表示一个节点,

表示从 v_i 指向 v_j 的一条边。那么,邻接矩阵 A 是一个 n x n 矩阵,

其中 n 是图中节点的数量。

此外,图可以具有节点属性,

其中 X 是节点特征矩阵,

代表节点 v 的特征向量。

以下是一些没有自循环的无向图及其对应的邻接矩阵的图示:

礼貌:mathworld.wolfram.com

这些节点中的每一个还可以具有由特征矩阵表示的节点属性或特征向量:

四个节点中的每一个都具有三维特征向量的示例

在社交网络的真实世界示例中,节点属性可以是关于人(节点)的人口统计信息,并且他们的关系可以是边。请务必阅读这篇文章,深入了解图表。

如何将结构化文档转换成图形?

当涉及标题和表格等实体时,发票和账单等结构化文档有一定的出现顺序。例如,在大多数发票中,表格位于页眉和页脚之间。此外,总金额通常位于表格的右下角,这是我们希望提取的发票的一个重要实体。这种重复出现的结构信息以及文本属性可以帮助图形神经网络学习邻域表示,并作为结果执行节点分类。

但是我们如何以图形格式获得这样的文档呢?

答案是 OCR 加上几何算法。

光学字符识别:

在这一步,我们使用商业上可用的 OCR 引擎,如 Amazon TextractGoogle Vision API 来生成所谓的文档对象图。

地图中的每个“对象”都是一个文本实体——一个单词或一个句子。在本文的上下文中,我们将对象映射称为具有五列的 Pandas 数据帧——xmin、ymin、xmax、ymax、object。

(xmin,ymin)和(xmax,ymax)给出了矩形边界框的坐标,而 Object 列给出了该框内的实际文本。

下面是 GitHub 的一个要点,给出了文档图像的路径和 Google Vision API 实例的端点 URL。

生成“对象图”的 OCR 脚本

对象映射到图形:

一旦我们生成了对象图,我们就利用基于可见性的几何算法将对象彼此连接起来,从而形成一个图,其中对象作为节点,连接作为

几何算法:基于可见性连接对象

*Input*: 
  Object Map*Output*: 
  Graph Dictionary: {source_node: [dest_node_1, dest_node_2, …], ..}
  Text List: Text for each node

第一步:从左上角的物体开始。对于地图中的每个对象,迭代所有其他对象。
步骤 2:对于每个物体,向右看。
第三步:如果另一个物体直接可见,建立连接。第四步:对于每个物体,直接看下面。
第五步:如果另一个物体直接可见,建立联系。

输出是字典的形式,因此我们可以使用网络分析的 Python 库 networkx 来构建一个图。此外,我们返回文本列表,以便使用每个节点的文本作为其特征向量。

下面是 Python 实现

接下来,我们使用 networkx 为文档创建一个邻接矩阵 A 以及一个特征矩阵 X ,该矩阵是通过在字符级别对每个文本对象进行计数矢量化而生成的。

下面是同样的代码

最终结果,我们希望文档看起来像这样:

将发票绘制成图表

用于信息抽取的文档图上的卷积

一旦我们有了图表形式的文档,下一步就是将这些数据提供给 GCN。在这里,我们可以从许多 GCN 实现中进行选择,其中最著名的如下所述:

GraphSAGE —大型图上的归纳表征学习:
论文: arXiv
网站:snap.stanford.edu
代码: GitHub 插图:

归纳表征学习。

图卷积神经网络半监督分类: 论文: arXiv
网站:tkipf . GitHub . io
代码: GitHub 插图:

具有一阶滤波器的多层 GCN。

以下是特别适用于从发票中提取信息的 GCNs 的实现:

结构化文档中的表格理解: 论文: arXiv
描述:
本文中 GCN 的实现并不严格遵循图卷积的数学定义,而是对于每个节点,将图中连通节点的特征向量简单地与所考虑节点的特征向量串接起来。因此,为每个节点形成新的聚集特征向量,然后简单地通过完全连接的层馈送该向量。

图形神经网络在发票单据中的表格检测: 纸张: ICDAR 幻灯片:priba . github . io
代码:虽然代码不是作者提供的,但这里是我的入门代码同样。 插图:

模型架构概述,P. Riba 等人。

实验

所有这些方法都给出了令人鼓舞的结果。例如,下面是一个样本发票的半监督分类方法的输出:

半监督分类的输出

由于半监督学习的范例规定,在学习表示时,标记的和未标记的节点都出现在图中,所以在作为图的单个发票上观察到该输出。

总共有四个类——信息、联系人、表项和其他(未显示)。最初为每个类标记了一组种子节点。然后,GCN 能够从这些初始种子节点中学习未标记节点的表示。

可以通过使用标准 NLP 技术首先识别种子节点,然后将图馈送到网络来实现端到端解决方案。这里最值得注意的特性是能够以非常低的延迟为以前看不见的文档生成节点表示。

结论

图卷积神经网络被证明在新的应用中越来越有用,其中数据采用连接主义结构。此外,在结构化文档的情况下,具有空间意义的数据可以适用于图形结构,然后与 gcn 一起使用。

半监督学习可以在静态图上动态使用,以生成节点的表示,而不需要大的训练集。超越非欧几里德数据的普通 CNN 为应用研究的新领域打开了令人兴奋的机会。

这里是截至 2019 年对图神经网络的全面调查,供进一步阅读。

达瓦尔·波特达尔是一名数据科学爱好者,拥有人工智能研究的经验。他毕业于 Udacity 的数据分析师机器学习工程师纳米学位,目前在孟买的一家分析初创公司工作。我越来越擅长处理杂乱的数据,并将其转化为有价值的东西。在 LinkedIn 上与我联系,并查看我的 GitHub 以获得更多酷项目。

使用 GridSearchCV

原文:https://towardsdatascience.com/using-gridsearchcv-76614defc594?source=collection_archive---------44-----------------------

学习一种可靠的工具来减少机器学习中的歧义

作为一个仍处于数据科学职业生涯初期的人,无论我构建的模型是什么类型,使用的数据集有多大,或者我需要完成的交付成果是什么,都有一个与数据打交道的元素始终悬在我的头上:模糊性。事实上,数据只不过是存储在令人困惑的文件、文件夹、存储库等矩阵中的数字和字母。我们去哪里以及我们用这些数据做什么是完全没有脚本的(字面上..哈..哈)。然而,这也是数据的美妙之处。我们可以做任何我们想做的事情来帮助理解它!随着我的技能的进步和一些舒适的感觉,我已经意识到绝对总是有一个解决方案或路线来帮助减轻数据的混乱。

分类建模

分类机器学习是数据科学中一种非常强大的算法,也是我花了无数时间绞尽脑汁的一种算法。更新一下,分类是一种机器学习技术,它允许我们获取大量数据和数据特征,并输出某种分类。例如,如果我们有一个数据集,给出了过去一周的所有天气特征(温度、湿度、紫外线指数等。)我们可以使用分类模型来预测明天是雨天还是晴天。

有许多类型的分类模型,从逻辑回归到 boosting 方法,但每一种都包含大量的模糊性,因为它们不是一刀切的。它们包含元素、参数和超参数,需要根据您的数据需求进行调整。幸运的是,正如我们之前所说的,总有办法减轻歧义!朋友和陌生人,相约格子搜索

阿曼德·库利在 Unsplash 上拍摄的照片

参数与超参数

在我们真正开始网格搜索之前,了解参数和超参数之间的区别非常重要。这两个词经常被互换使用,但实际上,它们是完全不同的。超参数是不能在模型中直接学习的模型元素。也就是说,它们是模型的用户输入,然后帮助确定模型的性能。参数更加通用,因为它们是超参数和其他与模型不直接相关的元素的组合。用更好的话来说,超参数是模型无法学习的,而参数是模型在训练阶段学习的。

from sklearn.tree import DecisionTreeClassifierX = pd.DataFrame(['Feat1'],['Feat2'],[...])
y = pd.Series('target')dt = DecisionTreeClassifier(
                           criterion = 'entropy',
                           max_depth = 6,
                           min_samples_split = 10,
                           random_state = 33
)dt.fit(X,y)

在上面的代码中,我们看到了一个决策树分类模型的实例。我们向决策树对象传递了几个参数。“Criterion”和“max_depth”都是超参数的实例,因为它们影响我们的模型学习的方式。

使用网格搜索

作为我们模型的输入,超参数现在变得极其重要。每个模型都有多个超参数可供使用,并且这些超参数都有多个输入可能性。如果我们允许上面的模型有太高的深度,或者太多的节点,我们的模型将会过拟合。但是,如果我们没有足够的,这将是欠适合。当我们增加 min_samples_split 时,max_depth 如何影响输出的精度?模型中的参数不是相互独立的。真正了解的唯一方法是尝试所有这些方法的组合!组合网格搜索是解决这些新问题的最佳方式,可以为我们的模型及其数据找到超参数和参数的最佳组合。

GridSearchCV 来自 sklearn 库,让我们能够网格搜索我们的参数。它通过将 K-Fold 交叉验证与参数(模型)的网格相结合来运行。我们将网格构造成一个字典(键=参数名,值=我们组合的不同可能性),然后将它传递给我们的估计器对象。

拟合多个倾向模型,并选择最佳表现的一个来实现利润优化

原文:https://towardsdatascience.com/using-h2o-and-dalex-to-estimate-the-likelihood-to-purchase-a-financial-product-a53f891f0d1?source=collection_archive---------7-----------------------

用 h2o 和 DALEX 评估购买金融产品可能性的案例研究

丹·梅耶斯在 Unsplash 上的照片

在当今时代,利用数据来理解客户行为驱动因素的企业拥有真正的竞争优势。通过有效地分析客户层面的数据,组织可以显著提高其在市场中的表现,并将精力集中在那些更有可能参与的客户身上。

从数据中梳理这种洞察力的一种经过试验和测试的方法是 倾向建模 ,它结合了以下信息:a 客户的人口统计数据(年龄、种族、宗教、性别、家庭规模、种族、收入、教育水平)心理图(社会阶层、生活方式和个性特征)参与度(打开的电子邮件、点击的电子邮件、移动应用程序上的搜索、网页停留时间等)。)、用户体验(客户服务电话和电子邮件等待时间、退款次数、平均运送时间)用户行为(不同时间尺度上的购买价值、最近一次购买后的天数、报价和转换之间的时间等)。)来估计某个客户档案执行某类行为(例如购买产品)的可能性。

一旦你了解了某个客户与某个品牌互动、购买某个产品或注册某项服务的可能性,你就可以利用这些信息来创造情景,比如最小化营销支出,最大化收购目标,以及优化电子邮件发送频率折扣深度

项目结构

在这个项目中,我正在分析一家银行向现有客户销售定期 a 存款的直接营销活动的结果,以确定哪种类型的特征更有可能让客户做出反应。营销活动基于电话,有时需要与同一个人进行多次联系。

首先,我将进行广泛的数据探索,并使用结果和见解为分析准备数据。

然后,我正在估算模型的数量,并使用模型不可知的方法评估它们的性能和对数据的拟合,这使得能够比较传统的“玻璃箱”模型和“黑箱”模型

最后,我将拟合一个最终模型,该模型结合了探索性分析的结果和模型选择的洞察力,并使用该模型进行收入优化

数据

library(tidyverse)
library(data.table)
library(skimr)
library(correlationfunnel)
library(GGally)
library(ggmosaic)
library(knitr)
library(h2o)
library(DALEX)
library(knitr)
library(tictoc)

数据是来自 UCI 机器学习库葡萄牙银行营销 集,描述了一家葡萄牙银行机构开展的旨在向其客户销售定期存款/存单的直接营销活动。营销活动基于 2008 年 5 月至 2010 年 11 月期间对潜在买家的电话访问。

我正在使用的数据(bank-direct-marketing . CSV)bank-additional-full . CSV的修改版本,包含 41,188 个带有 21 个不同变量的示例(10 个连续变量,10 个分类变量加上目标变量)。具体来说,目标订阅是一个二进制响应变量,表示客户是否订阅(‘是’或数值 1)定期存款(‘否’或数值 0),这使得这成为一个 二进制分类问题

这些数据需要一些操作才能变成可用的格式,详情可以在我的网页上找到: 倾向建模——数据准备和探索性数据分析在这里,我简单地在我的 GitHub repo 上为这个项目 加载我托管的预清理数据

data_clean <- 
  readRDS(file = "https://raw.githubusercontent.com/DiegoUsaiUK/Propensity_Modelling/master/01_data/data_clean.rds")

探索性数据分析

虽然是任何数据科学项目不可或缺的一部分,并且对分析的完全成功至关重要,但探索性数据分析(EDA) 可能是一个极其耗费人力和时间的过程。近年来,旨在加速这一过程的方法和库大量涌现,在这个项目中,我将对其中一个“街区上的新孩子”(correlation funnel)进行采样,并将其结果与更传统的 EDA 相结合。

相关通道

通过 3 个简单的步骤correlationfunnel可以生成一个图表,按照与目标变量绝对相关性的降序排列预测值。漏斗顶部的特征预计在模型中具有更强的预测能力。

这种方法提供了一种快速确定所有变量的预期预测能力等级的方法,并给出了任何模型中哪些预测因子应该具有强/弱特征的早期指示。

data_clean %>%  

  binarize(n_bins = 4, # bin number for converting features
           thresh_infreq = 0.01 # thresh. for assign categ.             
                                # features into "Other"
          ) %>% # correlate target variable to features 
  correlate(target = subscribed__1) %>%      # correlation funnel visualisation
  plot_correlation_funnel()

放大前 5 个特征,我们可以看到,在以下情况下,某些特征与目标变量(订购定期存款产品)的相关性更大:

  • 与客户最后一次电话联系的时间是 319 秒或更长
  • 客户最后一次联系后经过的days数量大于 6
  • previous营销活动的结果是success
  • 就业人数为 509.9 万或更多
  • 3 个月欧元银行同业拆放利率的值为 1.344 或更高

相反,漏斗底部的变量,如星期几住房贷款。与目标变量相比,变化非常小(即:它们非常接近响应的零相关点)。因此,我不认为这些特性会影响响应。

功能探索

在这种视觉相关性分析结果的指导下,我将在下一节中继续探索目标和每个预测因素之间的关系。为此,我将寻求优秀的 GGally 库的帮助,用Ggpairs可视化相关矩阵的修改版本,用 ggmosaic 包绘制mosaic charts,这是检查两个或更多分类变量之间关系的好方法。

目标变量

首先,目标变量** : subscribed显示出强烈的阶级不平衡,在无类别中有近 89%到有类别中有 11%。**

我将在建模阶段通过在 h2o 中启用重新采样来解决类不平衡。这将通过“收缩”主要类别(“否”或“0”)来重新平衡数据集,并确保模型充分检测哪些变量在驱动“是”和“否”响应。

预言者

让我们继续看一些数字特征:

尽管关联漏斗分析显示持续时间具有最强的预期预测能力,但它在通话前是未知的(显然在通话后是已知的),并且几乎不提供可操作的洞察力或预测价值。因此,它应该从任何现实的预测模型中被丢弃,并且不会在这个分析中被使用。

****年龄的密度图与目标变量相比具有非常相似的方差,并且以相同的区域为中心。基于这些原因,应该不会对认购产生太大影响。

尽管本质上是连续的, pdaysprevious 实际上是分类特征,并且都是强烈右偏的。由于这些原因,需要将它们离散成组。这两个变量也适度相关,表明它们可能捕捉到相同的行为。

接下来,我将银行客户数据马赛克图表可视化:

根据相关渠道的调查结果,工作教育婚姻默认都显示出与目标变量相比的良好变化水平,表明它们会影响响应。相比之下,住房贷款位于漏斗的最底部,预计对目标几乎没有影响,因为按“已认购”响应划分时变化很小。

****默认只有 3 个“是”级别的观察值,这些观察值将被滚动到最不频繁的级别,因为它们不足以做出正确的推断。住房贷款变量的“未知”级别有少量观察值,将被归入第二小类别。最后,工作、教育也将从最不常见水平的分组中受益。

继续讨论其他活动属性:

虽然在原则上是连续的,战役在本质上是更明确的,并且是强烈右倾的,将需要被离散成组。然而,我们已经从早期的相关性分析中了解到,在任何模型中,这都不是一个强有力的变异驱动因素。

另一方面, poutcome 是期望具有强预测能力的属性之一。级别的不均匀分布表明将最不常见的级别(成功scs)归入另一个类别。然而,联系以前购买过定期存款的客户是具有最高预测能力的特征之一,需要保留不分组。

然后,我在看最后一条联系信息:

****联系月份应影响响应变量,因为它们与目标相比都有较好的变化水平。也将受益于最不常见级别的分组。

相比之下,星期几似乎不会影响响应,因为水平之间没有足够的变化。

最后但同样重要的是,社会和经济属性:

与目标变量相比,所有的社会和经济属性都显示出良好的变化水平,这表明它们都应该对响应产生影响。它们都显示出高度的多模态,并且在密度图中不具有均匀的分布,并且将需要被分箱。

同样值得注意的是,除了消费者信心指数之外,所有其他社会和经济属性彼此之间都有很强的相关性,这表明只有一个属性可以被包括在模型中,因为它们都在“拾取”相似的经济趋势。

探索性数据分析总结

  • 使用相关通道进行的相关性分析有助于确定所有变量的预期预测能力等级
  • ****期限与目标变量的相关性最强,而一些银行客户数据如住房贷款的相关性最弱
  • 但是,持续时间而不是用于分析,因为它在调用前是未知的。因此,它提供的可操作的洞察力或预测价值非常少,应该从任何现实的预测模型中丢弃
  • 目标变量 subscribed 显示了强烈的类别不平衡,其中近 89%的没有变动,这需要在建模分析开始之前解决
  • 大多数预测者将受益于最不常见水平的分组
  • 进一步的特征探索揭示了大多数社会和经济背景属性彼此之间有很强的相关性,这表明在最终模型中只能考虑其中的一部分

最终数据处理和转换

根据探索性数据分析的结果,我将最不常见的级别组合到“其他”类别中,将除age之外的所有变量设置为无序因子( h2o 不支持有序分类变量),并缩短一些分类变量的级别名称,以方便可视化。你可以在我的网页上找到所有细节和完整代码: 倾向建模——数据准备和探索性数据分析

在这里,我简单地在我的 GitHub repo 上加载最终的数据集托管 :

data_final <- 
  readRDS(file = "https://raw.githubusercontent.com/DiegoUsaiUK/Propensity_Modelling/master/01_data/data_final.rds")

建模策略

为了坚持合理的项目运行时间,我选择了 h2o 作为我的建模平台,因为它提供了许多优势:

  • 它非常容易使用,你可以立刻评估几个机器学习模型****
  • 不需要通过“二进制化”字符/因子变量来预处理它们(这是在“内部”完成的),这进一步减少了数据格式化时间
  • 它有一个功能,即处理在数据探索阶段突出显示的类不平衡——我只是在模型规范中设置了balance_classes = TRUE,稍后会详细介绍
  • ****可以启用交叉验证,而不需要从训练集中“分割”出一个单独的validation frame
  • ****超参数微调(也称为网格搜索)可以与多种策略一起实施,以确保运行时间在不影响性能的情况下得到限制

使用 h2o 构建模型

我开始用rsample创建一个随机的训练和验证集,并将它们保存为train_tbltest_tbl

set.seed(seed = 1975) train_test_split <-
  rsample::initial_split(
    data = data_final,     
    prop = 0.80   
  ) train_tbl <- train_test_split %>% rsample::training() 
test_tbl  <- train_test_split %>% rsample::testing()

然后,我启动一个 h2o 集群。我将内存集群的大小指定为“16G”,以帮助加快速度,并关闭进度条。

# initialize h2o session and switch off progress bar
h2o.no_progress() 
h2o.init(max_mem_size = "16G")

接下来,我整理出响应和预测变量集。对于要执行的分类,我需要确保响应变量是因子(否则 h2o 将执行回归)。这是在数据清理和格式化阶段解决的。

# response variable
y <- "subscribed"# predictors set: remove response variable
x <- setdiff(names(train_tbl %>% as.h2o()), y)

拟合模型

对于这个项目,我正在估算一个广义线性模型(又名弹性网)、一个随机森林(其中 h2o 指的是分布式随机森林)和一个梯度推进机(或 GBM)。

为了实现对tree-based模型(DRF 和 GBM)的网格搜索,我需要建立一个随机网格来搜索h2o.grid()函数的最优超参数。为此,我从定义要传递给hyper_params参数的搜索参数开始:

  • sample_rate用于设置每棵树的行采样率
  • col_sample_rate_per_tree定义了每棵树的列抽样
  • max_depth指定最大树深
  • min_rows确定每片叶子的最小观察次数
  • mtries(仅 DRF)表示在树的每个节点上随机选择的列
  • learn_rate(仅适用于 GBM)指定建立模型时模型学习的速率
# DRF hyperparameters
hyper_params_drf <- 
  list(
     mtries                   = seq(2, 5, by = 1), 
     sample_rate              = c(0.65, 0.8, 0.95),
     col_sample_rate_per_tree = c(0.5, 0.9, 1.0),
     max_depth                = seq(1, 30, by = 3),
     min_rows                 = c(1, 2, 5, 10)
   )# GBM hyperparameters
hyper_params_gbm <- 
  list(
    learn_rate               = c(0.01, 0.1),
    sample_rate              = c(0.65, 0.8, 0.95),
    col_sample_rate_per_tree = c(0.5, 0.9, 1.0),
    max_depth                = seq(1, 30, by = 3),
    min_rows                 = c(1, 2, 5, 10)
    )

我还为search_criteria参数设置了第二个列表,这有助于管理模型的估计运行时间:

  • strategy参数设置为 RandomDiscrete ,以便搜索从网格搜索参数中随机选择一个组合
  • stopping_metric设置为 AUC,作为提前停止的误差度量——当度量停止改善时,模型将停止构建新的树
  • 使用stopping_rounds,我将指定考虑提前停止前的训练轮数
  • 我使用stopping_tolerance来设置继续训练过程所需的最小改进
  • max_runtime_secs将每个型号的搜索时间限制为一小时****
search_criteria_all <- 
   list(
      strategy           = "RandomDiscrete",
      stopping_metric    = "AUC",    
      stopping_rounds    = 10,
      stopping_tolerance = 0.0001,
      max_runtime_secs   = 60 * 60
   )

最后,我可以建立模型的公式。请注意,所有模型都有两个共同的参数:

  • nfolds参数,使交叉验证能够在不需要 validation_frame 的情况下执行——例如,如果设置为 5,它将执行 5 重交叉验证
  • balance_classes参数设置为,以解决探索性分析期间突出显示的目标变量的不平衡。启用后,h2o 将对多数类进行欠采样或对少数类进行过采样。
# elastic net model 
glm_model <- 
  h2o.glm(
    x               = x,
    y               = y, 
    training_frame  = train_tbl %>% as.h2o(),
    balance_classes = TRUE,
    nfolds          = 10,
    family          = "binomial",
    seed            = 1975
    )# random forest model
drf_model_grid <- 
  h2o.grid(
    algorithm       = "randomForest", 
    x               = x, 
    y               = y,
    training_frame  = train_tbl %>% as.h2o(),
    balance_classes = TRUE, 
    nfolds          = 10,
    ntrees          = 1000,
    grid_id         = "drf_grid",
    hyper_params    = hyper_params_drf,
    search_criteria = search_criteria_all,
    seed            = 1975
    )# gradient boosting machine model
gbm_model_grid <- 
  h2o.grid(
    algorithm       = "gbm",
    x               = x, 
    y               = y,
    training_frame  = train_tbl %>% as.h2o(),
    balance_classes = TRUE, 
    nfolds          = 10,
    ntrees          = 1000,
    grid_id         = "gbm_grid",
    hyper_params    = hyper_params_gbm,
    search_criteria = search_criteria_all,
    seed            = 1975
    )

我通过 AUC 分数对基于树的模型进行排序,并从网格中检索主要模型

# Get the DRM grid results, sorted by AUC 
drf_grid_perf <- 
  h2o.getGrid(grid_id     = "drf_grid",
               sort_by    = "AUC",
               decreasing = TRUE)# Fetch the top DRF model, chosen by validation AUC
drf_model <- 
  h2o.getModel(drf_grid_perf@model_ids[[1]])# Get the GBM grid results, sorted by AUC 
gbm_grid_perf <- 
  h2o.getGrid(grid_id     = "gbm_grid",
               sort_by    = "AUC",
               decreasing = TRUE)# Fetch the top GBM model, chosen by validation AUC
gbm_model <- 
  h2o.getModel(gbm_grid_perf@model_ids[[1]])

性能评价

有许多库(比如 IMLPDPVIPDALEX 等等,但更受欢迎)帮助机器学习可解释性特性解释一般性能评估,它们在最近几年都变得越来越受欢迎。

有多种方法来解释机器学习结果(即局部可解释的模型不可知解释部分依赖图基于排列的变量重要性),但在这个项目中,我检查了DALEX包,该包侧重于模型不可知的可解释性,并提供了一种跨具有不同结构的多个模型比较性能的便捷方式。

DALEX使用的模型不可知方法的一个关键优势可以在相同的尺度上比较传统“玻璃箱”模型和黑箱模型的贡献。然而,由于是基于排列的,它的主要缺点之一不能很好地适应大量的预测变量和大型数据集。****

DALEX 程序

目前DALEX不支持一些更新的 ML 包,如 h2oxgboost 。为了使它与这样的对象兼容,我遵循了 Bradley Boehmke 在他杰出的研究 中阐述的程序,用 DALEX 建模可解释性,从中我获得了很多灵感并借鉴了一些代码。

首先,数据集需要采用特定的格式:

# convert feature variables to a data frame
x_valid <- 
     test_tbl %>% select(-subscribed) %>% as_tibble()# change response variable to a numeric binary vector
y_valid <-   
     as.vector(as.numeric(as.character(test_tbl$subscribed)))

然后,我创建一个 predict 函数,返回一个数值向量,该向量提取二进制分类问题的响应概率。

# create custom predict function
pred <- function(model, newdata)  {
  results <- as.data.frame(h2o.predict(model, 
                                        newdata %>% as.h2o()))
  return(results[[3L]])
  }

现在,我可以用explain()函数将我的机器学习模型转换成 DALEK“解释器”,它充当参数的“容器”。

# generalised linear model explainer
explainer_glm <- explain(
  model            = glm_model, 
  type             = "classification",
  data             = x_valid,
  y                = y_valid,
  predict_function = pred,
  label            = "h2o_glm"
  )# random forest model explainer
explainer_drf <- explain(
  model            = drf_model, 
  type             = "classification",
  data             = x_valid,
  y                = y_valid,
  predict_function = pred,
  label            = "h2o_drf"
  )# gradient boosting machine explainer
explainer_gbm <- explain(
  model            = gbm_model, 
  type             = "classification",
  data             = x_valid,
  y                = y_valid,
  predict_function = pred,
  label            = "h2o_gbm"
  )

评估模型

最后,我准备将解释器对象传递给几个 DALEX 函数,这些函数将帮助评估和比较不同模型的性能。考虑到性能指标可能反映模型预测性能的不同方面,在评估模型时评估和比较几个指标非常重要使用 DALEX 您可以做到这一点!

为了评估和比较我的模型的性能,我从 Przemyslaw BiecekTomasz Burzykowski 在他们的书 解释性模型分析 中使用的框架中获得了灵感,该框架是围绕关键问题构建的:

  • 1 —模型是否合适?
  • 2 —这些模型如何相互比较?
  • 3-哪些变量在模型中很重要?
  • 4 —单个变量如何影响平均预测?

1 —模型是否合适?

通用模型拟合

为了初步了解我的模型与数据的吻合程度,我可以使用简单明了的model_performance()函数,它计算选定的模型性能度量。

model_performance(explainer_glm)
## Measures for:  classification
## recall   : 0 
## precision: NaN 
## f1       : NaN 
## accuracy : 0.8914653 
## auc      : 0.7500738
## 
## Residuals:
##          0%         10%         20%         30%         40%         50% 
## -0.48867133 -0.16735197 -0.09713539 -0.07193152 -0.06273300 -0.05418778 
##         60%         70%         80%         90%        100% 
## -0.04661088 -0.03971492 -0.03265955  0.63246516  0.98072521model_performance(explainer_drf)
## Measures for:  classification
## recall   : 0.1700224 
## precision: 0.76 
## f1       : 0.2778793 
## accuracy : 0.9040913 
## auc      : 0.7993824
## 
## Residuals:
##          0%         10%         20%         30%         40%         50% 
## -0.87841486 -0.13473277 -0.07933048 -0.06305297 -0.05556507 -0.04869549 
##         60%         70%         80%         90%        100% 
## -0.04172427 -0.03453394 -0.02891645  0.33089059  0.98046626model_performance(explainer_gbm)
## Measures for:  classification
## recall   : 0.2192394 
## precision: 0.7340824 
## f1       : 0.33764 
## accuracy : 0.9066408 
## auc      : 0.7988382
## 
## Residuals:
##          0%         10%         20%         30%         40%         50% 
## -0.83600975 -0.14609749 -0.08115376 -0.06542395 -0.05572322 -0.04789869 
##         60%         70%         80%         90%        100% 
## -0.04068165 -0.03371074 -0.02750033  0.29004942  0.98274727

基于所有模型可用的指标(准确性AUC ),我可以看到弹性网梯度提升的表现大致相当,而随机森林紧随其后。AUC 的范围在. 78-.80 之间,而准确性的范围稍窄,为. 89-.90

残留诊断

如前一段所示,model_performance()还生成残差分位数,可以绘制这些分位数来比较模型间的绝对残差。

# compute and assign residuals to an object
resids_glm <- model_performance(explainer_glm)
resids_drf <- model_performance(explainer_drf)
resids_gbm <- model_performance(explainer_gbm)# compare residuals plots
p1 <- plot(resids_glm, resids_drf, resids_gbm) +
        theme_minimal() +
        theme(legend.position = 'bottom',
              plot.title = element_text(hjust = 0.5)) + 
        labs(y = '')
p2 <- plot(resids_glm, resids_drf, resids_gbm, geom = "boxplot") +
        theme_minimal() +
        theme(legend.position = 'bottom',
              plot.title = element_text(hjust = 0.5)) gridExtra::grid.arrange(p2, p1, nrow = 1)

鉴于中值绝对残差,DRF 和 GBM 模型的表现似乎不相上下。查看右侧的残差分布,您可以看到这两个模型的中值残差最低,GLM 的尾部残差数量较多。这也反映在左侧的箱线图中,其中基于树的模型都实现了最低的中值绝对残差值。

2 —这些模型如何相互比较?

ROC 和 AUC

接收器工作特性(ROC) 曲线是一种图形化方法,允许可视化针对随机猜测的分类模型性能,这由图上的条纹线表示。该曲线在 y 轴上绘制了真阳性率(TPR ),在 x 轴上绘制了假阳性率(FPR)。

eva_glm <- DALEX::model_performance(explainer_glm)
eva_dfr <- DALEX::model_performance(explainer_drf)
eva_gbm <- DALEX::model_performance(explainer_gbm)plot(eva_glm, eva_dfr, eva_gbm, geom = "roc") +
  ggtitle("ROC Curves - All Models",  
          "AUC_glm = 0.750  AUC_drf = 0.799  AUC_gbm = 0.798") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

从 ROC 曲线可以看出两个方面:

  • 直接阅读:所有模型的表现都比随机猜测要好
  • ****比较读数:AUC(曲线下面积) 总结了 ROC 曲线,可用于直接比较模型性能——完美的分类器 AUC = 1。

所有模型的表现都比随机猜测好得多,AUC 达到 0.75-. 80,其中 DRF 的得分最高,为 0.799。

3-哪些变量在模型中很重要?

可变重要性图

每种 ML 算法都有自己的方式来评估每个变量的重要性:例如,线性模型参考它们的系数,而基于树的模型则考虑杂质,这使得很难跨模型比较变量的重要性。

DALEX 通过置换计算可变重要性度量,这是模型不可知论,并允许在不同结构的模型之间进行直接比较。然而,当可变重要性分数基于排列时,我们应该记住,当特征数量增加时计算速度会变慢

我再次将每个模型的“解释器”传递给feature_importance()函数,并将n_sample设置为 8000,以使用几乎所有可用的观察值。尽管不算过分,总执行时间是将近 30 分钟,但这是基于相对较小的数据集和变量数量。不要忘记,计算速度可以通过减少n_sample来提高,这对较大的数据集尤其重要。

# measure execution time
tictoc::tic()# compute permutation-based variable importance
vip_glm <- 
     feature_importance(explainer_glm, 
                        n_sample      = 8000,
                        loss_function = loss_root_mean_square) vip_drf <- 
     feature_importance(explainer_drf, 
                        n_sample      = 8000, 
                        loss_function = loss_root_mean_square)vip_gbm <- 
     feature_importance(explainer_gbm, 
                        n_sample      = 8000, 
                        loss_function = loss_root_mean_square) # show total execution time
tictoc::toc()## 1803.65 sec elapsed

现在,我只需将 vip 对象传递给一个绘图函数:正如自动生成的 x 轴标签( Drop-out loss )所暗示的,变量重要性如何计算背后的主要直觉在于,如果移除所选解释变量的贡献,模型拟合度会降低多少。分段越大,从模型中删除该变量时的损失就越大。

# plotting top 10 feature only for clarity of reading
plot(vip_glm, vip_drf, vip_gbm, max_vars = 10) +
  ggtitle("Permutation variable importance", 
          "Average variable importance based on 8,000 
                                permutations") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

我喜欢这个情节,因为它汇集了丰富的信息。

首先,你可以注意到,尽管相对权重略有不同,但前 5 个特征对每个模型来说都是共同的,其中nr_employed(经济中使用的)是所有这些特征中唯一最重要的预测因素。这种一致性令人放心,因为它告诉我们,所有模型都在数据中选取相同的结构和交互,并向我们保证这些特征具有强大的预测能力。****

您还可以注意到 x 轴左边缘的不同起点,这反映了三个模型之间 RMSE 损失的差异:在这种情况下,弹性网模型具有最高的 RMSE,这表明在残差诊断中较早看到的较高数量的尾部残差可能不利于 RMSE 评分。

4 —单个变量如何影响平均预测?

部分依赖曲线

在我们确定了每个变量的相对预测能力后,我们可能想要调查它们与所有三个模型的预测反应之间的关系有何不同。部分相关(PD)图,有时也称为 PD 曲线,提供了一种很好的方法来检查每个模型如何响应特定的预测值。

我们可以从最重要的特性nr_employed开始:

**# compute PDP for a given variable
pdp_glm  <- model_profile(explainer_glm, 
                          variable = "nr_employed", 
                          type     = "partial")
pdp_drf  <- model_profile(explainer_drf, 
                          variable = "nr_employed", 
                          type     = "partial")
pdp_gbm  <- model_profile(explainer_gbm, 
                          variable = "nr_employed", 
                          type     = "partial")plot(pdp_glm$agr_profiles, 
     pdp_drf$agr_profiles, 
     pdp_gbm$agr_profiles) +
  ggtitle("Contrastive Partial Dependence Profiles", "") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))**

尽管平均预测权重不同,但所有三个模型都发现,当经济中的就业水平达到 509.9 万(nInf_5099.1)时,银行客户更有可能签署定期存款。弹性网随机森林都在我们在correlationfunnel分析中观察到的nr_employed(对随机森林不太明显)的 3 个不同级别中发现了完全相同的预测能力层级,其中 GBM 稍微有点失调。

现在让我们来看看age,一个预测器,如果你还记得 EDA,它预计不会对目标变量产生影响:

我们注意到的一件事是,平均预测(x 轴)的变化范围在年龄谱(y 轴)上相对较浅,证实了探索性分析的发现,即该变量的预测能力较低。此外, GBM随机森林都以非线性方式使用age,而弹性网模型无法捕捉这种非线性动态。

部分依赖图也可以作为一种诊断工具:查看poutcome(previous营销活动的结果)可以发现 GBMrandom forest 在前一个活动的结果成功(scs)时正确地获得了更高的签约概率。

然而,弹性网模型未能做到这一点,这可能是一个严重的缺陷,因为在以前的活动中,成功与目标变量有非常强的正相关性。

我将以month特性结束,因为它提供了一个很好的例子,在这种情况下,您可能希望用行业知识和一些常识来覆盖模型的结果。具体来说, GBM 模型似乎表明3 月10 月12 月是成功几率更大的时期。

根据我以前对类似金融产品的分析经验,我不建议银行机构在圣诞节前几周增加直接营销活动,因为这是一年中消费者注意力从这类购买转移的时期。

最终模型

总而言之,随机森林是我最终选择的模型:它看起来是三个中更平衡的,并且没有显示出像monthpoutcome这样的变量所看到的一些“奇怪之处”。

现在,我可以通过结合探索性分析的结果、模型评估的洞察力以及大量行业特定/常识考虑因素,进一步完善我的模型并降低其复杂性。

特别是我的最终型号:

  • 排除了许多预测能力较低的特征(agehousingloancampaigncons_price_idx)
  • 删除了previous,它在 PD 图中显示其 2 个级别之间的差异很小——它也与pdays适度相关,表明它们可能捕捉到相同的行为
  • 也下降了emp_var_rate,因为它与nr_employed有很强的相关性,也因为从概念上讲,它们控制着非常相似的经济行为
**# response variable remains unaltered
y <- "subscribed"# predictors set: remove response variable and 7 predictors
x_final <- 
     setdiff(names(train_tbl %>% 
                   select(-c(age, housing, loan, 
                             campaign, previous, 
                             cons_price_idx, emp_var_rate)) %>% 
                           as.h2o()), y)**

对于最终的模型,我使用了与原始随机森林相同的规范

**# random forest model
drf_final <- 
  h2o.grid(
     algorithm       = "randomForest", 
     x               = x_final, 
     y               = y,
     training_frame  = train_tbl %>% as.h2o(),
     balance_classes = TRUE, 
     nfolds          = 10,
     ntrees          = 1000,
     grid_id         = "drf_grid_final",
     hyper_params    = hyper_params_drf,
     search_criteria = search_criteria_all,
     seed            = 1975
   )**

同样,我们通过 AUC 分数对模型进行排序,并检索先导模型

**# Get the grid results, sorted by AUC 
drf_grid_perf_final <- 
  h2o.getGrid(grid_id = "drf_grid_final",
               sort_by = "AUC",
               decreasing = TRUE)# Fetch the top DRF model, chosen by validation AUC
drf_final <- 
  h2o.getModel(drf_grid_perf_final@model_ids[[1]])**

最终模型评估

为了简单起见,我用同名包中的vip()函数可视化变量重要性图,该函数返回每个变量的排名贡献。

**vip::vip(drf_final, num_features = 12) +
  ggtitle("Variable Importance", "") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))**

移除emp_var_rate使得education进入了前 10 名。可以理解的是,变量层次和相对预测能力有所调整和变化,但令人欣慰的是,看到其他 9 个变量在前一个模型的前 10 名中。

最后,我将该模型的性能与原始的随机森林模型进行比较。

**drf_final %>% 
  h2o.performance(newdata = test_tbl %>% as.h2o()) %>% h2o.auc()## [1] 0.7926509drf_model %>% 
  h2o.performance(newdata = test_tbl %>% as.h2o()) %>% h2o.auc()## [1] 0.7993973**

AUC 只变化了百分之几,这告诉我这个模型保持了它的预测能力。

关于部分相关图的一个重要观察

由于已经熟悉逻辑回归中的优势比,我开始了解同样的直觉是否可以扩展到黑盒分类模型。在我的研究过程中,一个关于 交叉验证 的非常有趣的帖子脱颖而出,它在来自决策树的 比值比和随机森林的 之间画出了一条平行线。

基本上,这告诉我们,部分依赖图可以以类似于优势比的方式来定义客户档案的哪些特征影响他/她执行某种类型行为的倾向。

例如,jobmonthcontact等特征将提供关于何时如何瞄准的上下文:

  • 查看job会告诉我们,担任管理员角色的客户比担任个体户角色的客户订阅的可能性大约高 25%。****
  • 在 10 月month和 10 月month与潜在客户取得联系,会比在 5 月取得积极成果的几率增加一倍以上。****
  • contacting与通过电话通话相比,您的客户通过手机进行订购的机会增加了近四分之一。****

请注意,所有最终模型预测值的部分相关图可以在我的网页上找到:在我的网页上: 倾向建模——估计几个模型,并使用模型不可知方法比较它们的性能

有了这样的洞察力,你就可以帮助制定整体营销和沟通计划,把重点放在更有可能订阅定期存款的客户身上。

然而,这些都是基于模型级别的解释器,它们反映了一个整体的、聚合的视图。如果您有兴趣了解模型如何对单次观察做出预测(即哪些因素会影响在单个客户级别参与的可能性),您可以求助于利用“本地模型”概念的 本地可解释模型不可知解释(LIME) 方法。我将在以后的文章中探讨 LIME 方法。

模型估计和评估综述

对于这个项目的分析部分,我选择 h2o 作为我的建模平台。h2o 不仅非常易于使用,而且具有许多内置功能,有助于加快数据准备:它处理类不平衡而不需要预先建模重采样,自动 __“二进制化”字符/因子 __ 变量,并实现交叉验证而不需要从训练集中“分割”出单独的validation frame

在建立了一个随机网格来让搜索最佳超参数之后,我估计了模型的数量(一个逻辑回归、一个随机森林和一个梯度提升机器),并使用 DALEX通过一系列指标来评估和比较它们的性能。该库采用了模型不可知的方法,使得能够在相同的规模上比较传统的“玻璃盒子”模型和“黑盒”模型

我最终选择的模型是随机森林,我通过结合探索性分析的结果、从模型评估中收集的洞察力以及大量行业特定/常识考虑因素,进一步完善了该模型。这确保了降低模型的复杂性,而不影响预测能力。

优化预期利润

现在我有了我的最终模型,拼图的最后一块是最终的“那又怎样?”这个问题让所有人都有了正确的认识。对客户签署定期存款的概率的估计可用于创建许多优化的场景,从最小化您的营销支出,最大化您的总体收购目标,到推动一定数量的交叉销售机会

在我这样做之前,有几个内务任务需要“设置工作场景”和几个重要的概念要介绍:

  • 阈值和 F1 分数
  • 精确度和召回率

阈值和 F1 分数

该模型试图回答的问题是"该客户是否在直接营销活动后注册了定期存款?”而截止值(也称为阈值)是将预测分为YesNo的值。

为了说明这一点,我首先通过将test_tbl数据集传递给h2o.performance函数来计算一些预测。

perf_drf_final <- 
    h2o.performance(drf_final, newdata = test_tbl %>% as.h2o()) perf_drf_final@metrics$max_criteria_and_metric_scores## Maximum Metrics: Maximum metrics at their respective thresholds
##                         metric threshold       value idx
## 1                       max f1  0.189521    0.508408 216
## 2                       max f2  0.108236    0.560213 263
## 3                 max f0point5  0.342855    0.507884 143
## 4                 max accuracy  0.483760    0.903848  87
## 5                max precision  0.770798    0.854167  22
## 6                   max recall  0.006315    1.000000 399
## 7              max specificity  0.930294    0.999864   0
## 8             max absolute_mcc  0.189521    0.444547 216
## 9   max min_per_class_accuracy  0.071639    0.721231 300
## 10 max mean_per_class_accuracy  0.108236    0.755047 263
## 11                     max tns  0.930294 7342.000000   0
## 12                     max fns  0.930294  894.000000   0
## 13                     max fps  0.006315 7343.000000 399
## 14                     max tps  0.006315  894.000000 399
## 15                     max tnr  0.930294    0.999864   0
## 16                     max fnr  0.930294    1.000000   0
## 17                     max fpr  0.006315    1.000000 399
## 18                     max tpr  0.006315    1.000000 399

与许多其他机器学习建模平台一样, h2o 使用与最大 F1 分数相关联的阈值,它只不过是精确度和召回率之间的加权平均值。在这种情况下,阈值@ Max F1 为 0.190

现在,我使用h2o.predict函数通过测试集进行预测。预测输出有三列:实际模型预测(predict),以及与该预测相关联的概率(p0p1,分别对应于NoYes)。如您所见,与当前截止相关的p1概率大约为 0.0646

drf_predict <- 
    h2o.predict(drf_final, 
                newdata = test_tbl %>% as.h2o())# I converte to a tibble for ease of use
as_tibble(drf_predict) %>%
  arrange(p0) %>% 
  slice(3088:3093) %>%
  kable() predict              p0              p1
      1       0.9352865       0.0647135   
      1       0.9352865       0.0647135   
      1       0.9352865       0.0647135   
      0       0.9354453       0.0645547   
      0       0.9354453       0.0645547   
      0       0.9354453       0.0645547

然而, F1 分数只是识别截止值的一种方式。根据我们的目标,我们也可以决定使用一个阈值,例如,最大化精度或召回率。

在商业设置中,预先选择的阈值@ Max F1 不一定是最佳选择:输入精度并调用

精确度和召回率

Precision 显示模型对误报的敏感程度(即预测客户在订购而他/她实际上没有),而 Recall 查看模型对误报的敏感程度(即预测客户在没有订购而他/她实际上打算这样做)。**

这些指标与商业环境非常相关,因为组织对准确预测哪些客户真正有可能subscribe (高精度)特别感兴趣,以便他们可以针对这些客户制定广告策略和其他激励措施。与此同时,他们希望尽量减少对被错误归类为subscribing (高召回)的客户的努力,这些客户反而不太可能注册。

然而,从下面的图表中可以看出,当精度变高时,召回率变低,反之亦然。这通常被称为精确-召回权衡

为了充分理解这种动态及其含义,让我们先来看看截止零点截止一点点,然后看看当您开始在两个位置之间移动阈值时会发生什么:

  • 阈值为零** ( 最低精度,最高召回)时,模型将每个客户分类为subscribed = Yes。在这种情况下,你会通过直接营销活动联系每一个客户,但是浪费了宝贵的资源,因为还包括了那些不太可能订阅的客户。很明显,这不是一个最佳策略,因为你会招致更高的总体采购成本。**
  • 相反,在阈值一** ( 最高精度,最低召回)模型告诉你没有人可能订阅,所以你应该不联系任何人。这将为你节省大量的营销成本,但如果你通过直接营销将定期存款通知了那些已经订阅的客户,你将错过他们带来的额外收入。再说一次,这不是最优策略。**

当移动到一个更高的阈值时,该模型在将谁归类为subscribed = Yes时变得更加“挑剔”。因此,你在联系谁的问题上变得更加保守(精确度更高)并降低了你的采购成本,但同时你也增加了无法接触到潜在订户的机会(召回率更低),错失了潜在的收入。

这里的关键问题是你停在哪里?有没有“甜蜜点”,如果有,你如何找到它?嗯,那完全取决于你想达到的目标。在下一部分,我将运行一个迷你优化,目标是最大化利润

寻找最佳阈值

对于这个小优化,我实施了一个简单的利润最大化,基于与获得新客户相关的一般成本和从所述获得中获得的利益。这可以发展到包括更复杂的场景,但这超出了本练习的范围。

为了理解使用哪个临界值是最佳的,我们需要模拟与每个临界点相关的成本效益。这是一个源自期望值框架的概念,参见 商业数据科学

为此,我需要两件东西:

  • 每个阈值的预期比率 —这些可以从混淆矩阵中检索
  • 每个客户的成本/收益 —我需要根据假设来模拟这些

预期利率

****使用h2o.metric可以方便地检索所有分界点的预期比率

# Get expected rates by cutoff
expected_rates <- 
  h2o.metric(perf_drf_final) %>%
    as.tibble() %>%
    select(threshold, tpr, fpr, fnr, tnr)

成本/收益信息

****成本效益矩阵是对四种潜在结果的成本和效益的商业评估。为了创建这样一个矩阵,我必须对一个组织在开展广告主导的采购活动时应该考虑的费用和优势做出一些假设。

让我们假设出售定期存款的成本为每位客户 30 英镑。这包括执行直接营销活动(培训呼叫中心代表,为主动呼叫留出时间,等等。)和激励措施,如为另一种金融产品提供折扣,或加入提供福利和津贴的会员计划。银行组织在两种情况下会产生这种类型的成本:当他们正确预测客户将会订阅时(真肯定,TP),以及当他们错误预测客户将会订阅时(假肯定,FP)。

我们还假设向现有客户出售定期存款的收入为每位客户 80 美元。当模型预测客户会订阅并且他们确实订阅时,组织将保证这一收入流(真正,TP)。

最后,还有真否定** (TN)场景,我们正确预测客户不会订阅。在这种情况下,我们不会花任何钱,但也不会获得任何收入。**

以下是成本情景的简要回顾:

  • 真正 (TP) — predict 会订阅,他们确实这么做了:COST:-30;第 80 版
  • 误报 (FP) — predict 会订阅,而实际不会:COST:-30;版本 0
  • 真负 (TN) — predict 不会订阅,他们实际上也没有:COST:0;版本 0
  • 假阴性 (FN) — predict 不会订阅,但他们确实订阅了:COST:0;版本 0

我创建了一个函数,使用阳性案例 (p1)的概率以及与真阳性 (cb_tp)和假阳性 (cb_fp)相关联的成本/收益来计算预期成本。这里不需要包括真阴性假阴性,因为它们都是零。

我还包括之前创建的 expected_rates 数据帧,其中包含每个阈值的预期速率(400 个阈值,范围从 0 到 1)。

# Function to calculate expected profit
expected_profit_func <- function(p1, cb_tp, cb_fp) {

    tibble(
        p1    = p1,
        cb_tp = cb_tp,
        cb_fp = cb_fp
        ) %>%

        # add expected rates
        mutate(expected_rates = list(expected_rates)) %>%
        unnest() %>%

        # calculate the expected profit
        mutate(
            expected_profit =   p1    * (tpr * cb_tp) + 
                             (1 - p1) * (fpr * cb_fp)
        ) %>%
        select(threshold, expected_profit)
}

多客户优化

现在为了理解多客户动态是如何工作的,我创建了一个假设的 10 个客户群来测试我的功能。这是一个简化的视图,因为我将相同的成本和收入结构应用于所有客户,但是成本/收益框架可以针对单个客户进行定制,以反映他们单独的产品和服务水平设置,并且可以轻松调整流程,以针对不同的 KPI 进行优化(如净利润CLV订阅数量等)。)

# Ten Hypothetical Customers 
ten_cust <- tribble(
    ~"cust",   ~"p1",  ~"cb_tp",  ~"cb_fp",
    'ID1001',   0.1,    80 - 30,     -30,
    'ID1002',   0.2,    80 - 30,     -30,
    'ID1003',   0.3,    80 - 30,     -30,
    'ID1004',   0.4,    80 - 30,     -30,
    'ID1005',   0.5,    80 - 30,     -30,
    'ID1006',   0.6,    80 - 30,     -30,
    'ID1007',   0.7,    80 - 30,     -30,
    'ID1008',   0.8,    80 - 30,     -30,
    'ID1009',   0.9,    80 - 30,     -30,
    'ID1010',   1.0,    80 - 30,     -30
)

我使用purrrexpected_profit_func()映射到每个客户,返回一个按阈值计算的每个客户预期成本的数据框架。这个操作创建了一个嵌套 tibble,我必须通过unnest()将数据帧扩展到一个级别。

# calculate expected cost for each at each threshold
expected_profit_ten_cust <- 
  ten_cust %>%
    # pmap to map expected_profit_func() to each item
    mutate(expected_profit = pmap(.l = list(p1, cb_tp, cb_fp), 
                                  .f = expected_profit_func)) %>%
    unnest() %>%
    select(cust, p1, threshold, expected_profit)

然后,我可以想象每个客户的预期成本曲线。

# Visualising Expected Cost 
expected_profit_ten_cust %>%
    ggplot(aes(threshold, expected_profit, 
               colour = factor(cust)), 
               group = cust) +
    geom_line(size = 1) +
    theme_minimal()  +
    tidyquant::scale_color_tq() +
    labs(title = "Expected Profit Curves",
         colour = "Customer No." ) +
    theme(plot.title = element_text(hjust = 0.5))

最后,我可以合计预期成本,可视化最终曲线,并突出显示最佳阈值。

# Aggregate expected cost by threshold 
total_expected_profit_ten_cust <- expected_profit_ten_cust %>%
    group_by(threshold) %>%
    summarise(expected_profit_total = sum(expected_profit)) # Get maximum optimal threshold 
max_expected_profit <- total_expected_profit_ten_cust %>%
    filter(expected_profit_total == max(expected_profit_total))# Visualize the total expected profit curve
total_expected_profit_ten_cust %>%
    ggplot(aes(threshold, expected_profit_total)) +
    geom_line(size = 1) +
    geom_vline(xintercept = max_expected_profit$threshold) +
    theme_minimal() +
    labs(title = "Expected Profit Curve - Total Expected Profit",
         caption  = paste0('threshold @ max = ', 
                          max_expected_profit$threshold %>% round(3))) +
    theme(plot.title = element_text(hjust = 0.5))

这有一些重要的商业含义。根据我们的假设的 10 个客户群,选择优化的阈值0.092将产生近 164 的总利润,相比之下,与自动选择的0.190截止点相关的总利润近 147

这将导致的额外预期利润,接近每位客户1.7 英镑。假设我们有大约 500,000 的客户群,转换到优化型号可以产生额外的850,000的预期利润!

total_expected_profit_ten_cust %>% 
  slice(184, 121) %>%
  round(3) %>%
  mutate(diff = expected_profit_total - lag(expected_profit_total)) %>% 
  kable()threshold     expected_profit_total        diff 
    0.190                   146.821          NA 
    0.092                   163.753      16.932

显而易见,根据企业的规模,潜在利润增长的幅度可能是显著的。

结束语

在这个项目中,我使用了一个公开可用的数据集来估计银行现有客户在直接营销活动后购买金融产品的可能性。

在对数据进行彻底的探索和清理之后,我估计了几个模型使用 DALEX 库比较了它们的性能和对数据的拟合,该库侧重于模型不可知的可解释性。其关键优势之一是能够在相同的尺度上比较传统“玻璃箱”模型和黑箱模型的贡献。然而,由于是基于排列的,它的一个主要缺点是它不能很好地扩展到大量的预测器和更大的数据集。

最后,我用我的最终模型实现了一个多客户利润优化,它揭示了一个潜在的额外预期利润每个客户近 1.7(或者 85 万如果你有 50 万客户群)。此外,我还讨论了关键概念,如阈值和 F1 分数以及精确-召回权衡,并解释了为什么决定采用哪个截止值非常重要。

在探索和清理数据、拟合和比较多个模型并选择最佳模型之后,坚持使用默认阈值@ Max F1 将达不到最终的“那又怎样?”这使得所有的艰苦工作都有了前景。

****最后一件事:完成后不要忘记关闭 h2o 实例!

h2o.shutdown(prompt = FALSE)## [1] TRUE

代码库

完整的 R 代码和所有相关文件可以在我的 GitHub profile @ 倾向建模 中找到

参考

原载于 2020 年 5 月 1 日https://diegousai . io**

使用沙漏网络理解人类姿势

原文:https://towardsdatascience.com/using-hourglass-networks-to-understand-human-poses-1e40e349fa15?source=collection_archive---------5-----------------------

一个简单易懂的深入研究沙漏网络背后的人类姿态估计理论

人体姿态估计(来源

一个男人拿着一把刀向你跑来。你是做什么的?嗯,大多数人心里只会有一个想法:跑。嗯,你为什么要跑?因为观察这个男人咄咄逼人的姿态后,你可以断定他是想害你。既然你想活着看到明天,你决定尽可能跑得快。

你怎么能在几秒钟内完成所有这些复杂的分析呢?嗯,你的大脑刚刚做了一件叫做的人体姿态估计的事情。幸运的是,由于人类的姿势估计是通过眼睛和大脑的结合来完成的,这是我们可以在计算机视觉中复制的东西。

为了执行人体姿态估计,我们使用一种特殊类型的全卷积网络,称为沙漏网络。该网络的编码器-解码器结构使其看起来像一个沙漏,因此得名“沙漏网络”。

沙漏网络图(来源)。

但是,在我们深入研究网络的本质组件之前,让我们看看这个网络所基于的其他一些深层神经网络。

后退一步

在研究沙漏网络之前,您应该熟悉以下网络架构:

卷积神经网络(CNN)

  • 重要性:自动学习与特定对象最对应的特征,从而提高分类精度。

继续学习的资源:视频课程文章

剩余网络

  • 重要性:通过减缓网络梯度在反向传播中的收敛,允许更深的网络。

进修资源:

全卷积网络(FCN)

  • 重要性:用 1x1 卷积代替密集层,允许网络接受不同维度的输入。

进一步学习的资源:文章视频

编码器-解码器网络

  • 重要性:允许我们通过提取输入的特征并尝试重新创建它来操纵输入(例如。图像分割、文本翻译)

我们将更多地讨论编码器-解码器,因为这基本上就是沙漏网络,但如果你想要一些其他很酷的资源,这里有一些:视频quora 线程文章GitHub

高层的网络

因此,希望您在学习所有这些网络架构时获得了一些乐趣,但是现在是时候将它们结合起来了。

沙漏网络架构(来源)

沙漏网络是一种卷积编码器-解码器网络(意味着它使用卷积层来分解和重建输入)。他们接受一个输入(在我们的例子中,是一个图像),并通过将图像解构为一个特征矩阵来从这个输入中提取特征。

然后,它采用这个特征矩阵并且将其与具有比特征矩阵更高的空间理解能力(比特征矩阵更好地感知对象在图像中的位置)的早期层组合。

  • 注意:特征矩阵具有低空间理解,意味着我 t 并不真正知道物体在图像中的位置。这是因为,为了能够提取对象的特征,我们必须丢弃所有不是对象特征的像素。这意味着丢弃所有的背景像素,通过这样做,它删除了图像中对象位置的所有知识。
  • 通过将特征矩阵与网络中具有更高空间理解能力的早期图层相结合,我们可以更好地了解输入内容(输入内容+输入内容在影像中的位置)。

我在 Canva 制作的快速图表。希望有帮助:)

将网络中的早期层传输到后面的层难道没有引起人们的注意吗?resnet。残差在整个网络中大量使用。它们用于将空间信息与特征信息结合起来,不仅如此,每个绿色块代表我们称之为 瓶颈块 的东西。

瓶颈是一种新型的残差。我们有 1 个 1X1 卷积、1 个 3X3 卷积和 1 个 1X1 卷积,而不是 2 个 3X3 卷积。这使得计算机上的计算变得容易得多(3×3 卷积比 1×1 卷积更难实现),这意味着我们可以节省大量内存。

左:剩余层右:瓶颈块(来源

所以总而言之,

  • 输入:人物图像
  • 编码:通过将输入分解成特征矩阵来提取特征
  • 解码:结合特征信息+空间信息,深入理解图像
  • 输出:取决于应用程序,在我们的例子中,是关节位置的热图。

逐步了解流程

如果我们真的想能够编码,我们需要理解每一层发生了什么,为什么。因此,在这里,我们将分解整个过程,一步一步地进行,以便我们对网络有一个深入的了解(我们只是要回顾沙漏网络的架构,而不是整个培训过程)。

在这个网络中,我们将使用:

  • 卷积层:从图像中提取特征
  • 最大池层:消除图像中对特征提取不必要的部分
  • 剩余层:将层推进网络更深处
  • 瓶颈层:通过包含更多不太密集的卷积来释放内存
  • 上采样层 : 增加输入的大小(在我们的例子中,使用最近邻技术——观看视频了解更多)

好了,在深入研究之前,让我们来看看沙漏网络的另一张图。

沙漏网络图(来源)

这里我们可以看到一些东西:

  • 有两个部分:编码和解码
  • 每个部分有 4 个立方体。
  • 左边的立方体被传递到右边,形成右边的立方体

所以如果我们展开每个立方体,它看起来像这样:

瓶颈层(来源)

所以在网络全网图中,每个立方体都是一个瓶颈层(如上图)。在每个池层之后,我们会添加一个瓶颈层。

然而,第一层有点不同,因为它有一个 7X7 卷积(这是架构中唯一大于 3X3 的卷积)。以下是第一层的外观:

第一层的可视化

这是第一个立方体的样子。首先,输入被传递到与 BatchNormalization 和 ReLu 层结合的 7X7 卷积中。接下来,它被传递到一个瓶颈层,该层复制:一个通过最大池并执行特征提取,另一个仅在稍后的上采样(解码)部分连接回网络。

接下来的立方体(立方体 2、3 和 4)具有彼此相似的结构,但是与立方体 1 的结构不同。下面是其他多维数据集(在编码部分)的样子:

第二、第三和第四层的可视化

这些层要简单得多。先前的输出层被传递到瓶颈层,然后复制到残差层和用于特征提取的层。

我们将重复这个过程 3 次(在立方体 2、3 和 4 中),然后我们将生成特征地图。

以下是创建要素地图所涉及的图层(此部分是您在整个网络图中看到的三个非常小的立方体):

底层的可视化

这是网络中最深的层次。它也是要素信息最高的部分和空间信息最低的部分。在这里,我们的图像被压缩成一个矩阵(实际上是一个张量 T1),它代表了我们图像的特征。

为此,它通过了所有 4 个编码立方体和底部的 3 个瓶颈层。我们现在准备向上采样。以下是上采样层的外观:

放大图层的可视化

因此,在这里,传入的残差层将通过瓶颈层,然后在其自身(残差层)和上采样特征层(来自主网络)之间执行元素加法

我们将重复这个过程 4 次,然后将最后一层(解码部分的第 4 个立方体)传递到最后一部分,在那里我们确定每个预测的准确度。

  • :这叫直接监督。就是你计算每个阶段末端的损耗而不是整个网络末端的损耗。在我们的例子中,我们在每个沙漏网络的末端计算损失,而不是在所有网络组合的末端计算损失(因为对于人体姿势估计,我们使用堆叠在一起的多个沙漏网络)。

以下是最终图层的外观:

最终网络预测的可视化

这是网络的终点。我们通过卷积层传递最终网络的输出,然后复制该层以产生一组热图。最后,我们在网络的输入、热图和网络的两个输出(一个是预测,另一个是到达下一个网络末端的输出)之间执行元素相加。

然后,重复!

对,就是这样。你刚刚走过了整个沙漏网络。在实践中,我们将一起使用许多这样的网络,这就是标题为并重复为的原因。希望这个看似吓人的话题现在可以消化了。在我的下一篇文章中,我们将编写网络代码。

就像我之前提到的,我们将把这个应用到人体姿态估计中。然而,沙漏网络可以用于很多事情,比如语义分割、3d 重建等等。我读了一些关于沙漏网 3D 重建的很酷的论文,我会把它们链接到下面,这样你也可以读读。

总的来说,我希望你喜欢阅读这篇文章,如果你在理解这个概念上有任何困难,请随时通过电子邮件、linkedin 甚至 Instagram (insta:@nushaine)与我联系,我会尽力帮助你理解。除此之外,祝您愉快,编码愉快:)

资源

非常酷的论文

牛逼 GitHub Repos

在数据科学中使用内存访问

原文:https://towardsdatascience.com/using-in-memory-access-in-data-science-96130a5bcc33?source=collection_archive---------38-----------------------

基于磁盘的数据库和内存访问之间的简单性能比较

图片来源:vectorgraphit.com。知识共享署名许可证。

在金融市场数据分析中,要分析的数据量和吞吐量可能令人望而生畏。这绝不是金融界特有的,因为它也发生在许多其他数据分析领域。这个特定行业的独特之处在于数据是高度结构化的(这在其他领域并不常见)。构成金融市场数据的是大量大小不一且大多互不相关的数据消息:最终很容易收到、存储、解码、解析和关联数亿条小消息。

分析如此大量的数据需要许多次迭代,并且在随机数据访问中节省时间的需要变得相关。

数据库与内存

在数据科学和数据工程中,我们面临许多次如前所述的处理数据集的需要。这可能是因为我们想要以大吞吐量注入数据——模拟环境——或者因为我们只是需要迭代大型数据集以进行统计分析或蒙特卡罗模拟——并非数据科学中的一切都可以简化为 ML 模型。

当存在这样的需求时,第一反应是使用数据库(可以是任何数据库)中的数据,并使用查询/访问策略来提取和使用数据。有时,这种方法根本无法避免,因为数据集太大,即使拆分也无法放入内存,或者因为使用数据的预期方式复杂多样。

但是在许多其他情况下,数据可以成功地简单地存储在 RAM 中,以便以后立即分配,或者在大块数据中,使用原始 I/O 直接从磁盘传输到内存。

后一种方法尽管简单,但通常不被采用(通过将数据访问减少到仅仅从磁盘进行内存复制,然后通过数组进行内存请求,从而减少了一层复杂性)。它肯定不适合所有的场景,但是它非常适合模拟、分析和回溯测试,正如在定量研究和分析中发现的那样。数据通常可以按年份或任何其他标准进行划分。

拆分可能被视为一个问题,但根据我的经验,它实际上简化了数据处理管道的后续阶段,即使使用数据库,也经常需要拆分。

在内存中存储要分配的数据的方式和将数据存储为可以查询的存储库的方式存在差异。如果您计划将所有内容都移入 RAM,那么理想情况下,您会希望存储一个将在编程语言中使用的结构的字节内存副本,而如果您计划使用数据库或存储库,那么它将是底层解决方案,即规定如何准备和存储数据的解决方案。

虽然您会发现许多人认为第一种方法违反了一些最佳实践(数据的字节顺序会引起可移植性问题,编译器不保证语言结构的一致性,等等),但事实是它在受控环境中工作得非常好,在受控环境中,您可以定义平台、操作系统、编译器和分析的源代码。

这些反对意见/建议可能对打算发布的商业软件有效,或者可能是从理论角度进行讨论的基础,但是在现实生活的研究和分析任务中,它们的有效性可能会受到质疑,在这种情况下,平台和环境都是及时受控和稳定的。

此外,我们已经到达了一个点,在平台和编译器架构方面没有太多的选择,所以关于字节序和编译器差异的讨论更多的是学术而不是实践。对我来说,这些是 80 年代和 90 年代的回忆,那时我们在平台方面更加多样化(SPARC、PowerPC、英特尔、Alpha)。这同样适用于许多不同 UNIX 供应商的操作系统。这种多样性很久以前就消失了。

RAM 比基于磁盘的数据库快多少?

为了回答这个问题,我们将测试两个场景。一个将模拟使用内存映射文件访问基于磁盘的存储系统(这被认为是大文件的快速访问方法),另一个将模拟针对内存阵列的直接内存访问。

在这两种情况下,文件和内存数组都填充了随机数据,我们在整个数据集(512Mb)上迭代 10 次,进行简单的计算(以避免编译器在其优化功能中跳过指令),我们将测量每种方法花费的时间。

用于测试基于磁盘的存储访问与内存访问的源代码

结果

在低端 FreeBSD 机器上运行的模拟结果清楚地显示了 RAM 如何显著提高访问性能:

durationTestFile
20318
durationTestArray
7312

即使我们用操作(每个检索字节的累积和与位屏蔽操作)来偏置测试,当比较内存和磁盘时,我们仍然得到 3:1 的速度性能比。可能内存与磁盘的隔离效应比这个比率还要高得多。

任何数据库或存储解决方案的性能都不可能超过内存映射文件的性能,因此我们可以得出结论,在内存上迭代可以大大减少计算时间。这不会让任何人感到惊讶,但是你仍然会发现有人声称两者是平等的,或者干脆否认直接内存访问方法是可行的,这是一个错误的假设。

作为对这个非正式基准的合理反对,有人可能会说我们没有考虑初始化数组的时间和将数据放入数组的时间。这些都是合理的反对意见,但是在对数据进行大量后续迭代(可能会持续几十分钟、几小时甚至几天)的情况下,使用内存而不是磁盘仍然有很大的好处,因为它将大大减少运行分析的时间

处理时间越长,使用的内存就越多。

这种方法没有被广泛使用,因为它通常意味着能够压缩数据并处理底层语言的实际内存表示,而且它远远超出了在 JSONJupyter 笔记本中处理数据的舒适区。它可能更适合使用 C/C++ Java 或 Go 的低延迟开发环境。

还值得一提的是,整个方法只对高度结构化的数据有效,金融市场也是如此。处理非结构化数据或明确呈现关系依赖的数据需要不同的方法。

免责声明:此处表达的所有观点和信息均为我个人观点,并不代表我曾经、现在或将隶属或关联的任何实体的观点。

使用 joblib 加速 Python 管道

原文:https://towardsdatascience.com/using-joblib-to-speed-up-your-python-pipelines-dd97440c653d?source=collection_archive---------10-----------------------

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

让您的 Python 工作得更快!

动机:

随着多个预处理步骤和计算密集型流水线的增加,在某些点上有必要使流程高效。这可以通过删除一些冗余步骤或获得更多内核/CPU/GPU 来提高速度来实现。很多时候,我们专注于获得最终结果,而不考虑效率。我们很少努力优化管道或进行改进,直到耗尽内存或计算机挂起。最终,我们会觉得…

幸运的是,已经有了一个被称为 joblib 的框架,它提供了一组工具,使得 Python 中的管道在很大程度上变得轻量级。

为什么是 joblib?

joblib 工具集成为 ML 管道的一部分有几个原因。在他们的网站上提到了使用它的两个主要原因。然而,我想重新表述一下:

  • 能够使用缓存,避免重新计算某些步骤
  • 执行并行化,充分利用 CPU/GPU 的所有内核。

除此之外,我推荐 joblib 还有其他几个原因:

  1. 可以轻松集成
  2. 没有特定的依赖关系
  3. 节省成本和时间
  4. 简单易学

还有其他一些功能也很丰富,如果包含在日常工作中会很有帮助。

1.使用缓存的结果

在测试或创建模型时,我们经常需要多次重新运行我们的管道。有些函数可能会被调用多次,使用相同的输入数据,然后再次进行计算。Joblib 提供了一种更好的方法来避免重复计算相同的函数,从而节省了大量的时间和计算成本。比如下面我们举个简单的例子:

如上所述,该函数只是计算一个给定范围内的数字的平方。大约需要 20 秒才能得到结果。现在,让我们使用 joblib 的内存函数,定义一个位置来存储缓存,如下所示:

在第一次计算时,结果与之前的大约 20 秒非常相似,因为结果是第一次计算,然后存储到一个位置。让我们再试一次:

瞧啊。提供结果花了 0.01 秒。时间缩短了近 2000 倍。这主要是因为结果已经被计算并存储在计算机的缓存中。所有功能的效率并不相同!它可能因所请求的计算类型而有很大不同。但是你肯定会有这种通过缓存来加速管道的超能力!

要清除缓存结果,可以使用一个直接命令:

但是在使用这段代码之前要小心。你可能会毁掉你几周的计算工作。

2.并行化

顾名思义,我们可以使用“ joblib 并行计算任何具有多个参数的指定函数。平行”。在后台,当使用多个作业(如果指定)时,每个计算不会等待前一个完成,可以使用不同的处理器来完成任务。为了更好地理解,我展示了如何在缓存中运行并行作业。

考虑以下生成的随机数据集:

下面是我们正常顺序处理的运行,其中新的计算仅在先前的计算完成后开始。

对于并行处理,我们设置作业数= 2。作业的数量受限于 CPU 拥有或可用(空闲)的核心数量。

这里我们可以看到使用并行方法处理的时间减少了 2x

注意 :如果用于计算量较小的函数,使用此方法可能会降低性能。

3.倾卸和装载

我们经常需要存储和加载数据集、模型、计算结果等。在电脑上的某个位置来回移动。Joblib 提供了可用于轻松转储和加载的函数:

将数据转储到某个位置的代码

从某个位置加载数据的代码

4.压缩方法

当处理较大的数据集时,这些文件占用的空间非常大。使用特征工程,当我们添加更多的列时,文件会变得更大。幸运的是,如今,随着存储变得如此便宜,这不再是一个问题。然而,为了提高效率, joblib 提供的一些压缩方法使用起来非常简单:

a .简单压缩:

非常简单的就是上面显示的那个。它不提供任何压缩,但却是存储任何文件的最快方法

b .使用 Zlib 压缩:

这是一个很好的 3 级压缩方法,实现如下:

c .使用 lz4 压缩:

这是另一个很棒的压缩方法,也是已知最快的压缩方法之一,但压缩率略低于 Zlib。我个人认为这是最好的方法,因为它是压缩大小和压缩率之间的一个很好的平衡。下面是实现它的方法:

将所有内容放在一个表格中,如下所示:

作者创造的形象

结束语:

我发现 joblib 是一个非常有用的库。我已经开始将它们集成到我的许多机器学习管道中,并且肯定看到了许多改进。

感谢您抽出时间阅读这篇文章。任何意见/反馈总是很感谢!

使用 Jupyter 笔记本管理您的 BigQuery 分析

原文:https://towardsdatascience.com/using-jupyter-notebook-to-manage-your-bigquery-analytics-c4dc7b2a4113?source=collection_archive---------12-----------------------

如何利用 Jupyter Notebook 更好地管理您的 SQL 查询?

图片来源:我们都去过那里…

如果你有打开和不断在一万个 BigQuery 标签页之间切换的困扰,你并不孤单。作为一名数据专业人员,使用 SQL 查询是一项日常任务,如果你没有条理,你会很快淹没在 Chrome 标签中。

幸运的是,您可以使用带有几个软件包的 Jupyter 笔记本,在处理 SQL 查询时会有更加愉快的体验。在这篇文章中,我将详细介绍设置环境的步骤,并将您的工作流程迁移到 Jupyter 笔记本上。无论您是数据分析师、数据工程师还是数据科学家,拥有一个有组织的工作流程将有助于显著提高您的工作效率。

环境设置

下载并安装 Anaconda

安装 Python 和 Jupyter Notebook 的最快最简单的方法是使用 Anaconda 发行版。你可以按照这里的指示找到在你的操作系统上安装 Anaconda 的指南。

确保将 Anaconda 添加到 PATH 环境变量中。

安装谷歌云 Python SDK 并认证

按照谷歌的指南为你的特定操作系统安装云 SDK。安装并初始化 SDK 后,您应该通过执行以下操作来设置应用程序默认身份验证:

打开终端/Anaconda 提示符/命令行,键入以下内容

gcloud auth application-default login

会弹出一个浏览器,要求你用谷歌账户登录。登录并选择Allow对云 SDK 进行认证。

安装 GCP python 库和 pandas_gbq

我们鼓励您为每个项目建立一个单独的环境来安装 python 包。为了简单起见,我将跳过在这里创建不同环境的步骤。

安装以下 Python 包:

pip install --user --upgrade google-api-python-client
pip install --user pandas-gbq -U

Jupyter 笔记本中的 SQL 查询

打开 Jupyter 笔记本或 Jupyter 实验室

您可以使用以下任一方法从命令行快速启动 Jupyter 笔记本或 Jupyter 实验室实例:

jupyter notebook
jupyter lab

创建一个 Python 3 笔记本,并确保您选择了之前安装软件包的环境。

导入库

import pandas as pd
import pandas_gbqfrom google.cloud import bigquery
%load_ext google.cloud.bigquery# Set your default project here
pandas_gbq.context.project = 'bigquery-public-data'
pandas_gbq.context.dialect = 'standard'

导入所需的库,就大功告成了!不再有无尽的 Chrome 标签,现在你可以在笔记本上组织你的查询,比默认编辑器有更多的优势。让我们复习一些例子。

在单元格查询中

借助%%bigquery的魔力,您可以编写如下多行单元格内查询:

您也可以使用%%bigquery df_name将结果存储到熊猫数据框中

可以使用— params标志在查询中传递参数:

但是,为了提高性能,一定要限制返回行。IPython Magics for BigQuery 的详细文档可以在这里找到。

使用 pandas_gbq 库

除了使用 IPython Magics,您还可以使用 pandas_gbq 与 BigQuery 进行交互。

将数据写回 BigQuery 也很简单。您可以运行以下代码:

df.to_gbq(df, table_id, project_id = project_id)

pandas_gbq的文件可以在这里找到。

使用 Python BigQuery 客户端

client = bigquery.Client(project = project_id) 
query = '''
    SELECT name, SUM(number) as count
    FROM `bigquery-public-data.usa_names.usa_1910_current`
    GROUP BY name
    ORDER BY count DESC
    LIMIT 5
'''
client.query(query).result().to_dataframe()

这段代码产生的结果与上述方法相同。但是,您可以使用 Python 客户端与 BigQuery 进行更高级的交互。诸如创建表、定义模式、定义自定义函数等。BigQuery Python 客户端的完整文档可以在这里找到。

结论

在这篇文章中,我介绍了使用 Jupyter Notebook 与 BigQuery 进行更程序化交互的步骤。以下是使用 Jupyter Notebook 查询数据时可以做的一些高级事情:

  • 用 Jupyter 笔记本上的 markdown 单元格记录您的代码
  • 以 HTML 或 PDF 格式分享您的分析
  • 参数化您的查询
  • 为您的查询编写单元测试
  • 使用 BigQuery Python 客户端将基础设施实现为代码
  • 构建一个 ETL 管道。

快乐学习!

使用一行代码写入关系数据库

原文:https://towardsdatascience.com/using-just-one-line-of-code-to-write-to-a-relational-database-3ed08a643c5f?source=collection_archive---------23-----------------------

PYTHON 和 SQL

这将使添加到数据库变得更加容易。

由 Instagram @softie__art 制作的艺术作品

当将数据从 Pandas 数据帧写入 SQL 数据库时,我们将使用DataFrame.to_sql方法。虽然您可以执行一个INSERT INTO类型的 SQL 查询,但是原生的 Pandas 方法使得这个过程更加容易。

下面是从熊猫文档中摘录的DataFrame.to_sql的完整参数列表:

**DataFrame.to_sql** ( selfname:strconschema=Noneif _ exists:str= ' fail 'index:bool

为了向您展示这种方法是如何工作的,我们将通过几个例子向费用记录的数据库添加交易。在本文中,我们将使用 SQLite 数据库。DataFrame.to_sql与 SQLite 和 SQLAlchemy 支持的其他数据库一起工作。

请随意创建您自己的数据库,以便您可以跟进。如果您对创建数据库不熟悉,下面快速介绍一下如何免费设置一个简单的 SQLite 数据库:

* [## 在自己的计算机上开始使用数据库的简单方法

介绍如何使用 SQLite 浏览器来管理自己的数据库。

towardsdatascience.com](/an-easy-way-to-get-started-with-databases-on-your-own-computer-46f01709561)

在开始之前,不要忘记导入 Pandas 和 SQLite:

import sqlite3
import pandas as pd

用熊猫写数据库

在本练习中,我将在数据库的“Expense”表中插入新行:

示例 SQLite 数据库表

我用以下代码生成了一个示例事务数据框架:

data = {'Person': ['Byron'], 'Amount': [20], 'Category': ['Groceries'], 'Date':['27-08-20']}
df = pd.DataFrame.from_dict(data)

我们将插入到费用表中的数据帧如下所示:

准备输入新记录时,请确保数据框架中的列名与数据库表中的列名相匹配。

此外,所有操作都需要一个连接参数,您可以将它作为参数提供给con。要为 SQLite 数据库这样做,只需准备好这个变量供重用,其中db是本地 SQLite 数据库的文件路径。

conn = sqlite3.connect(db)

现在我们准备好开始了!

基本插入(和警告)

对于第一个例子,正如我们承诺的,我们使用下面一行代码:

df.to_sql(‘Expenses’, con=conn, if_exists=’append’, index=False)

这里,我们将"Expenses"作为我们想要写入的 SQLite 数据库中的表名。如前所述,我们还包含了con=conn,这样我们就可以连接到相关的 SQL 数据库。

默认情况下,如果一个表不存在,并且您尝试了上面的代码,一个新的表将在您的数据库中以您指定的名称创建。如果您想用现有的数据框架创建一个表,这是一种可行的方法。

对于我们的例子,表已经存在,所以我们需要指定应该发生什么。因为我们希望向表中添加新行并保留表中已有的行,所以我们传递if_exists='append',以便将数据帧中的新值插入到数据库表中。确保包括这一点,因为默认行为是if_exists='fail',这意味着代码将而不是执行。

我们还传递了index=False,因为index=True的默认行为是将数据帧的索引作为一列写入数据库表。但是,我们不需要这些信息,因为当我们添加新行时,SQLite 数据库中的“ID”列会自动增加。

自己摆弄这些参数,然后用 DB Browser for SQLite 检查更改,或者打印如下表格:

conn.execute("SELECT * FROM Expenses").fetchall()

用于检查结果的 SQL 查询的部分输出

写入大型数据帧—块大小

在前面的例子中,我们所做的只是将一个包含一行的数据帧插入到数据库表中。然而,对于更大的操作,如果您想写一个有几千行的数据帧,您可能会遇到一些问题。

堆栈溢出发出样本

这可能是因为不同数据库的数据包大小限制。如果您的数据帧超过最大数据包大小(即一次发送的数据太多),将会发生错误,因为默认情况下,所有行都是一次写入的。

您可以通过指定一个chunksize来避免这种错误,T8 是您想要一次插入到数据库表中的数据帧中的行数。

例如,如果您有一个包含 2,000 行的表,并且您使用的数据库的最大数据包大小一次只允许 1,000 行,那么您可以将 chunksize 设置为 1,000 来满足该要求。

df.to_sql(‘Expenses’, con=conn, index=False, chunksize=1000)

我希望这个快速概述对您有用!Pandas 库提供了丰富的功能,可以进行各种各样的数据相关操作,您可以充分利用这些功能。如果您总是使用 SQL 查询或其他 Python 方法向数据库写入数据,那么单行的DataFrame.to_sql方法应该会为您提供一种快速而简单的替代方法。

如果您正在寻找一种简单的方法来探索和理解您的数据,Pandas 还提供了不同的排序功能来实现这一点:

* [## 4 种不同的方法来有效地排序熊猫数据帧

正确探索、理解和组织您的数据。

towardsdatascience.com](/4-different-ways-to-efficiently-sort-a-pandas-dataframe-9aba423f12db)

要了解 Pandas MultiIndex 功能,它可用于各种高级数据分析任务,请查看以下内容:

[## 如何在 Pandas 中使用 MultiIndex 来提升您的分析

复杂数据分析中数据帧的层次索引介绍

towardsdatascience.com](/how-to-use-multiindex-in-pandas-to-level-up-your-analysis-aeac7f451fce)*

利用 K-均值聚类进行图像分离

原文:https://towardsdatascience.com/using-k-means-clustering-for-image-segregation-fd80bea8412d?source=collection_archive---------25-----------------------

使用 CNN 和 KMeans 来分离图像。

KMeans 聚类是最常用的无监督机器学习算法之一。顾名思义,它可以用来创建数据集群,本质上是隔离它们。

让我们开始吧。这里我将举一个简单的例子,从一个既有猫和狗的图像的文件夹中分离图像到它们自己的簇中。这将创建两个独立的文件夹(集群)。我们还将介绍如何自动确定 k 的最佳值。

我已经生成了一个猫和狗的图像数据集。

猫和狗的图像。

首先,我们将从导入所需的库开始。

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import cv2
import os, glob, shutil

然后,我们将从图像文件夹中读取所有图像,并对它们进行处理以提取特征。我们将图像的大小调整为 224x224,以匹配我们的模型的输入层的大小,用于特征提取。

input_dir = 'pets'
glob_dir = input_dir + '/*.jpg'images = [cv2.resize(cv2.imread(file), (224, 224)) for file in glob.glob(glob_dir)]paths = [file for file in glob.glob(glob_dir)]
images = np.array(np.float32(images).reshape(len(images), -1)/255)

现在我们将在 MobileNetV2(迁移学习)的帮助下进行特征提取。为什么选择 MobileNetV2?你可能会问。我们可以用 ResNet50,InceptionV3 等。但是 MobileNetV2 速度快,资源也不多,所以这是我的选择。

model = tf.keras.applications.MobileNetV2(*include_top*=False,
*weights*=’imagenet’, *input_shape*=(224, 224, 3))predictions = model.predict(images.reshape(-1, 224, 224, 3))
pred_images = predictions.reshape(images.shape[0], -1)

既然我们已经提取了特征,现在我们可以使用 KMeans 进行聚类。因为我们已经知道我们正在分离猫和狗的图像

k = 2
kmodel = KMeans(*n_clusters* = k, *n_jobs*=-1, *random_state*=728)
kmodel.fit(pred_images)
kpredictions = kmodel.predict(pred_images)
shutil.rmtree(‘output’)for i in range(k):
    os.makedirs(“output\cluster” + str(i))for i in range(len(paths)):
    shutil.copy2(paths[i], “output\cluster”+str(kpredictions[i]))

这是输出,成功了!它把图像分离出来:

狗:

猫:

还有一件事,我们如何确定一个数据集的 K 值,对于这个数据集,你不知道类的数量。我们可以用侧影法或肘法来确定。这里我们将使用剪影法。这两种方法都应该用来获得最有把握的结果。我们将直接确定 k。

更多关于确定 K 的值:https://medium . com/analytics-vid hya/how-to-determine-the-optimal-K-for-K-means-708505d 204 EB

让我们将马的图像添加到原始数据集中。我们现在将确定 k 的值。

sil = []
kl = []
kmax = 10for k in range(2, kmax+1):
    kmeans2 = KMeans(*n_clusters* = k).fit(pred_images)
    labels = kmeans2.labels_
    sil.append(silhouette_score(pred_images, labels, *metric* =   ‘euclidean’))
    kl.append(k)

我们现在将绘制这些值:

plt.plot(kl, sil)
plt.ylabel(‘Silhoutte Score’)
plt.ylabel(‘K’)
plt.show()

如您所见,K 的最佳值是 3。我们还成功创建了第三个集群:

结论

如您所见,KMeans 聚类是一种很好的图像分离算法。有时,我们使用的方法可能不会给出准确的结果,我们可以尝试通过使用不同的卷积神经网络来修复它,或者尝试将我们的图像从 BGR 转换为 RGB。

GitHub 资源库:https://GitHub . com/hris 1999/Image-Segregation-with-k means/

感谢阅读!希望你有美好的一天:)

使用 K-均值聚类创建支持和阻力:

原文:https://towardsdatascience.com/using-k-means-clustering-to-create-support-and-resistance-b13fdeeba12?source=collection_archive---------21-----------------------

迈克尔·泽兹奇在 Unsplash 上的照片

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

说到技术分析,支撑和阻力是人们谈论最多的概念。支撑位和阻力位被用作价格壁垒,价格在其中“反弹”。在本文中,我将使用 K-means 聚类算法来找到这些不同的支持和阻力通道,并根据这些见解进行交易。

支撑和阻力:

为了理解如何最好地实现某事,我们应该首先理解我们想要实现的事情。

自画支撑位和阻力位。作者图片

支撑位和阻力位,是画在图上的两条线,形成一个通道,价格就在这个通道里。

支持和阻力是由于来自卖方或买方的压力,证券不能再减少或增加的结果。一个很好的经验法则是,价格偏离支撑线或阻力线的次数越多,它再次起作用的可能性就越小。

支撑线和阻力线能很好地洞察进场点和卖点,因为支撑线和阻力线理论上是这段有限时间内的最低点和最高点。

支持和抵制策略的缺点是,它在一段未知的时间内有效,而且线条是主观的,因此容易出现人为错误。

计划概念:

K-means 聚类算法查找时间序列数据的不同部分,并将它们分组到定义数量的组中。这个数字(K)可以被优化。然后,每组的最高值和最低值被定义为该组的支持值和阻抗值。

现在我们知道了程序的意图,让我们试着用 Python 重新创建它!

代码:

import yfinance
df = yfinance.download('AAPL','2013-1-1','2020-1-1')
X = np.array(df['Close'])

这个脚本用来访问苹果股票价格的数据。对于这个例子,我们只在收盘价上实现支撑和阻力。

from sklearn.cluster import KMeans
import numpy as np
from kneed import DataGenerator, KneeLocator

sum_of_squared_distances = []
K = range(1,15)
for k in K:
    km = KMeans(n_clusters=k)
    km = km.fit(X.reshape(-1,1))
    sum_of_squared_distances.append(km.inertia_)
kn = KneeLocator(K, sum_of_squared_distances,S=1.0, curve="convex", direction="decreasing")
kn.plot_knee()
# plt.plot(sum_of_squared_distances)

这个脚本测试不同的 K 值以找到最佳值:

2 的 K 值产生了很长一段时间都不会达到的支撑线和阻力线。

K 值为 9 会产生太常见的支撑和阻力,因此很难做出预测。

因此,我们必须找到 K 的最佳值,在比较 K 值之间的方差时通过肘点来计算。肘点是最大的进步,给定一定的动作。

基于 kneed 库,肘点在 4。这意味着最佳 K 值是 4。

kmeans = KMeans(n_clusters= kn.knee).fit(X.reshape(-1,1))
c = kmeans.predict(X.reshape(-1,1))
minmax = []
for i in range(kn.knee):
    minmax.append([-np.inf,np.inf])
for i in range(len(X)):
    cluster = c[i]
    if X[i] > minmax[cluster][0]:
        minmax[cluster][0] = X[i]
    if X[i] < minmax[cluster][1]:
        minmax[cluster][1] = X[i]

该脚本查找每个聚类中的点的最小值和最大值。当绘制时,这些线成为支撑线和阻力线。

from matplotlib import pyplot as plt
for i in range(len(X)):
    colors = ['b','g','r','c','m','y','k','w']
    c = kmeans.predict(X[i].reshape(-1,1))[0]
    color = colors[c]
    plt.scatter(i,X[i],c = color,s = 1)for i in range(len(minmax)):
    plt.hlines(minmax[i][0],xmin = 0,xmax = len(X),colors = 'g')
    plt.hlines(minmax[i][1],xmin = 0,xmax = len(X),colors = 'r')

这个脚本绘制了支撑位和阻力位,以及实际的价格图,这些价格图基于分类进行了颜色编码。不幸的是,我认为颜色是有限的,这意味着可以对数据进行颜色编码的 K 值有限。

这是程序的结果,一组支撑线和阻力线。请记住,当值回落到通道中时,线条是最准确的。此外,最终阻力线将是最不准确的,因为它考虑了最后一个值,而没有考虑任何其他值。

我的链接:

如果你想看更多我的内容,点击这个 链接

使用 K-Means 检测零售店中的变化

原文:https://towardsdatascience.com/using-k-means-to-detect-changes-in-a-retail-store-96af1476dd9f?source=collection_archive---------31-----------------------

迈克·彼得鲁奇在 Unsplash 上的照片

识别消费者行为变化的无监督技术

U 当识别特定时期之间的变化时,受监督的机器学习算法会非常有用。

我将为批发商看一家零售商店。作为一家商店,你可能会期望或可能会瞄准你的客户群中的一些规律性。考虑到这一点,我将解释如何识别在一定时期内购买行为发生变化的客户。这种类型的分析可用于不同的领域。例如,它可以用来瞄准那些购买量较少的客户。在这种情况下,商店可以通过向他们提供折扣来留住他们。

我就不为了岗位而去走一遍整个数据科学的流程了。为了简明扼要,我将跳过这些步骤。如果你想了解更多关于解决业务问题的数据科学过程,看看这篇关于 CRISP-DM 方法论的帖子。

数据

它是一个管状数据集,包含英国零售商店中不同客户的交易。这里是公开的

它包含材料采购的发票。来自 38 个国家 4372 个不同客户的 541909 张不同发票。

交易时间从 2011 年 12 月到 2012 年 12 月不等。

问题

乍一看,该数据集没有给出任何有关客户购买行为发生变化的提示。缺乏标签使得不可能应用监督技术,因为没有任何东西明确他们是哪种类型的顾客。这在一个层面上增加了问题的难度,其中有许多细微差别和未决问题。

在所有的机器学习问题中,对问题有很好的理解对于给出量身定制的解决方案至关重要。根据业务问题的不同,会有许多不同的方法。

考虑

我们将根据客户的行为对他们进行分组。为此,我们将使用两个变量,即每月采购频率和货币价值。

在 13 个月的购买活动中,我会比较两个时期的活动。第一个创建行为的基线,第二个评估它。对于前者,我们将使用 8 个月,对于后者,我们将使用 5 个月。它将分别占 60%和 40%的时间。

谈到技术细节,K-Means 是在数据中寻找相似性的好算法。将数据分成 5 组将足够大,但不会大到对微小的变化敏感。

由于商店的目标是增加购买频率或消费金额,因此可以用以下名称对这些群体进行分类:

  • 休眠——很少访问,很少花钱。他们可能需要大的刺激来重新激活它们。
  • 潜在客户——与最后一组相似,区别在于使他们成为更好的客户的刺激较小。
  • 中—普通用户。
  • 不定期——他们很少去,但去的时候往往会花很多钱。
  • 忠诚——他们经常光顾商店,但不会花很多钱。

根据公司的战略,名称和组号可能会有所不同。数据科学和利益相关者之间的良好沟通和理解对于将业务需求正确转化为模型非常重要。

K-Means 快速介绍

这是最著名和最常用的无监督算法。它允许对数据进行分组或分类,这是您自己从未想到过的。此外,K-Means 属于硬聚类算法组,其原因是因为一旦分类完成,数据点就属于一个且仅属于一个组。另一方面,软聚类算法同时将数据分配给不同的组。

K-Means 的工作方式是,给定聚类的数量,它将根据到数据点的距离自动调整所有组的中心,直到算法收敛。一面之词,有不同类型的距离!K-Means 使用欧几里得距离(它是两个数据点之间的几何距离)。

使用 K-Means 的主要问题是算法不能自动计算最佳的聚类数。所以集群的数量取决于要解决的业务问题和屏幕后面的用户。然而,一些技术可以帮助用户估计最适合的聚类数。这被称为肘方法,但它不包括在这篇文章中。

K-Means 如何有助于检测变化?

在执行一些数据预处理操作后,这就是数据的样子。

tx.describe()

值得注意的是,两个变量(频率和货币值)都有一些异常值:

  • 在价格上,中位数是 136 英镑,平均数是 390 英镑(与 1887 年的标准偏差相当大)。接下来,我将不包括花费超过 20,000 英镑的客户。
  • 关于每月的总发票,中位数是每月 1 次访问,平均数是 0.8 次访问(标准偏差为 1.2)。在此之后,每月访问次数超过 15 次的客户将不会包括在分析中。

请注意,根据分析的性质,选择要移除的异常值可能会有所不同。

分割数据集

现在,是时候在基线期(8 个月)和评估期(5 个月)之间划分数据集了。这是两个时期的样子:

除了非常相似之外,我们从他们身上没有什么可以得到的。让我们开始分组吧。

k-表示动手!

如前所述,K-Means 使用欧几里得距离工作,这使得它在变量的尺度不同时很敏感。这是两个变量的情况。我将应用 0 到 1 之间的范围。

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range = (0, 1))baseline_norm = pd.DataFrame(scaler.**fit_transform(baseline)**, 
 index = baseline.index, columns = [‘PriceInvoices’,’TotalInvoices’])evaluation = pd.DataFrame(**scaler.transform(evaluation)**, 
                          index = evaluation.index, columns = ['PriceInvoices','TotalInvoices'])

现在我们准备应用 K-Means 并看到一些结果:

from sklearn.cluster import KMeans**kmeans = KMeans(n_clusters = 5, init = ‘k-means++’, random_state = 1)**
y_kmeans_train = kmeans.**fit_predict(baseline_norm)**

关于 K-均值函数参数的几个解释?

  • 如前所述,簇的数量被设置为 5。
  • 我们使用的初始化方法是 k-means++,它灵活地选择聚类中心,从而加快收敛速度。
  • 随机状态用于使算法具有确定性,换句话说,总是返回相同的结果。

下面是应用 K-Means 后两个周期的样子。注意,首先,我用基线数据集运行算法,以创建组。以便以后查看评估数据集中每个数据点的最接近组。

y_kmeans_test = kmeans.**predict**(evaluation.values)

在计算了从一个群集移动到另一个群集的客户端数量后,有 785 个不同的客户端。这是评估数据集中 40%的客户端。但是,检查集群变化的差异,我们可以看到许多客户端的变化非常小。这是因为许多客户端从一个集群的边缘移动到另一个集群的边缘。我们可以在下一个直方图中看到距离的分布。

为了减少标记的客户端数量,我们可以设置一个阈值,并挑选所有高于该级别的客户端。在我们的例子中,我选择了 0.25,所以它们在图表中是可见的。

最后,这些变化看起来是这样的。黄线是客户端的移动,而 X 标记是第一个位置,dop 是它们结束的位置。

结论:

我们已经看到了如何使用机器学习技术来为零售店中的购买行为建模,我们使用 K-Means 实现了这一点。获得的结果可以用于不同的目的,我在这里解释的一个将适合营销目的。它有助于识别转向活动较少的群体的客户(例如,忠诚于中等)。然而,它可以外推到另一种分析。

缺点和改进 该分析有一些缺点:

  • 分析完成后,客户端可能已经丢失,并且很难重新激活它们。解决这个问题的一个方法是为他们最后一次交易的时间添加一个新的变量,即最近时间。例如,RFM 分析将解决这个问题,将交易的新近性作为一个新的变量添加到分类中。
  • 大量的客户都放弃了。软聚类技术,如高斯混合模型(GMMs)将作为 K-均值的替代方法来缓解这一问题。GMMs 将给出数据点属于每个聚类的概率。例如,一个聚类边缘的数据点将有接近两个聚类的 50%-50%的概率。

为了提高模型捕捉变化的性能,K-Means 可以处理尽可能多的变量,并且 K-Means 将按照相同的标准进行分组。然而,增加更多的变量会使可视化更加困难。

继续学习

正如我上面刚刚写的几行,你可以探索 RFM 分析,这是一个在业界广泛使用的伟大分析。

您可能想尝试其他非监督技术,如层次聚类或高斯混合模型。它们解决了 K-Means 的主要问题,但是请记住,即使它们解决了 K-Means 的一些问题,它们也有自己的缺点!正如所说,“天下没有免费的午餐”!

使用 k 近邻预测 Spotify 曲目的类型

原文:https://towardsdatascience.com/using-k-nearest-neighbours-to-predict-the-genre-of-spotify-tracks-796bbbad619f?source=collection_archive---------29-----------------------

来源:约翰·泰克里迪斯

Spotify 是世界上最受欢迎的音频流媒体平台,提供超过 5000 万首歌曲和超过 1.7 亿用户的访问[1]。我们可以找到更多关于数据的方法之一是通过检查歌曲的音频特征。在这篇文章中,我将向你展示如何利用 Spotify 的音频功能、python 和 K-nearest neighbors(一种受监督的机器学习算法)来预测我最喜欢的歌曲的风格。我们开始吧!

检索数据

为了分析这些数据,我在 Spotify 中创建了两个大型播放列表,一个是爵士乐,一个是嘻哈/RnB 音乐,使用的是 Spotify 上每个流派下嵌套的播放列表。每个播放列表都有超过 1800 首歌曲,因此应该提供一个相当好的样本量来应用我们的机器学习算法。以下是爵士乐播放列表的示例:

Spotify 上的自定义聚合爵士播放列表。来源:内森·托马斯。

为了检索数据,我在 Spotify 上设置了一个开发者账户,以获取所需的相关凭证 (client_id,client_secret)。为了找到如何做到这一点,Spotify 为他们的 web API 提供了很好的文档[2];我建议你去看看它的入门指南。因此,我设置了一个 API 请求来检索四个因素:歌曲名称、歌曲 ID、专辑和艺术家。

API 设置。

之后,我检索了每首歌的音频特征。Spotify 有许多音频功能,用于描述每首曲目:可跳舞性、能量、模式、音调、响度、语速、声音、乐器性、活跃度、效价、速度持续时间 [3]。其中大多数都是不言自明的,除了模式,前者指示音轨是大调还是小调,后者指示音轨值越高听起来越积极。以下是我收集的专栏摘录;这段代码检索每首歌曲的专辑名称:

输出:

hip-hop 属性数据帧的提取。

我总共收集了约 3000 首歌曲进行分析,平均分为两种风格。

数据可视化

将每个属性的值调整到 0 到 1 之间后,我们可以使用雷达图来查看每个流派的属性分布。

这些图表是交互式的,所以你可以自己随意摆弄图表,看看你能从数据中找到什么关系!

爵士乐:

爵士属性雷达图。来源:内森·托马斯。

嘻哈:

嘻哈属性雷达图。来源:内森·托马斯。

检查这两个流派的特征的雷达图,似乎爵士音乐通常比嘻哈音乐具有更低的能量,而爵士音乐比嘻哈音乐更像 T2 音乐。嘻哈歌曲也比爵士歌曲更快乐、更乐观。从定性的角度来看,这是有意义的,因为布鲁斯和流畅的爵士乐可以非常柔和,乐器在音轨中占主导地位。

通过取其主要成分、或 PCA,这是另一种可视化每种体裁分布的方式。PCA 降低了数据的维度,旨在提取信号并从数据中去除噪声。在这里找到更多关于算法的实际应用,在这里我把它应用到收益率曲线【4】。为了绘制 Spotify 的统计数据,我使用了前两个主要成分,,它们一起解释了超过 99%的数据:

每个流派的第一和第二主成分散点图。来源:内森·托马斯。

这张图再次证实了我们的雷达图分析——在散点图上,嘻哈音乐似乎更加集中和聚集在一起,而爵士歌曲似乎分布更广。

使用 kNN 预测流派

但是首先:kNN 实际上是做什么的?

简而言之,K-nearest neighbors 是一种监督的机器学习技术【5】,它获取一个数据点,并计算 K 个标记的数据点之间的距离,根据它从 K-nearest 数据点获得的票数对该点进行分类。“受监督的”部分意味着我们在测试之前就已经知道这首歌是什么类型的,因此有了标签。

例如,如果 k=3 并且三个最近的邻居被分类为 1。爵士乐,2。爵士乐和 3。hip-hop,这导致 2 比 1 的投票支持 jazz,并且新的数据点将被分类为 jazz。使用欧几里得距离计算距离(参见毕达哥拉斯定理[6])。

欧几里德距离方程。克里斯·麦考密克。

选择 k 值可能是一个复杂的过程,有几种不同的方法可以找到最佳值。然而,对于这个数据集,我将 k 近似为 n 的平方根。

首先,为了准备我们的数据,我们需要合并每种风格的两个数据帧,随机化这些行,并标记每首歌曲的类别(风格)。对于这个项目,爵士= 0,嘻哈= 1

这是我们所有歌曲的完整标签样本。

标记数据很重要,因为这些标记稍后将用于评估模型的准确性。

接下来,我们需要在训练数据和测试数据之间拆分数据。虽然 KNN 模型在技术上不需要训练(因为算法携带所有数据),但比较训练和测试样本的准确性将是一种方便的感觉检查。

培训 KNN 模特

与你可能习惯看到的通常的训练和测试分割不同,最好是对训练数据使用 k 倍交叉验证来获得我们的初始预测。这是通过随机移动数据集,然后将其分成 k 个部分(折叠)来实现的。对于这个例子,我选择了 k = 10。因此,k-folds 算法分离出一个折叠,将其用作测试集。其他九个折叠构成了训练数据。然后,将 KNN 模型拟合在训练集上,并在测试集上进行评估,保存评估分数并丢弃模型[7]。这个过程然后重复十次,不同的折叠用作测试集。这导致每个折叠在训练数据中使用九(k-1)次,并且作为测试集使用一次。有关该过程的直观解释,请参见下图:

k 倍交叉验证示例。来源:丹·奥弗

这种方法是优越的,因此将数据集一次分割成 80%训练 20%测试分割。这是因为这向我们展示了模型在看不见的数据上表现得有多好。在前一种情况下,存在过度拟合模型的风险。

我们可以使用流行的 python 模块 sci-kit learn 来拟合模型:

取交叉验证分数的平均值,我们的模型在训练集上的准确率为 90%。伟大的结果!

将 KNN 模型与测试集相匹配

有了测试数据,我们就用剩下的值(再来 721 首)。

性能

将模型拟合到我们的训练数据,我们的模型在测试集上的准确率为 91%。作为感觉检查,这类似于我们的训练集上的准确度分数。我们可以使用混淆矩阵来可视化模型的性能。

混淆矩阵描述了我们的 KNN 分类模型的性能[8]。一个真正的正数告诉我们算法预测的是 1,而实际的类是 1。假阳性告诉我们模型预测的是 1,而实际的类是 0。反过来适用于真否定和假否定。我们的混淆矩阵告诉我们,模型中的错误主要来自假阴性。

KNN 模型的混淆矩阵。来源:内森·托马斯。

所以,成功!我们已经成功创建了一个模型,利用机器学习的力量,以 91%的准确率预测歌曲的风格。

感谢阅读!如果您有任何见解,请随时发表评论。包含我用来做这个项目的源代码的完整 Jupyter 笔记本可以在我的 Github 库上找到。

参考资料:

[1]《每日电讯报》,2020 年。“最佳音乐流媒体服务:苹果音乐、Spotify、YouTube 音乐和亚马逊音乐对比”。可在:https://www . telegraph . co . uk/technology/0/best-music-streaming-services-apple-music-Spotify-Amazon-music/获取

[2] Spotify,2020 年。《Web API 教程》。可从以下网址获得:https://developer . Spotify . com/documentation/we b-API/quick-start/

[3] Spotify,2020 年。“获取音轨的音频特征”。可从以下网址获得:https://developer . Spotify . com/documentation/we b-API/reference/tracks/get-audio-features/

[4]托马斯·内森,《走向数据科学》,2020 年。“将主成分分析应用于收益率曲线——艰难之路”。可从https://towardsdatascience . com/applying-PCA-to-yield-curve-4d 2023 e 555 b 3获取

[5] GeeksforGeeks,2020。“K-最近邻”。可在:https://www.geeksforgeeks.org/k-nearest-neighbours/

[6]数学趣味,2017。“毕达哥拉斯定理”。可在:https://www.mathsisfun.com/pythagoras.html

[7]机器学习掌握,2019。“k 倍交叉验证的温和介绍”。可在:https://machinelearningmastery.com/k-fold-cross-validation/

[8] GeeksforGeeks,2020 年。《机器学习中的混淆矩阵》。可从以下网址获得:https://www . geeks forgeeks . org/confusion-matrix-machine-learning/

[9] S .罗森博格、戴维;彭博,2018。"机器学习基础,第一讲:黑盒机器学习."可在:https://bloomberg.github.io/foml/#lectures

免责声明:本文表达的所有观点均为本人观点,与先锋集团或任何其他金融实体无关。我不是一个交易者,也没有用本文中的方法赚钱。这不是财务建议。

使用 Kafka 作为数据湖中的临时数据存储和数据丢失预防工具

原文:https://towardsdatascience.com/using-kafka-as-a-temporary-data-store-and-data-loss-prevention-tool-in-the-data-lake-5472f2b586e?source=collection_archive---------17-----------------------

汤姆·盖诺尔在 Unsplash 上的照片

探索 Kafka 如何用于存储数据以及作为流媒体应用的数据丢失预防工具

介绍

Apache Kafka 是一个流媒体平台,允许创建实时数据处理管道和流媒体应用程序。Kafka 对于一系列用例来说是一个优秀的工具。如果您对 Kafka 如何用于 web 应用程序的度量收集的例子感兴趣,请阅读我的上一篇文章

Kafka 是数据工程师工具箱中的一种强大技术。当您知道 Kafka 的使用方式和位置时,您就可以提高数据管道的质量,并更高效地处理数据。在本文中,我们将看一个例子,说明 Kafka 如何应用于更不寻常的情况,如在亚马逊 S3 存储数据和防止数据丢失。如您所知,数据存储的容错性和持久性是大多数数据工程项目的关键要求之一。所以,知道如何以满足这些需求的方式使用卡夫卡是很重要的。

这里我们将使用 Python 作为编程语言。要从 Python 中与 Kafka 进行交互,还需要有一个特殊的包。我们将使用 kafka-python 库。要安装它,您可以使用以下命令:

pip install kafka-python

作为数据仓库的卡夫卡

卡夫卡可以用来存储数据。您可能想知道 Kafka 是关系数据库还是 NoSQL 数据库。答案是,非此即彼。

Kafka 作为一个事件流平台,处理的是流数据。同时,Kafka 可以在删除数据之前存储一段时间。这意味着 Kafka 不同于传统的消息队列,传统的消息队列在消费者阅读完消息后会立即丢弃消息。Kafka 存储数据的时间称为保留时间。理论上,你可以把这个周期设置为永远。Kafka 还可以在持久存储上存储数据,并通过集群内的代理复制数据。这只是使卡夫卡看起来像一个数据库的另一个特征。

那么,为什么 Kafka 没有被广泛用作数据库,为什么我们没有提出这可能是一种数据存储解决方案的想法?最简单的原因是因为 Kafka 有一些特性,这些特性对于一般的数据库来说是不典型的。例如,Kafka 也不提供对数据的任意访问查找。这意味着没有查询 API 可用于获取列、过滤列、将列与其他表连接等等。其实还有一个卡夫卡 StreamsAPI,甚至还有一个 ksqlDB。它们支持查询,非常类似于传统的数据库。但它们就像卡夫卡身边的脚手架。他们充当消费者,在数据被消费后为您处理数据。所以,当我们谈论 Kafka 而不是它的扩展时,这是因为 Kafka 中没有像 SQL 这样的查询语言来帮助你访问数据。顺便说一下,现代的数据湖引擎,比如 Dremio T1,可以解决这个问题。Dremio 支持使用 SQL 与本身不支持 SQL 的数据源进行交互。例如,你可以在 AWS S3 保存 Kafka 流的数据,然后使用 Dremio AWS edition 访问它。

卡夫卡也专注于用流工作的范例。Kafka 旨在充当基于数据流的应用程序和解决方案的核心。简而言之,它可以被视为大脑,处理来自身体不同部位的信号,并通过解释这些信号让器官工作。Kafka 的目标不是取代更传统的数据库。卡夫卡生活在一个不同的领域,它可以与数据库交互,但它不是数据库的替代品。在 Dremio 的帮助下,Kafka 可以轻松地与数据库和云数据湖存储(如亚马逊 S3 和微软 ADLS)集成。

请记住,卡夫卡具有存储数据的能力,并且数据存储机制相当容易理解。Kafka 存储从第一条消息到现在的记录(消息)日志。使用者从该记录日志中指定的偏移量开始获取数据。这是它看起来的简化解释:

偏移可以在历史中向后移动,这将迫使消费者再次读取过去的数据。

因为 Kafka 不同于传统的数据库,所以它可以用作数据存储的情况也有些特殊。以下是其中的一些:

  • 当处理逻辑改变时,从头开始重复数据处理;
  • 当一个新系统包含在处理管道中,并且需要从头或从某个时间点开始处理所有以前的记录时。此功能有助于避免将一个数据库的完整转储复制到另一个数据库;
  • 当消费者转换数据并将结果保存在某个地方,但出于某种原因,您需要存储数据随时间变化的日志。

在本文的后面,我们将看一个例子,说明 Kafka 如何在与上述第一个用例类似的用例中用作数据存储。

Kafka 作为防止数据丢失的工具

许多开发人员选择 Kafka 作为他们的项目,因为它提供了高水平的持久性和容错性。这些功能是通过在磁盘上保存记录和复制数据来实现的。复制意味着数据的相同副本位于集群中的几个服务器(Kafka 代理)上。因为数据保存在磁盘上,所以即使 Kafka 集群在一段时间内处于非活动状态,数据仍然存在。由于有了复制,即使代理中的一个或几个集群受损,数据也能得到保护。

数据被消费后,通常会被转换和/或保存在某个地方。有时,在数据处理过程中,数据可能会损坏或丢失。在这种情况下,Kafka 可以帮助恢复数据。如果需要,Kafka 可以提供一种从数据流的开头执行操作的方法。

您应该知道,用于控制数据丢失防护策略的两个主要参数是复制因子和保留期。复制因子显示在 Kafka 集群中为给定主题创建了多少冗余数据副本。为了支持容错,您应该将复制因子设置为大于 1 的值。一般来说,推荐值是三。复制因子越大,Kafka 集群越稳定。您还可以使用这个特性将 Kafka 代理放在离数据消费者更近的地方,同时在地理上远程的代理上拥有副本。

保留期是 Kafka 保存数据的时间。很明显,时间越长,保存的数据就越多,在发生不好的事情时(例如,用户因断电而停机,或者数据库因意外的错误数据库查询或黑客攻击而丢失所有数据等)能够恢复的数据也就越多。).

例子

这里有一个例子,说明当数据处理的业务逻辑突然发生变化时,Kafka 的存储能力会非常有帮助。

假设我们有一个收集温度、湿度和一氧化碳(CO)浓度等天气指标的物联网设备。该设备的计算能力有限,因此它所能做的就是将这些指标发送到某个地方。在我们的设置中,设备将向 Kafka 集群发送数据。我们将每秒发送一个数据点,每秒测量一天(换句话说,物联网设备每天收集信息)。下面的图表展示了这一流程:

消费者订阅生产者向其发送消息的主题。消费者然后以指定的方式聚集数据。它在一个月内积累数据,然后计算平均指标(平均温度、湿度和 CO 浓度)。每个月的信息都应该写入文件。该文件只包含一行,值用逗号分隔。以下是该文件的一个示例:

先从《卡夫卡制作人》的创作说起。生产者将位于 producer.py 文件中。在文件的开头,我们应该导入我们需要的所有库并创建 KafkaProducer 实例,它将与位于 localhost:9092 上的 Kafka 集群一起工作:

import random
import json
Import time
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers=[‘localhost:9092’])

下面您可以看到生成数据并将其发送到 Kafka 集群的代码。在顶部,我们定义了温度、湿度和 CO 浓度的初始值( prev_tempprev _ weatherprev_co_concentration )。计数器 i 用于记录索引。对于这个模拟,我们希望生成的值是随机的,但我们也希望避免非常不一致的结果(如在一天内将温度改变 40 度)。所以我们不只是产生随机值。当生成第二天的值时,我们还需要考虑前一天的值。同样,在每次迭代中,我们检查生成的数字是否在可接受的区间内。

topic_name = ‘weather’
i = 0prev_temp = round(random.uniform(-10, 35), 1)prev_humidity = random.randint(1, 100)prev_co_concentration = random.randint(50, 1500)while True:i += 1lower_temp_bound = -10 if (prev_temp-5) < -10 else (prev_temp-5)upper_temp_bound = 35 if (prev_temp+5) > 35 else (prev_temp+5)temperature = round(random.uniform(lower_temp_bound, upper_temp_bound), 1)lower_humid_bound = 1 if (prev_humidity-20) < 1 else (prev_humidity-20)upper_humid_bound = 100 if (prev_humidity+20) > 100 else (prev_humidity+20)humidity = random.randint(lower_humid_bound, upper_humid_bound)lower_co_bound = 50 if (prev_co_concentration-100) < 50 else (prev_co_concentration-100)upper_co_bound = 1500 if (prev_co_concentration+100) > 1500 else (prev_co_concentration+100)co_concentration = random.randint(lower_co_bound, upper_co_bound)weather_dict = {“record_id”: i,“temperature”: temperature,“CO_concentration”: co_concentration,“humidity”: humidity}producer.send(topic_name, value=json.dumps(weather_dict).encode())producer.flush()prev_temp = temperatureprev_humidity = humidityprev_co_concentration = co_concentrationtime.sleep(1)

在生成当前时间戳所需的所有数据之后,脚本从包含数据的字典中创建了 weather_dict 变量。之后,生产者将 JSON 编码的 weather_dict 发送到 Kafka 集群的指定主题。我们将当前值赋给相应的变量,这些变量代表来自前一个时间戳的数据。最后,在执行循环的下一次迭代之前,我们等待一秒钟。

现在让我们来研究一下 consumer.py 文件。在文件的顶部,我们用几个参数定义了消费者。第一个和第二个参数是订阅主题的名称和 Kafka 服务器所在的 URL。 auto_offset_reset 参数定义了发生offset auto frange错误时的行为。‘earliest’值意味着偏移应该移动到最早的可用记录。因此,所有消息(记录)将在出错后被再次使用。 consumer_timeout_ms 参数负责在指定时间段内没有新消息时关闭消费者。 -1 值表示我们不想关掉消费者。

您可以在文档中阅读关于 KafkaConsumer 的这些和其他参数的更多信息。

import json
from kafka import KafkaConsumer
consumer = KafkaConsumer(‘weather’,bootstrap_servers=[‘localhost:9092’],auto_offset_reset=’earliest’,consumer_timeout_ms=-1)

让我们转到 consumer.py 文件中最重要的部分。开始时,我们定义计数器 i 和我们将在一个月中存储数据的列表。在每次迭代中,我们将解码消息(从中提取 weather_dict )并将当天的值附加到相应的列表中。如果 i 计数器等于 30,我们计算该月指标的平均值。

i = 0
month_temperatures = []
month_humidities = []
month_co = []
month_id = 1
for message in consumer:
i += 1
value = message.value.decode()
weather_dict = json.loads(value)
month_temperatures.append(weather_dict[‘temperature’])
month_humidities.append(weather_dict[‘humidity’])
month_co.append(weather_dict[‘CO_concentration’])
if i == 30:
month_aggregation = {‘month_id’: month_id,
‘avg_temp’: round(sum(month_temperatures)/len(month_temperatures), 1),
‘avg_co’: round(sum(month_co)/len(month_co)),
‘avg_humidity’: round(sum(month_humidities)/len(month_humidities))
}with open(‘weather_aggregation.txt’,’a’) as file:data = [str(month_aggregation[‘month_id’]), str(month_aggregation[‘avg_temp’]),str(month_aggregation[‘avg_co’]), str(month_aggregation[‘avg_humidity’])]
file.write(“,”.join(data))
file.write(“,”)i = 0
month_id += 1
month_temperatures = []
month_humidities = []
month_co = []

接下来,我们打开文件weather _ aggregation . txt并将数据写入其中。数据写在一行中,没有换行。因此,需要读取文件的程序应该知道每 5 个值就是新数据点的开始。

在运行生产者和消费者之前,您应该运行 Kafka 集群。您可以使用以下命令完成此操作(假设您已经安装了 Kafka):

sudo kafka-server-start.sh /etc/kafka.properties

以下是输出文件的样子:

假设现在时间飞逝,过了一段时间后,业务需求发生了变化。现在我们需要以不同的方式处理天气数据。

首先,按照前面的要求,聚合指标(平均值)应该按周计算,而不是按月计算。其次,我们需要将摄氏温度转换成华氏温度。最后,我们想改变存储逻辑。而不是创建一个。txt 文件并将所有数据写入一个文件,我们需要创建一个包含列和行的 CSV 文件。每行应代表一个数据点(一周的数据)。此外,我们希望将相同的数据保存到 AWS S3 存储桶中。

更改代码来实现这些更改不成问题。但是我们之前收集了很多数据,我们不想丢失它们。在理想情况下,我们希望从头开始重新计算所有指标。因此,在结果中,我们需要接收新格式的数据,但要确保它包括我们之前使用不同处理方法的那些时间段。卡夫卡的储存能力会帮助我们。

让我们探索一下我们需要对代码进行的修改(文件 consumer.py )。首先,我们需要导入 boto3 库,指定 AWS 凭证,并实例化资源(S3)。此外,我们用列表更改了变量的名称,使它们反映了这样一个事实,即它们累积的是每周数据,而不是每月数据。下一个变化是,我们现在查看每第 7 条记录,以便执行聚合(之前我们等待每第 30 条记录)。另外,我们实现了从摄氏温度到华氏温度的转换公式((c * 1.8) + 32)。

import boto3
ACCESS_KEY = “<AWS_ACCESS_KEY>”SECRET_KEY = “<AWS_SECRET_KEY>”s3 = boto3.resource(‘s3’, aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY)i = 0week_temperatures = []week_humidities = []week_co = []week_id = 1for message in consumer:i += 1value = message.value.decode()weather_dict = json.loads(value)week_temperatures.append(weather_dict[‘temperature’])week_humidities.append(weather_dict[‘humidity’])week_co.append(weather_dict[‘CO_concentration’])if i == 7:week_aggregation = {‘week_id’: week_id,‘avg_temp’: round((sum(week_temperatures)/len(week_temperatures) * 1.8), 1)+32,‘avg_co’: round(sum(week_co)/len(week_co)),‘avg_humidity’: round(sum(week_humidities)/len(week_humidities))}if week_id == 1:with open(‘weather_aggregation.csv’,’a’) as file:data = [‘week_id’, ‘avg_temp’, ‘avg_co’, ‘avg_humidity’]file.write(“,”.join(data))file.write(“\n”)with open(‘weather_aggregation.csv’,’a’) as file:data = [str(week_aggregation[‘week_id’]), str(week_aggregation[‘avg_temp’]),str(week_aggregation[‘avg_co’]), str(week_aggregation[‘avg_humidity’])]file.write(“,”.join(data))file.write(“\n”)s3.Object(‘s3-bucket_name’, ‘weather_aggregation.csv’).put(Body=open(‘weather_aggregation.csv’, ‘rb’))i = 0week_id += 1week_temperatures = []week_humidities = []week_co = []
ACCESS_KEY = “<AWS_ACCESS_KEY>”SECRET_KEY = “<AWS_SECRET_KEY>”s3 = boto3.resource(‘s3’, aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY)i = 0week_temperatures = []week_humidities = []week_co = []week_id = 1for message in consumer:i += 1value = message.value.decode()weather_dict = json.loads(value)week_temperatures.append(weather_dict[‘temperature’])week_humidities.append(weather_dict[‘humidity’])week_co.append(weather_dict[‘CO_concentration’])if i == 7:week_aggregation = {‘week_id’: week_id,‘avg_temp’: round((sum(week_temperatures)/len(week_temperatures) * 1.8), 1)+32,‘avg_co’: round(sum(week_co)/len(week_co)),‘avg_humidity’: round(sum(week_humidities)/len(week_humidities))}if week_id == 1:with open(‘weather_aggregation.csv’,’a’) as file:data = [‘week_id’, ‘avg_temp’, ‘avg_co’, ‘avg_humidity’]file.write(“,”.join(data))file.write(“\n”)with open(‘weather_aggregation.csv’,’a’) as file:data = [str(week_aggregation[‘week_id’]), str(week_aggregation[‘avg_temp’]),str(week_aggregation[‘avg_co’]), str(week_aggregation[‘avg_humidity’])]file.write(“,”.join(data))file.write(“\n”)s3.Object(‘s3-bucket_name’, ‘weather_aggregation.csv’).put(Body=open(‘weather_aggregation.csv’, ‘rb’))i = 0week_id += 1week_temperatures = []week_humidities = []week_co = []

其他变化与已处理数据的保存有关。现在我们处理 CSV 文件,如果是第一周,我们将列名写入文件。此外,该脚本在每个数据点后添加一个新的行字符,以便在新的一行中写入有关每周的信息。最后,我们还将 weather_aggregation.csv 文件插入到 AWS S3 存储中。桶S3-桶名创建较早。

为了从我们的物联网设备生命周期的开始(当它开始向 Kafka 发送数据时)重新计算聚合,我们需要将偏移量移动到消息队列的开始。在 kafka-python 中,使用消费者的seek _ to _ begin()方法非常简单:

consumer.seek_to_beginning()

换句话说,如果我们将调用方法放在上面描述的代码之前,我们将偏移量移动到消费者队列的开始。这将迫使它再次读取已经读取和处理过的消息。这证明了一个事实,即当 Kafka 存储消息时,它不会在消费者阅读一次之后删除数据。下面是更新后的消费者生成的 weather_aggregation.csv 文件:

这个例子表明,卡夫卡是一个有用的数据存储系统。Kafka 在防止数据丢失方面的优势显而易见。假设我们的消费者所在的服务器停机了一段时间。如果卡夫卡没有数据存储能力,制作者发送的所有信息都会丢失。但我们知道,当消费者再次活着时,它将能够获取 Kafka 集群在消费者停机期间积累的所有消息。使用此功能不需要其他操作。你不需要移动偏移量。它将位于消费者最后一次使用的消息上;它将从停止的地方开始消耗数据。

我们演示的例子很简单,但它让我们明白卡夫卡是多么有用。目前,我们有 CSV 文件,其中包含每周汇总的天气数据。我们可以用它来进行数据分析(例如,看看将 Tableau 与亚马逊 S3 教程集成),创建机器学习模型(使用 ADLS Gen2 数据创建回归机器学习模型),或者用于应用程序的内部目的。Dremio 还允许我们加入数据湖中的数据源,并使用 SQL 对其进行处理(即使原始数据源不支持 SQL——参见合并来自多个数据集的数据使用 Dremio 和 Python 分析学生成绩数据的简单方法教程)。Dremio 是数据工程工具包中的一个有用工具。

结论

在本文中,我们探讨了 Kafka 如何用于存储数据,以及如何作为流媒体应用程序的数据丢失预防工具。我们提供了这些特性的概述,列出了它们有用的用例,并解释了为什么 Kafka 不是传统数据库的替代品。我们展示了一个实现了不同的数据处理和转换方法的案例。与此同时,利益相关者希望从一开始就根据我们处理的所有数据的新方法来计算数据处理的结果。有了卡夫卡,这个问题迎刃而解。

使用 Kafka 在云数据湖中收集 Web 应用程序指标

原文:https://towardsdatascience.com/using-kafka-for-collecting-web-application-metrics-in-your-cloud-data-lake-b97004b2ce31?source=collection_archive---------25-----------------------

伊利亚·巴甫洛夫在 Unsplash 上的照片

本文演示了如何使用 Kafka 从 web 应用程序中收集数据湖存储(如亚马逊 S3)的指标。

度量收集

指标是反映流程或系统状态的指标(值)。当我们有一系列数值时,我们也可以对趋势或季节性做出结论。总之,度量是过程或系统如何发展的指示器。指标可以由应用程序、硬件组件(CPU、内存等)生成。),web 服务器,搜索引擎,物联网设备,数据库等等。度量可以反映系统的内部状态,甚至一些真实世界的过程。真实世界指标的示例包括电子商务网站,它可以生成关于任何给定时间段内新订单数量的信息,空气质量设备,它可以收集关于空气中不同化学物质浓度的数据,以及 CPU 负载,这是关于计算机系统内部状态的指标的示例。

可以实时分析收集的指标,也可以存储起来供以后进行批量分析。收集的指标也可以用来训练机器学习模型。

收集指标可能是一个复杂的过程,因为它取决于许多参数和条件。指标的来源产生值,然后这些值或者被传送到云数据湖存储,或者被实时使用。将指标从源交付到存储的方法以及存储的方式可能会因情况而异。

有助于收集指标的工具之一是 Apache Kafka。

卡夫卡概述

Apache Kafka 是一个用于构建实时数据处理管道和流应用程序的工具。Kafka 是一个分布式系统,这意味着它可以从几个源作为一个集群运行。Kafka 集群是不同的数据生成器(称为生产者)和数据消费者(称为消费者)使用的中心枢纽。应用程序(桌面、web、移动)、API、数据库、web 服务和物联网设备都是生产者的典型例子。生产者是向 Kafka 集群发送数据的实体。消费者是从 Kafka 集群接收数据的实体。Kafka 集群可以由一个或多个 Kafka 代理组成。

卡夫卡使用主题来组织数据。主题是数据流的类别。主题中的每个数据点都有自己唯一的时间戳、键和值。生产者可以将数据写入特定的主题,而消费者可以订阅所需的主题来接收特定的数据集。Kafka 支持数据复制,即在不同的代理上创建相同数据的副本。这可以防止其中一个代理由于某种原因损坏或中断时数据丢失。

Kafka 是最受欢迎的事件流平台和消息队列之一。许多大公司用它来管理他们的数据管道。以下是卡夫卡提供的几个最重要的优势:

  • 可扩展性(由于支持分布式操作)
  • 容错
  • 持久性
  • 快速操作
  • 高流通量
  • 实时模式以及以批处理模式工作的能力

让我们看看 Kafka 是如何用于收集指标的。

Kafka 如何用于收集指标

通常,收集指标是实时完成的。这意味着指标的来源不断地生成数据,并可以作为数据流发送。正如我们所知,Kafka 是一个处理数据流的好工具,这就是为什么它可以用于收集指标。

在这个例子中,我们将使用一个简单的 Flask web 应用程序作为生产者。它将向 Kafka 集群发送有关其活动的指标。消费者将是一个 python 脚本,它将从 Kafka 接收指标,并将数据写入 CSV 文件。该脚本将从 Kafka 接收指标,并将数据写入 CSV 文件。Python 应用程序本身可以丰富数据,并将指标发送到云存储。在这一阶段,数据可供一系列同类最佳的数据湖引擎(如 Dremio)查询和处理。

这里有个小技巧:如果你想进行度量监控,可以使用 Prometheus、Grafana、Kibana 等工具。管道是相同的:web 应用程序将数据发送到 Kafka 集群中,然后指标应该被交付到前面提到的平台,在那里进行可视化和分析。如果发生某些事件,还可以设置警报和通知。

例子

让我们看看卡夫卡帮助下的度量收集的例子。我们将使用 Flask web 应用程序作为度量的来源。使用该应用程序,人们可以创建订单和购买必需品。这是该网站的主页:

非常简单:当用户点击新订单按钮时,他们将进入下一页,在那里他们可以下订单。

当用户选中复选框字段时,这意味着他们想立即为订单付款。否则,货物将在信用条件下供应。用户点击下单按钮后,进入下一页:

在此页面上,用户可以查看已创建订单的详细信息。这里的新元素是总价,其计算方法是将 1 个单位的价格乘以订购数量。

现在让我们看看应用程序的代码。它有几个文件,包括表格,模板,配置,数据库(SQLite)等。但是我们将只演示在生成和发送指标到 Kafka 集群中起作用的文件。

需要注意的是,对于本文,我们将使用 kafka-python 包。它允许我们直接从 Python 代码中使用 Kafka。我们使用以下命令安装它:

pip install kafka-python

下面,你可以看到来自 models.py 文件的代码。这个文件描述了数据库的结构。我们那里只有一张桌子叫做订单。它由一个 Python 类表示,其中每个属性都是数据库中的列。但是这个文件最有趣的部分是send _ order _ info _ to _ Kafka()函数。

import jsonfrom app import dbfrom sqlalchemy import eventfrom kafka import KafkaProducerclass Order(db.Model):id = db.Column(db.Integer, primary_key=True)customer_email = db.Column(db.String(120),nullable=False, default=””)amount = db.Column(db.Integer)total_price = db.Column(db.Integer)is_prepaid = db.Column(db.Boolean, default=False)@event.listens_for(Order, ‘after_insert’)def send_order_info_to_kafka(mapper, connection, target):assert target.id is not Noneproducer = KafkaProducer(bootstrap_servers=[‘localhost:9092’])topic_name = ‘new_orders’order_dict = {“order_id”: target.id,“order_amount”: target.amount,“order_total_price”: target.total_price,“is_paid”: target.is_prepaid}producer.send(topic_name, value=json.dumps(order_dict).encode())producer.flush()

这个函数由event . listens _ for()decorator(从 sqlalchemy 库导入)增强。当关于新订单的记录被插入数据库时,这个装饰器监视事件。当这种情况发生时,该函数创建一个 KafkaProducer 实例(指向正在运行的 Kafka 集群所在的 URL)并指定 Kafka 主题的名称new_orders 。然后,该函数创建消息体。我们想将订单的统计数据发送给 Kafka。这就是为什么对于每个订单,我们都用订单金额、总价以及是否预付的信息来创建字典。然后我们把这个字典转换成 JSON 格式,编码,用生产者的方法 send()flush() 发送给卡夫卡。因此,每当用户创建新订单时,都会触发该函数。该函数的目的是向 Kafka 集群发送有关已创建订单的信息。

我们希望再收集一组指标——特定时间段内的请求数量。这是监控任何 web 应用程序的一个常见指标。因此,每当有人访问我们网站上的页面时,我们都需要向我们的 Kafka 集群发送通知。下面是我们如何实现这种行为。在文件 utils.py 中我们定义了名为的函数 ping_kafka_when_request() 。这个函数的主体与我们之前看到的函数非常相似。它创建生产者的实例,定义生产者应该提交消息的主题名称( web_requests ),然后使用 send()flush() 方法发送消息。这个函数稍微简单一点,因为我们不需要为消息创建复杂的主体。每当一个新的请求出现时,我们就发送 value=1

from kafka import KafkaProducerdef ping_kafka_when_request():producer = KafkaProducer(bootstrap_servers=[‘localhost:9092’])topic_name = ‘web_requests’producer.send(topic_name, value=”1".encode())producer.flush()

为了使这个函数工作,我们需要在每个页面的视图函数中调用它。这可以在 routes.py 文件中完成(参见下面的代码)。有三个函数:( index()create_order()order_complete() )。这些函数中的每一个都负责在网站上呈现页面时执行一些逻辑。最复杂的函数是 create_order() 函数,因为它应该处理表单提交和向数据库插入新记录。但是如果我们谈论与 Kafka 的交互,您应该注意到这样一个事实:我们从 utils 文件中导入了ping _ Kafka _ when _ request()函数,并在每个视图函数内部调用它(在执行该函数中所有剩余的代码之前)。

from app import app, dbfrom app.models import Orderfrom flask import render_template, redirect, sessionfrom app.forms import OrderFormfrom .utils import ping_kafka_when_request@app.route(‘/’)def index():ping_kafka_when_request()return render_template(‘index.html’)@app.route(‘/order-new’, methods=[‘GET’, ‘POST’])def create_order():ping_kafka_when_request()form = OrderForm()if form.validate_on_submit():price = 15customer_email = form.email.dataamount = form.amount.datais_prepaid = form.is_paid_now.datatotal_price = amount * priceorder = Order(customer_email=customer_email,amount=amount,total_price=total_price,is_prepaid=is_prepaid)db.session.add(order)db.session.commit()session[‘order’] = {“email”: customer_email,“amount”: amount,“total_price”: total_price,“is_paid”: is_prepaid}return redirect(‘/order-new-complete’)return render_template(‘new_order.html’,title=’Make a new order’, form=form)@app.route(‘/order-new-complete’)def order_complete():ping_kafka_when_request()return render_template(‘new_order_complete.html’,order=session[‘order’])

这些是我们建筑的生产者方面。我们解释说,代码需要位于 web 应用程序内部,以便向 Kafka 集群发送指标。现在让我们看看另一面——消费者。

第一个文件是 consumer_requests.py 。我们来分块考察一下。在文件的开头,我们导入我们需要的所有包,并创建 Kafka consumer 的实例。我们将应用几个参数,以便它能够按照预期的方式工作。你可以在文档中读到它们。最重要的参数是我们希望为消费者订阅的主题名称(web_requests)和指向 Kafka 集群所在服务器的 bootstrap_servers 参数。

import timeimport threadingimport datetimefrom kafka import KafkaConsumerconsumer = KafkaConsumer(‘web_requests’,bootstrap_servers=[‘localhost:9092’],auto_offset_reset=’earliest’,enable_auto_commit=True,auto_commit_interval_ms=1000,consumer_timeout_ms=-1)

接下来,我们需要创建一个函数,它将每分钟轮询 Kafka 集群一次,并处理 Kafka 将返回的消息。该函数的名称是fetch _ last _ min _ requests(),您可以在下面的代码示例中看到它。它需要两个参数作为输入。 next_call_in 参数显示该函数的下一次调用应该发生的时间(记住我们需要每 60 秒从 Kafka 获取新数据)。不需要 is_first_execution 参数。默认情况下,它等于 False。

在函数开始时,我们确定下一次调用该函数的时间(从现在起 60 秒)。此外,我们为请求初始化计数器。然后,如果是第一次执行,我们创建文件 requests.csv 并向其中写入一行标题。这个数据集的结构将会很简单。它应该有两列— 日期时间请求数量。每一行在日期时间列中有时间戳,在请求数量列中有网站在给定时间内处理的请求数量。

def fetch_last_min_requests(next_call_in, is_first_execution=False):next_call_in += 60counter_requests = 0if is_first_execution:with open(‘requests.csv’,’a’) as file:headers = [“datetime”, “requests_num”]file.write(“,”.join(headers))file.write(‘\n’)else:batch = consumer.poll(timeout_ms=100)if len(batch) > 0:for message in list(batch.values())[0]:counter_requests += 1with open(‘requests.csv’,’a’) as file:data = [datetime.datetime.now().strftime(“%Y-%m-%d %H:%M:%S”), str(counter_requests)]file.write(“,”.join(data))file.write(‘\n’)threading.Timer(next_call_in — time.time(),fetch_last_minute_requests,[next_call_in]).start()

如果这不是函数的第一次执行,我们将强制消费者轮询 Kafka 集群。您应该将 poll() 方法的 timeout_ms 参数设置为一个大于零的数字,因为否则,您可能会错过一些消息。如果 poll() 方法返回非 void 对象( batch ),我们希望遍历所有获取的消息,并且在每次迭代中,将 count_requests 变量加 1。然后,我们打开 request.csv 文件,生成行(由逗号连接的当前日期时间和 counter_requests 值的字符串),并将该行追加到文件中。

给定函数中的最后一行是定时器设置。我们将三个参数插入到定时器对象中。第一个是触发函数(第二个参数)的时间段。这段时间是通过从存储在 next_call_in 变量中的时间减去当前时间戳来动态计算的,这是我们在函数开始时计算的。定时器对象的第三个参数是带有参数的列表,这些参数应该传递给我们想要执行的函数。我们立即使用其 start() 方法启动计时器。

为什么我们需要这样一种微妙的方式来定义下一次函数调用发生的时间呢?难道就不能用更流行的 time.sleep() 方法吗?答案是否定的。我们使用这种方法是因为位于函数内部的逻辑的执行需要一些时间。例如,Kafka 集群轮询至少需要 100 毫秒。此外,我们还需要对请求进行计数,并将结果写入文件。所有这些事情都很耗时,如果我们简单地使用 time.sleep() 暂停执行,分钟周期将在下一次迭代中漂移。这可能会破坏结果。使用穿线。Timer object 是一种稍微不同但更合适的方法。我们不是暂停 60 秒,而是通过减去在函数体内执行代码所花费的时间来计算函数应该被触发的时间。

现在我们可以使用定义的函数了。只需在当前时间前初始化 next_call_in 变量,并使用fetch _ last _ minute _ requests()函数,将该变量作为第一个参数,将 True 标志作为第二个参数(以标记这是第一次执行)。

next_call_in = time.time()fetch_last_minute_requests(next_call_in, True)

这就是 consumer_requests.py 文件的全部内容。但是在执行它之前,您应该运行 Kafka 集群。下面是如何从终端本地完成此操作(假设您已经安装了它):

sudo kafka-server-start.sh /etc/kafka.properties

现在您可以运行该文件了。然后在你的浏览器中进入网络应用程序(你可以使用命令 flask run 运行 Flask 应用程序)并尝试浏览它——访问它的页面。过一会儿,您应该在您的消费者文件所在的文件夹中有文件 requests.csv 。该文件可能如下所示(实际数据会有所不同,取决于您访问应用页面的次数):

我们所做的是构建管道,允许我们使用 Kafka 和 Python 收集 web 应用程序指标(请求数量)。

现在我们来看另一个消费者。我们将这个文件命名为 consumer_orders.py 。文件的第一部分与前一个文件非常相似。一个区别是我们导入了 json 库,因为我们需要处理 json 编码的消息。另一个区别是 Kafka 消费者订阅了新订单主题。

import jsonimport timeimport datetimeimport threadingfrom kafka import KafkaConsumerconsumer = KafkaConsumer(‘new_orders’,bootstrap_servers=[‘localhost:9092’],auto_offset_reset=’earliest’,enable_auto_commit=True,auto_commit_interval_ms=1000,consumer_timeout_ms==-1)

主要函数是fetch _ last _ minute _ orders()。与前一个消费者的函数不同的是,这个函数有六个计数器,而不是只有一个。我们希望统计一分钟内创建的订单总数、订购的商品总数、所有订单的总成本、预付订单的数量、预付订单中的商品数量以及所有预付订单的总价。这些指标可能对进一步的分析有用。

另一个区别是,在开始计算上述值之前,我们需要使用 json 库解码从 Kafka 获取的消息。所有其他逻辑都与处理请求的消费者相同。应该写入数据的这个文件叫做 orders.csv

def fetch_last_minute_orders(next_call_in, is_first_execution=False):next_call_in += 60count_orders = 0count_tot_amount = 0count_tot_price = 0count_orders_paid = 0count_tot_paid_amount = 0count_tot_paid_price = 0
if is_first_execution:with open(‘orders.csv’,’a’) as file:headers = [“datetime”, “total_orders_num”,“total_orders_amount”, “total_orders_price”,“total_paid_orders_num”,“total_paid_orders_amount”,“Total_paid_orders_price”]file.write(“,”.join(headers))file.write(‘\n’)else:batch = consumer.poll(timeout_ms=100)if len(batch) > 0:for message in list(batch.values())[0]:value = message.value.decode()order_dict = json.loads(value)# all orderscount_orders += 1count_tot_amount += order_dict[“order_amount”]count_tot_price += order_dict[“order_total_price”]if order_dict[“is_paid”]:# only paid orderscount_orders_paid += 1count_tot_paid_amount += order_dict[“order_amount”]count_tot_paid_price += order_dict[“order_total_price”]with open(‘orders.csv’,’a’) as file:data = [datetime.datetime.now().strftime(“%Y-%m-%d %H:%M:%S”),str(count_orders), str(count_tot_amount),str(count_tot_price), str(count_orders_paid),str(count_tot_paid_amount),str(count_tot_paid_price)]file.write(“,”.join(data))file.write(‘\n’)threading.Timer(next_call_in — time.time(),fetch_last_minute_orders,[next_call_in]).start()

文件的最后一部分是相同的:获取当前时间并触发上面定义的函数:

next_call_in = time.time()fetch_last_minute_orders(next_call_in, True)

假设您已经运行了 Kafka 集群,那么您可以执行 consumer_orders.py 文件。接下来,进入你的 Flask 应用程序,在几分钟内创建一些订单。生成的 orders.csv 文件将具有以下结构:

您可以看到我们的 Python 脚本(尤其是那些处理订单数据的脚本)执行了一些数据丰富。

数据工程师可以定制这个过程。例如,如果应用程序非常大并且负载很高,Kafka 集群应该水平扩展。对于不同的指标,您可以有许多主题,并且每个主题都可以用自己的方式进行处理。可以跟踪新用户注册、用户变动、反馈数量、调查结果等。

此外,您可以设置一些低级指标的集合,如 CPU 负载或内存消耗。基本的管道将是类似的。还值得一提的是,将数据写入 CSV 文件并不是唯一的选择,您还可以利用开放的数据格式,如 Parquet,并将所有这些数据直接放到您的数据湖中。

一旦您收集了指标,您就可以使用 Dremio 直接查询数据,以及创建和共享虚拟数据集,这些数据集将指标与数据湖中的其他来源相结合,所有这些都不需要任何副本。

结论

在本文中,我们为从 Flask web 应用程序收集指标构建了一个数据管道。Kafka 集群用作数据生产者(部署在 web 应用程序中)和数据消费者(Python 脚本)之间的层。Python 脚本充当从 Kafka 获取指标,然后处理和转换数据的应用程序。给出的示例是基本的,但是您可以根据自己的需要,使用它来构建更复杂、更多样的指标收集管道。然后,您可以使用 Dremio 的数据湖引擎来查询和处理结果数据集。

使用 Kafka 优化你的 Twitter 流的数据流

原文:https://towardsdatascience.com/using-kafka-to-optimize-data-flow-of-your-twitter-stream-90523d25f3e8?source=collection_archive---------12-----------------------

高效处理来自 Twitter 的大数据指南

萨法尔·萨法罗夫在 Unsplash 上拍摄的照片

我最近在做一个大数据分析项目,在这个项目中,我从 Twitter 上长时间收集了大约 50-60 个热门话题的实时流数据。这个项目严重依赖 Twitter API 进行数据收集,使用 Twitter API 进行数据收集的一个主要限制是,客户端机器必须始终跟上数据流,如果不能,数据流就会断开,相信我,这种情况会经常发生,尤其是在您实时处理大量数据的情况下!在本文中,我将向您展示我的解决方案,通过使用 Kafka 作为消息队列来克服这一限制。

到目前为止,卡夫卡主要有两种风格,一种是阿帕奇基金会的,另一种是合流派的。对于本文,我们将使用合流版本,因为它为我们提供了一个很好的 Python 客户端来与 Kafka 进行交互。

入门

由于网上有很多关于如何从 Twitter 流式传输数据的惊人资源,我不会讨论如何获得您的 Twitter 开发人员帐户并设置 Python 库来从 Twitter 流式传输数据。本文面向已经在使用 Twitter API 进行数据收集并希望优化其数据流的数据科学家/工程师。请注意,这不是一个突破 Twitter 流媒体 API 速率限制的解决方案。

以下是您开始工作所需的物品清单:

  1. Ubuntu 或 Mac OS 驱动的机器
  2. 中级 Python 知识,熟悉 JSON
  3. 熟悉 Twitter API 和 Tweepy Python 库
  4. 融合平台的自我管理版本

到底什么是卡夫卡和合流台?

Kafka 是一个分布式流媒体平台,允许您创建数据流管道,它位于融合平台的核心,该平台包括各种功能,使 Kafka 更易于使用。有关 Kafka 和合流平台的更多详细信息,请参见下面的链接:

[## 什么是合流平台?汇合平台

Confluent 提供了业内唯一的企业级事件流平台

文件汇合](https://docs.confluent.io/current/platform.html#cp-platform)

在 Twitter 上使用 Kafka 的好处

将 Kafka 与您的 Twitter 流一起使用的主要好处之一是容错,因此您将拥有两个模块,而不是拥有一个收集、处理和保存所有内容到 JSON 文件中的 Python 模块。一个称为“生产者”的模块从 twitter 流中收集数据,然后将其作为日志保存到队列中,不做任何处理,另一个称为“消费者”的模块读取日志,然后处理数据,本质上创建了一个解耦的流程。以下是您的数据流外观的高级概述:

图 1.0。卡夫卡如何使用 Twitter

汇合平台设置

首先,你需要去 https://www.confluent.io/download的合流平台网站,下载合流平台的自我管理版本,并将其保存到你选择的目录中。对于我的 mac,它将在用户目录中。

第一步。设置动物园管理员

下载完成后,您需要设置 Zookeeper 服务器,它本质上是一个协调器,维护 Kafka 集群中的节点以及其他关键功能。

在您的终端上运行以下命令(请确保使用您的文件目录更新命令):

cd file_directory_of_your_choice/confluent-5.5.0/etc/kafkanano zookeeper.properties

图 1.1。终端上的 Zookeeper 服务器属性

您可以在这里更改和调整 zookeeper 服务器与 Kafka 集群的交互方式。然而,对于本文,我们将把它保留为默认值。

第二步。建立卡夫卡

现在我们继续设置 Kafka 服务器。在您的终端上运行以下命令:

nano server.properties

图 1.2。终端上的 Kafka 服务器属性

由于我们使用 Kafka 进行优化,我们将把默认日志保留时间减少到 5 小时,并将日志保留和段字节减少到 507374182。向下滚动到服务器属性配置文件中的日志保留策略部分,并进行以下调整:

log.retention.hours=5
log.retention.bytes=507374182
log.segment.bytes=507374182

然后,通过确保 Zookeeper 部分中的端口号正确,确保您的 Kafka 服务器能够连接到 Zookeeper 服务器。我们将使用 Zookeeper 服务器的默认设置。

zookeeper.connect=localhost:2181

第三步。启动服务器

现在大部分设置已经完成,我们将开始运行服务器。

初始化动物园管理员服务器

sudo file_directory_of_your_choice/confluent-5.5.0/bin/zookeeper-server-start file_directory_of_your_choice/confluent-5.5.0/etc/kafka/zookeeper.properties

初始化 Kafka 服务器

sudo file_directory_of_your_choice/confluent-5.5.0/bin/kafka-server-start file_directory_of_your_choice/confluent-5.5.0/etc/kafka/server.properties

第四步。主题创建和分区配置

Kafka 中的主题本质上是在多个分区中发布日志的类别。所以在我们的例子中,为了让我们的 Python 代码与 Kafka 交互,我们需要分配一个主题名并为该主题配置分区。

运行以下命令来创建和配置 Kafka 主题。

sudo file_directory_of_your_choice/confluent-5.5.0/bin/kafka-topics --zookeeper localhost:2181 --create --replication-factor 1 --partitions 2 --topic twitterdata

分区和复制因子可以根据您的偏好和配置进行调整。

Python 代码

为 Python 安装 Kafka API

所以在我们开始使用 Python 中的 Kafka 之前,我们需要安装 Python 中的 Kafka 库。在您的终端上运行以下代码:

pip3 install kafka

生产商模块代码

在您的 IDE 上,创建一个名为 producer 的新 Python 模块。在这里,您可以将 Tweepy 的 on_data 函数与 KafkaProducer 一起使用,将原始 twitter 数据输入 Kafka 集群。

消费模块代码

创建另一个名为 Consumer 的 Python 模块。该模块将从您的 Kafka 集群中读取原始数据,您可以添加额外的功能,如语言检测、情感分类器,而不用担心流断开,因为现在我们已经基本上解耦了整个 Twitter 流。

结论

总之,我们已经创建了一个解耦的 twitter 流,我们本质上将这个流分成两个不同的模块。一个模块用于从 Twitter API 检索数据并将其提供给 Kafka 集群,另一个模块用于从 Kafka 集群读取数据并单独处理数据。这允许我们处理来自 twitter 的原始数据,而不用担心流被断开。

利用卡尔曼滤波器预测冠状病毒传播

原文:https://towardsdatascience.com/using-kalman-filter-to-predict-corona-virus-spread-72d91b74cc8?source=collection_archive---------0-----------------------

本文介绍了在线实时卡尔曼滤波算法的实现,以预测每个给定区域的新冠肺炎传播。

冠状病毒(新冠肺炎)最近引起了世界范围的关注。
据报道,随着冠状病毒病例数量的增加,新冠肺炎的传播对全球健康构成了严重威胁。在这项工作中,我们将尝试预测冠状病毒在每个感染区域的传播。拟合时间序列分析和统计算法,以产生最佳的短期和长期预测。自适应在线卡尔曼滤波器为我们提供了每个地区非常好的一天预测。

首先,让我们简单地将新冠肺炎病毒与一种更古老的致命病毒——伊波拉病毒进行比较。埃博拉不是一种新疾病(第一例病例于 1976 年发现),但在 2014 年和 2018 年,它再次爆发,直到这些天。
*与新冠肺炎约 3.9%的死亡率相比,埃博拉病毒的死亡率更高,可能达到 75%的死亡病例。必须注意的是,新冠肺炎是一种持续的疾病,因此死亡率不是最终的,很可能会增加。
*地点和国家明显不同,埃博拉病毒主要危害非洲和中国及亚洲的新冠肺炎。
*新冠肺炎似乎最有可能在寒冷的天气传播,而埃博拉在温暖的天气传播。
*两者之间的喷发趋势似乎非常相似,两种疾病都表现出迅速有力的喷发。

为了预测冠状病毒的传播,我使用了卡尔曼滤波算法和其他线性模型。优化问题是用 Python 解决的,而脚本可以在 Google Colab 笔记本中找到。这个项目的过程描述如下,完整的代码可以在 Github 这里找到。

预处理数据: *从 Github 中读取数据-包含世界卫生组织数据下每个地点每日确诊、死亡和康复的总病例数。
*固定地区名称并将国家和省连接到一列“地区”。然后,清除数据集中的一些错误字符。

获取人口: 我使用了 python 的“qwikidata”包,它根据地区坐标(经度,纬度)从维基百科中提取人口数据。我们将使用这些数据来评估每个区域的感染率,并在模型中进行测试。

创建时间序列和图表 该脚本允许我们生成每个区域的可视化图表,以获得关于数据的初步见解。绝大多数病例发生在中国湖北(82%),因此提取了不同的地块。
以下是每日总病例数排名靠前的区域。
证实 :

湖北等地区确诊病例总数

我们可以看到一个明显的喷发趋势在湖北与异常日 13.02.20 的一个高跳。在那一天,计数方法发生了变化,这将在以后影响模型。我们无法验证数据的可信度,但假设数据是可靠的。在其他顶级领域,趋势似乎有所不同,也不那么明显。钻石公主号游轮似乎与众不同,自 2 月中旬以来出现了急剧的趋势。(粉色线)

死亡:
*死亡病例以湖北居多,超过 2000 例。之后,河南以 19。
*湖北死亡率为 2.1978%,河南为 1.5055%。在其他地区,人数太少。

湖北等地区总死亡病例

痊愈:
*所有地区的痊愈病例呈急剧趋势,主要在湖北,这可能暗示我们有一个积极的未来。

湖北等地区痊愈病例总数

拟合预测模型阶段 1-一日预测卡尔曼滤波器

我实现了一个卡尔曼滤波算法来解决这个问题,并为每个病例生成 1 天的预测——确诊、死亡、康复;对于每个地区。
卡尔曼滤波器是一种递归算法,它使用随时间推移的时间序列测量值,包含统计噪声并产生未知变量的估计值。我建立了一个在线实时算法。这种算法是自适应的,意味着它不需要大量的历史/训练数据。每天,该算法都会根据新的观察结果进行更新,在完成估计后,它可以生成第二天的预测。
卡尔曼滤波器显示出预测第二天观测值的非常好的结果。它分别跟踪每个区域的趋势,快速调整序列并生成预测。由于我们不需要训练集和测试集,所以评估模型很容易。没有过度拟合或偏见——这是一个在线算法。

实现: 我写了一个 R 脚本来实现一个在线卡尔曼算法,然后使用了 Python 的‘rpy 2’包,它允许我们在 Python 笔记本内部组合 R 脚本并传递结果变量。
短暂初始化后,算法开始跟踪序列,并为每个区域生成 1 天预测。它在每个可能的区域运行。

结果和评估: 为了评估结果,我为每个区域添加了一些基本的误差估计参数:MSE——均方误差,RMSE——均方根误差,MAE——平均绝对误差。我将它们与其他方法进行了比较,以优化模型并找到最佳方法。
该模型也可通过如下所示的图表进行评估。
该脚本允许选择区域并获得合适的绘图和预测。它对每个区域迭代运行。以下是几个例子:

A.北京:橙色线是卡尔曼预测,蓝色线是真实的证实的病例——如前所述,这不是偏差预测,它总结了以前的在线预测。
我们可以看到近乎完美的契合:

卡尔曼预测与实际值-北京

下表显示了预测值与实际值的接近程度。
例如,2010 年 2 月 19 日,卡尔曼预测 394 例,其中 7 例为新确诊病例,而实际为 6 例。明天(20.02.20)卡尔曼预测北京新增 5 例确诊病例。

每日确认的预测与实际-北京

B.湖北但它适应得很快,几天后,将获得更好的预测。

卡尔曼预测 vs 湖北实际确诊病例

下图显示了湖北死亡病例的高预测率

卡尔曼预测 vs 湖北实际死亡病例

下表以数字显示了预测和实际死亡,在湖北。例如,在 18.02,卡尔曼预测有 140 人死亡,而实际死亡人数是 132 人。2002 年 19 月 19 日,卡尔曼预测有 129 人死亡,而实际死亡人数是 108 人。对于明天(20.02),卡尔曼预测另外 132 个新的死亡病例。预测值非常接近真实值。

每日死亡预测与实际-湖北

痊愈每日预测:对于湖北痊愈病例的急剧趋势,我们可以得到一些积极的见解。
卡尔曼预测适应了这一趋势,并预测最近恢复的病例数量会增加。

卡尔曼预测与湖北实际恢复案例

C.上海——证实了的预测。这里也有非常接近真实值的每日预测

卡尔曼预测与实际值-上海

D.河南——有超过 1200 例确诊病例的地区——再次非常好的预测,采用了最近几天确诊病例较少的趋势。

卡尔曼预测与实际值-河南

河南收复预测:

卡尔曼预测与实际值-河南

20 年 2 月 19 日,卡尔曼预测有 56 个新的恢复案例,最终有 51 个。明天它预测又有 48 个新的康复病例。
与湖北类似,我们可以看到最近恢复案例的良好趋势和算法的良好适应性。

每日恢复预测与实际—河南

E.另一个有趣的地区是靠近日本的知名钻石公主号游轮
这个系列的行为与其他地区完全不同,尽管如此,卡尔曼的预测还是做得很好。2 月初船上爆发过病例,随后急剧增加。卡尔曼预测明日(20.02.2020)又有 46 宗新个案。

卡尔曼预测与实际值-钻石公主号游轮

拟合预测模型第 2 阶段—随时间传播和新病例

卡尔曼算法是非常强大的,并提供了一个很好的指示什么将是明天。
但要预测更长的时期,这是不够的。(提醒这是在线算法)。
我拟合了一个线性模型来预测新冠肺炎随时间的传播。
想法是使用时间序列算法,如 Kalman,并将它们作为另一个机器学习算法的特征发送,以及其他应该考虑的特征,如天气、人口、感染率等。
数据不稳定,因此随机森林回归等 ML 模型不太适用。最终,我使用了一个依赖于卡尔曼预测的线性模型。

创建数据集 线性模型的数据集包含不同的结构,其中:
*每行代表日期和地区。
*每列代表特征/变量,其中目标变量为确诊/死亡/恢复病例数。

特征工程:
天气数据: 我已经使用 python 'pyweatherbit '包提取每个区域坐标(经度,纬度)的历史和预报天气数据。这个包需要一个 API 密匙,每天限制为 500 个调用,所以我运行了几天,然后将数据存储在 CSV 文件(w.csv 和 w_forecast.csv)中,以将其与 Github 数据合并。我已经提取了每天和每个地点的最低和最高温度。(预测限于 14 天,对于更长的时间,我已经做了一个班轮温度预测)。

湖北每日最低和最高温度

下表显示了最高感染地区的平均温度(从 2010 年 1 月 22 日至 2010 年 2 月 17 日)。我们可以看到大部分地区(除了广东)天气相当寒冷,最高平均温度不超过 12-13 度,最低不超过 5 度。

大多数受感染地区的平均温度

疫区数据: 我用人口数据和每天的总确诊病例,计算出每个地区每天的感染率,用百分比表示。这个特性在最终的模型中很重要。

特性列表: 模型中被测试的特性:
1。最低-最高温度。
2。每个地区的感染率。
3。最后一天总计。
4。最近 X 天的变化(x=1,3,7)。
5。过去 X 天的变化率。
6。卡尔曼的预测。
7。地区。
8。人口。
9。确认/死亡/康复—目标变量。

相关矩阵:
在下图中我们可以看到变量之间的相关矩阵。
*不出所料,它们之间有 X 天的变化/速率相关。(所有时变特征)
*卡尔曼和时变变量与目标相关。
*感染率也与目标有较高的相关性。
*最低-最高温度与目标的相关性非常低。(但这并不意味着它们没有帮助)。

所有变量之间的相关矩阵

功能重要性 我已经使用了 python‘H20’包,并为湖北地区运行了一个单独的模型。
下面的结果显示了最重要的特征:
卡尔曼的预测是显著的,只要其他与时间有关的特征。
感染率是的一个重要特征,将在最终模型中使用。

随机森林特征重要性

最终模型 通常在训练 ML 模型时,我们需要将我们的数据分成训练/测试集(75%到25%)。
由于我们处理的是时变非平稳序列,这种方法并不适合。我通过模仿一个每天训练模型并预测第二天的迭代“在线”系统解决了这个问题。
然后,我没有得到真实值的反馈,而是用上一次预测加上一些计算出来的噪声更新了下一个时间戳值,并再次预测。
每天所有的功能都在更新(卡尔曼、感染率、天气预报和最近 X 天的计算)。
在评估了几个模型后,我选择了一个使用 python‘sk learn’包的简单线性回归模型。

结果: 脚本允许我们选择每个地区并得到总确诊、死亡和痊愈病例的预测。这里我展示了湖北和其他主要地区的地图。

湖北确诊 :
预测显示湖北未来确诊病例越来越少,但预计下个月会超过 10 万例确诊病例。

一个月证实了湖北预测

根据预测,下个月湖北将有 3100 人死于新冠肺炎。到目前为止,湖北已有 2156 人死亡(19.02.20)。

一个月死亡预测湖北

康复- 这种正向预测显示了湖北康复人的良好步伐。根据这一预测,下个月将有 8 万多人从冠状病毒中康复。

一个月恢复预测湖北

主要地区: *我们可以看到,在许多地区,下个月的预测显示几乎没有扩散。所有的底线和曲线的斜率不变。

*基于这一预测,大规模爆发已经过去,趋势显示确诊病例越来越少,甚至在顶部地区,如浙江河南安徽广东(图表中的前 4 行)。

  • 钻石船预计会得到越来越多的确认发布——直到船上的人全部撤离。这应该很快就会发生,然后就不会有更多的预测了。(第 5 行浅蓝色)

一个月证实了主要地区预测

死亡:根据大部分地区的预测,我们不会看到死亡病例的增加。主要关注的地区是河南,预计下个月将有近 40 人死亡(今天有 19 人)

主要地区一个月死亡预测

恢复 —预测显示下个月恢复率将增加。越来越多的人将从新冠肺炎被解救出来。

一个月恢复了主要地区预测

热图: 为了呈现预测的另一种可视化,我使用 python 包“gmplot”来显示谷歌地图上的预测。
该地图根据该区域的坐标显示了具有高感染位置的热点。
观测是对每个地区下个月的预测。谷歌的 API 不再是免费的,所以这个版本是有限的,但它确实显示了受感染的位置。

基于下个月预测的受感染位置热图

基于下个月预测的受感染位置热图。不包括湖北

结论

  • 最终模型预测未来 30 天或更长时间内新冠肺炎病毒的趋势。一天的卡尔曼预测非常准确和强大,而更长时间的预测更具挑战性,但提供了未来趋势。
  • 看起来感染的高峰已经过去了,这一趋势的预测显示,确诊人数减少,痊愈人数增加。
  • 在分析结果时,有必要将湖北与其他地区分开。
  • 天气特征不可能是一个重要特征,因为模型从未看到新冠肺炎传播在炎热天气中的表现,但温度确实稍微改善了结果。根据感染最严重地区的平均天气,我们可以假设,一旦这些地区天气变暖(~20 度或更高),确诊病例将会减少。在湖北,这样的天气预计从 4 月中旬开始。
  • 感染率是影响传播的主要特征之一——该模型发现它尤其与湖北钻石公主船相关。该模型无法预测影响感染率的地区之间的人员流动。因此,根据这一数据,预测哪些新的国家将受到影响是不相关的,但一旦它们受到影响,该模型将预测这些新地区的传播。
  • 根据积极的复苏趋势预测、中国政府对湖北的隔离以及中国即将出现的温暖天气****我认为新冠肺炎疫情在三月底会大幅减少。但在此之前,感染者向其他地区的转移可能会改变现状…

就这样,感谢您的阅读。请随时联系我,分享您的想法、问题或反馈。

更新的预测可以在这里找到

使用 KDTree 检测多维数据集中的相似性

原文:https://towardsdatascience.com/using-kdtree-to-detect-similarities-in-a-multidimensional-dataset-4be276dcf616?source=collection_archive---------61-----------------------

找到完美匹配的快速简单的方法

艾莉娜·格鲁布尼亚克在 Unsplash 上的照片

真实生活的例子

大多数数据科学都面向训练、测试、预测范式。未来谁不想猜啊!但在某些情况下,需要其他实现,如无监督分类或在现有数据中发现模式。换句话说,如何利用已经拥有的数据。

我认为与数据科学的其他分支相比,这个方面有点被忽视了,关于它的文献也更少。因此,这一点点贡献的原因。

故事是这样的:我们的一个客户需要一种方法,根据固定数量的参数为一个给定的实体找到相似的商品(邻居)。实际上,该数据集由人力资源专业人士的投票组成,他们可以将多达 5 种技能归于任意数量的世界大学。这意味着人力资源部的爱德华可以投票选择麻省理工学院作为数字化的好学校,牛津大学作为国际化的好学校,巴黎大学作为软技能的好学校。

我准备了数据,输出了一个蜘蛛网图表,客户可以选择任何机构,并与其他机构进行比较,下面是三所随机大学的例子:

三所大学的投票技能

在这一点上,寻找以同样方式投票的大学似乎很有趣,也许是为了比较他们的行动,研究他们做得好和不好的地方。

数据来自 spss 文件,一行由投票决定,根据我们的简报,输出必须很快,因为它被用作后端服务,具有接近实时响应

经过一些研究之后,我认为最好的处理格式应该是一个 KD 树 ,因为它具有多维性,并且处理起来相对容易和快速。我不会详细解释什么是 KD 树,但是你可以参考维基百科的文章。

它完全集成到了 sklearn 模块中,并且非常容易使用,我们将在下面看到。

但首先,让我们做一些准备!

数据准备

作为客户的财产,我们的数据集已被匿名化。大学的名字被拿走了,但是价值观是真实的。

我们将从导入库开始:

import pandas as pd
from sklearn.neighbors import KDTree
  • 熊猫将被用来输入和处理我们的数据。对于类似数据库的处理来说,它非常快速和有用
  • sklearn 代表 scikit-learn ,最著名的数据分析库之一。它用于分类、聚类、回归等等。我们将从最近邻居子库中导入 KDTree

我们已经将 spss 文件转换为 csv 文件,所以我们只需使用 pandas read_csv 方法导入它并显示它的第一行:

df = pd.read_csv("[https://bitbucket.org/zeerobug/material/raw/12301e73580ce6b623a7a6fd601c08549eed1c45/datasets/votes_skills_anon.csv](https://bitbucket.org/zeerobug/material/raw/12301e73580ce6b623a7a6fd601c08549eed1c45/datasets/votes_skills_anon.csv)", index_col=0)
df.head()

数据集结构

每行对应一个投票,其中:

  • SUID 是投票者的唯一 ID
  • univid :机构的唯一标识
  • 响应:投票技能

例如,这意味着用户#538 投票选择“国际性”作为大学#5c9e7a6834cda4f3677e662b 的一项重要技能。

我们的下一步包括按机构和技能分组(响应)。我们使用优秀的 groupby 方法来实现,该方法生成一个 SeriesGroupBy 对象,我们可以用它来计算数据集中相似的(univid,response)对的数量。

skills = df.groupby(["univid", "response"])["response"].count().reset_index(name='value')

我们使用 reset_index 从 count()函数输出的序列中获取一个 DataFrame,并创建包含该计数的“value”列。这是我们的桌子:

按 univid 和响应分组的相同数据集

即使可读性更好,这种格式对我们的目标也没用。很难区分机构,因为行数是任意的(一些技能可能没有被投票),并且许多工具最好使用行值而不是列值。

幸运的是,熊猫提供了一个非常强大的交换行列的工具: pivot_table 。它的论点不言自明,所以我不会进入细节。

univSkills = skills.pivot_table(values="value", columns="response", index=["univid"])
univSkills.head()

pivot_table 之后的值

我们的数据几乎已经准备好进行处理,但是我们仍然有一个问题:要使具有可比性,每一行都必须在相同的范围内,如果我们计算一行中值的总和,各行之间的总和远远不相似:

univSkills.sum(axis=1).head()univid
5c9e345f34cda4f3677e1047    69.0
5c9e349434cda4f3677e109f    51.0
5c9e34a834cda4f3677e10bd    40.0
5c9e34ae34cda4f3677e10c7    66.0
5c9e34d534cda4f3677e1107    70.0

这是因为像哈佛这样的大学比一些偏远和不知名的大学拥有更多的选票。在其他计算中使用这个参数可能会很有趣,但是对于目前的问题,我们需要去掉那个差异。我们通过使用百分比而不是绝对值来遵从。

所以我们必须对每一行求和,然后将每个值除以这个和。这是在一行程序中完成的,然后我们去掉一些 Nan 值来完成数据集的润色。

univSkills = univSkills.div(univSkills.sum(axis=1), axis=0)
univSkills.fillna(0, inplace=True )

我们的数据集现在是干净的,准备好了,值在相同的范围内,我们可以开始玩一些更有趣的处理。

处理数据以查找邻居

因此,我们的算法必须在我们的数据集中检测所有大学中,哪些大学同时具有与我们的 5 个变量最接近的值。我们可以立即想到一个带有嵌套循环的强力算法,它会逐值比较,直到找到每个变量的 5 个最接近的值,但这将远离最佳,对于我们的应用程序来说不够快!

KD 树算法更加有效,它由数据的几何方法组成,通过 n 维空间的后续划分,生成一棵以允许复杂查询快速运行的方式重新组织数据的树。让我们用这个方法生成一棵树:

tree = KDTree(univSkills)

我们的树可以被查询了。第一步是选择一所大学开始,例如索引 9 ( univSkills[9:10])的行,我们想要 5 所最近的大学(k=5)的结果集,应用于我们的树的“查询”函数将返回 2 个 numpy 数组(dist, index)的元组,这将分别是结果的距离索引,从最近到最远排序。

dist, ind = tree.query(univSkills[9:10], k=5)

然后我们可以显示我们的邻居的值:

univSkills.iloc[ind.tolist()[0]]

我们马上注意到值非常接近,我们可以用新的雷达图来确认:

5 大学技能对比

另一个例子

你可以尝试不同的开始行,在大多数情况下,雷达图将保持非常相似的形状。

您也可以使用其他 KDTree 方法变量,您可以在文档中找到它们。让我知道你的实验是否会带来更好的结果。

进一步的实验

我认为这个算法还有很多其他的应用。它可以用在推荐系统约会网站中,以及通常任何依赖于多维矩阵之间的接近度的处理中

关系,例如:通过确定最大距离并将查询函数应用于整个数据集的每一行,我们可以发现看不见的关系,并生成一个类似于 GraphQL 的数据库。

由于它的速度、简单性和有效性, KDTree 也可以在一些简单的情况下作为复杂得多的库的替代,如 TensorFlowPytorch 。我们将在我的 n ext 文章中研究这个问题。

瞧啊!我希望这篇文章对某人有用。你可以在这里找到完整的笔记本。不要犹豫,留下你的评论,给我发电子邮件,或者任何问题。

在谷歌的边缘 TPU 使用 Keras

原文:https://towardsdatascience.com/using-keras-on-googles-edge-tpu-7f53750d952?source=collection_archive---------12-----------------------

在边缘快速部署准确的模型

内森·希普斯在 Unsplash 拍摄的照片

使用 Keras 是快速理解和高效使用深度神经网络模型的好方法。目前,Keras API 不正式支持量化感知训练,因此无法使用该 API 直接生成一个用于 edge 硬件优化(例如,仅使用整数数学)推理的模型。与云相比,边缘上的推断可以提供几个优势,包括减少延迟、降低云成本和改善客户数据隐私。

本文涵盖了以下步骤,因此您可以利用快速高效的 Google Coral Edge TPU 轻松使用 Keras 模型。

  • 选择 Edge TensorFlow 处理单元(TPU)兼容的 Keras 模型。
  • 在自定义数据集上微调模型。
  • 使用训练后量化将微调的 Keras 模型转换为张量流(TF) Lite 模型。
  • 编译用于 Edge TPU 的量化 TF Lite。
  • 在边缘 TPU 上评估量化模型精度。
  • 在边缘 TPU 上部署量化模型。

本文不会深入研究训练深度神经网络、TF Lite 或 Keras APIs,也不会深入研究模型量化背后的理论。您可以通过提供的各种链接来了解这些主题的更多信息。另外,参见皮特·沃顿的优秀博客。这篇文章反映出我主要是一名工程师,比起花大量时间在理论上,我更关心让事情顺利进行。

请注意,我为 smart-zoneminder 项目开发了大量材料,帮助我更好地理解深度学习,因为我在家里解决了一个实际问题——识别一个人是我的家人还是陌生人。我将使用项目中的例子来阐明这些材料,如下所示。

型号选择

根据 Google Coral 文档显示,只有少数 Keras 机型在 Edge TPU 上得到验证——Mobilenet _ v1Mobilenet_v2Inception_v3ResNet50 。我决定使用 Mobilenet_v2、Inception_v3 和 ResNet50 来确认它们在 Edge TPU 上确实工作得很好。我还尝试了 VGG16InceptionResNetV2 ,看看它们在对比中表现如何。这些模型对于我的 smart-zoneminder 应用程序来说是完美的,因为它需要一种方法来分类一个人是我的家庭成员还是陌生人——这是这些卷积神经网络(CNN)模型的理想用途。选择这些模型可以很好解决的问题来使用 edge 硬件,否则当您尝试将另一种类型的网络(可能使用 TF Lite 无法量化的运算符)映射到硬件时,您将处于未知领域。

模型微调

有许多很好的资料向您展示如何在您自己的定制数据集上微调 Keras 模型,例如 Keras 文档smart-zoneminder 。但是,我发现在 Keras 模型的输出中使用 max pooling 将不会针对边 TPU 进行编译,因为它有一个当前无法由 TF Lite 量化的运算符(REDUCE_MAX)。因此,您将需要使用平均池(或无),如下例所示。

使用平均池实例化 Keras 模型

训练后量化

由于 Keras 模型不支持量化感知训练,您需要使用训练后量化,如 TF Lite 文档中所述。在 smart-zoneminder 项目中使用的 Python 训练脚本将在 Keras 模型的最终训练步骤之后运行训练后量化,如下所示。

从 Keras .h5 文件生成量化的 TFLite 模型

keras_to_tflite_quant 模块将 keras 模型(以. h5 格式)量化为 TF Lite 8 位模型。该脚本还可以评估量化模型的准确性,但 TF Lite 模型的推理在英特尔 CPU(我与 Nvidia GPU 一起用于培训)上非常慢,因为当前的 TF Lite 运算符内核针对 ARM 处理器进行了优化(使用 NEON 指令集)。参见此线程了解更多细节,并在下面了解评估边缘 TPU 量化模型的可行方法。

为边缘 TPU 编译

在下一步中,您需要使用 Google Coral 编译器为 Edge TPU 编译量化 TensorFlow Lite 模型。在 smart-zoneminder 项目中使用的 Python train 脚本将运行如下所示的编译器。

为边缘 TPU 编译

评估量化模型的准确性

你的模型会受到量化的影响,所以你应该检查一下它的精度是否在应用程序所能容忍的范围之内。一种快速的方法是使用 TPU 的量化模型对用于开发模型的训练和验证数据集进行推理。为此,我使用了 Python 脚本 evaluate_model.py ,它是在 TPU 边缘运行的智能区域管理器项目的一部分。该脚本的实际评估函数如下所示。参见 Google Coral 文档了解更多关于如何使用 TF Lite 在 edge TPU 上运行推理的信息。

函数在边 TPU 上运行推理

Resnet50 的 evaluate_model.py 的输出如下所示。您可以看到精度为 0.966,非常接近训练和验证数据集精度 0.973 的加权平均值。这个模型的量化损失对我的应用来说是可以接受的。

TPU 评估结果的量化结果

总体而言,我测试的所有模型都具有可接受的量化性能,除了 InceptionResNetV2,其量化性能仅为 0.855,而基线为 0.980。目前,尚不清楚这种量化模型表现不佳的原因。

部署量化模型

在您验证了足够的模型准确性之后,您可以部署它以在您的应用程序中运行推理。smart-zoneminder 为此使用了一个基于 zerorpc 的推理服务器。

结论

通过仔细选择和训练 Edge TPU 兼容的 Keras 模型,您可以快速将推理模型部署到 Edge。这些模型必须首先量化为 8 位,然后针对边缘 TPU 进行编译。量化过程会在模型中引入精度误差,在生产环境中使用之前,应评估其在应用程序中的适用性。边缘推理提供了几个优势,包括减少延迟、降低云成本和改善客户数据隐私。

使用贷款数据训练和构建分类机器学习应用程序

原文:https://towardsdatascience.com/using-loan-data-to-train-and-build-a-classification-machine-learning-app-174c2f1058e3?source=collection_archive---------55-----------------------

构建应用程序以服务分类模型、将应用程序容器化并进行部署的简要指南。

作者:爱德华·克鲁格道格拉斯·富兰克林

照片由 Unsplash 上的纽约公共图书馆拍摄

请点击此处查看该项目中使用的所有代码!

[## edkrueger/python-分类-实验室

在 GitHub 上创建一个帐户,为 edkrueger/python-classification-labs 的开发做出贡献。

github.com](https://github.com/edkrueger/python-classification-labs)

我们使用 GCP 来编码这个项目,它配置了 Docker。如果您有兴趣了解预配置的云虚拟机,请阅读本文开始学习!

[## 使用 GCP 人工智能平台笔记本作为可复制的数据科学环境

使用预配置的云托管虚拟机解决 Python 和 Jupyter 笔记本电脑的再现性问题。

towardsdatascience.com](/using-gcp-ai-platform-notebooks-as-reproducible-data-science-environments-964cba32737)

我们的数据来自一本叫做《ISLR》的书。查看这项工作的机器学习和统计信息!

问题

我们可以想象一家银行想知道谁是安全的贷款接受者,但是我们如何在只知道一些关于某人的事情的情况下判断他是否值得信任呢?

我们的数据集包含代表具有默认状态的个人的行,以及我们将用来预测默认状态的一些其他信息。换句话说,这就是监督学习。

首先,我们将清理数据,使其更适合分类模型。

数据

让我们看看我们的数据。

原始数据

看起来我们有四列和一个索引,包括银行余额、年收入、学生状态和贷款违约状态。可以清理这些数据以更好地适应分类模型,所以让我们做一些更改。

数据清理和 EDA

为了清理我们的数据,我们使用 pandas 和一些 lambda 函数来删除索引,并将我们的 yes/no 字符串更改为 True/False 布尔值。

data_cleaning.ipynb

现在我们的干净数据看起来像这样。

在我们为训练模型设置数据之前,让我们最后看一眼熊猫。

EDA 值和数据类型

这里我们用value_counts()来看看我们有多少真和假。这让我们看到有多少人拖欠贷款,有多少人没有。值得注意的是,虚假比真实多得多,几乎多 30 倍。这意味着我们的数据包含了更多的非违约者的例子,而不是违约者,这是一种阶级不平衡。

在审查我们的模型指标时,记住样本数据中的这种差异是很重要的。我们可以想象一个虚拟模型,它总是猜测错误或无违约,同时它将具有 96.67%的准确率,是一个可怕的模型。要知道我们的模型是否运行良好,我们需要密切关注它何时出错。我们对错误分类感兴趣。

预处理

现在,我们将设置我们的笔记本来运行一些机器学习模型。首先,我们要准备将输入模型的变量。

我们的X或独立变量将是学生身份、银行存款余额和年收入。这些是我们用来预测个人是否可能违约的变量。

X = df[["student", "balance", "income"]].values

个人是否拖欠贷款将是我们的 y或因变量。

y = df["default"].values

因此,我们假设我们的三个自变量与因变量之间存在某种因果关系。换句话说,我们假设银行存款余额、收入和学生身份有助于预测某人是否会拖欠贷款。

建筑模型

使用我们上面设置的 X 和 y 变量,我们可以建立一些模型并分析它们的度量。然而,首先让我们回顾一下当前问题背景下的模型度量精度和召回率。

模型度量:精确度和召回率

我们如何判断哪种模型最有效地解决了这个问题?

精密试图回答以下问题:

实际上,有多少比例的肯定识别是正确的?

精确度是对预测违约的个人和实际违约的个人的度量。

回想一下试图回答以下问题:

正确识别实际阳性的比例是多少?

召回衡量被正确分类的违约个人的比率。

要全面评估一个模型的有效性,你必须同时检查精确度和召回率。不幸的是,精确度和召回率经常处于紧张状态。也就是说,提高精度通常会降低召回率,反之亦然。

请注意,我们想象了一个准确率为 96.67%的虚拟模型,它永远不会说“真”精度和召回允许我们放大模型归类为“真实”的值,以便我们可以看到我们的模型在对我们重要的时候是如何表现的!

每个问题都是不一样的,所以你需要知道对于你的情况,精确和回忆哪个更重要?这将是一个基于价值的决定,你决定哪个成本更高,假阳性或假阴性。

让我们来看看使用 scikit-learn 构建的几个模型。

决策图表

在决策分析中,决策树可以直观、明确地表示决策和决策制定。顾名思义,它使用树状决策模型。

在这里,我们可以看到我们的准确性几乎是我们的虚拟模型,总是说假。这是由于数据集中存在大量错误,即类别不平衡。

当我们观察cv_recallcv_precision时,事情变得更加有趣。这些低分数表明该模型很难识别实际的“真实”值,precision 告诉我们,我们的模型预测“违约”的时间只有三分之一左右是正确的回忆告诉我们,当它确实将一个人归类为“真实”时,它只在大约三分之一的情况下是正确的。不是一个很好的模型,

k-最近邻

k-最近邻(KNN)算法是一种简单的监督机器学习算法,可用于解决分类和回归问题。

我们看到我们的准确性略有提高,但我们再次对cv_recallcv_precision更感兴趣。我们看到这里的精度更高,这意味着我们的模型预测的“真实”值中,有一半是正确的。这意味着,我们的模型认为会违约的人中,有一半真的会违约。

然而,我们也看到召回率的相应下降。这意味着我们的模型只确定了 15%的“真实”值。当我们说“真”的时候,似乎我们更经常是对的,但是我们也忽略了许多“真”的价值。

随机森林

顾名思义,随机森林由大量单独的决策树组成,这些决策树作为一个整体运行。随机森林中的每一棵特定的树都会产生一个类别预测,拥有最多票数的类别将成为我们模型的预测。该模型的一些实现让每棵树提交一个平均值而不是一个投票,这可以提高准确性。

随机森林背后的基本概念简单而强大——群体的智慧。用数据科学的话来说,随机森林模型如此有效的原因是,大量相对不相关的树作为一个委员会运行,将胜过任何单个的组成树。

我们再一次看到我们的准确度略有提高,但是我们再一次对cv_recallcv_precision更感兴趣,与单个 KNN 模型相比,它们在随机森林中都有所提高。我们看到这里的精度稍高,这意味着在我们的模型预测的“真实”值中,有 57%被正确选择。我们看到一个cv_recall远高于 KNN,但略低于一个单一的决策树。尽管如此,我们仍然只能用这个模型识别出大约三分之一的“真实”价值。

在这三个模型中,随机森林具有最佳的整体指标,因此我们将继续推进该模型,并将其构建到一个应用程序中。

保存您的模型

一旦你决定了一个模型,把它保存为一个. joblib,就像下面我们对随机森林模型所做的那样。

dump(rf, "clf.joblib")

现在我们已经将贷款分类器保存为. joblib 文件,该文件将出现在我们的目录中。

构建应用程序

服务于这个模型的应用程序很简单。我们需要将我们的模型导入到应用程序中,接收 POST 请求并返回模型对该 POST 的响应。

这是应用程序代码。

我们可以看到有一条简单的/home路线让我们知道应用程序已经准备好了。我们的/predict路线才是真正起作用的。请注意,它使用了一个POST从帖子中提取变量,然后将这些变量作为X_predict提供给我们的模型。然后,该路径将该模型的响应返回为真或假,并将违约概率返回为浮点数。

一旦我们有了一个可以为我们的模型服务的应用程序,我们需要用 Docker 封装我们的应用程序,这样我们就可以轻松地部署它。

Docker 和我们的 docker 文件

Docker 是将应用程序投入生产的最佳方式。Docker 使用 docker 文件来构建容器。构建的容器存储在 Google Container Registry 中,可以在这里进行部署。Docker 容器可以在本地构建,并将在任何运行 Docker 的系统上运行。

以下是用于此项目的 docker 文件:

Dockerfile 文件

使用 Docker 进行本地测试

请注意,您需要安装 Docker 才能在本地运行。或者,您可以使用已经配置了 Docker 的云平台(参见本文开头链接的文章)。

docker build命令将 docker 文件的每一行视为一个“步骤”,并提供一个终端输出,指示每个步骤何时运行,如上所示。

我们运行命令docker build . -t loan-classifier并看到输出确认,即每个步骤都是按顺序完成的。一旦我们的 docker 容器构建完成,我们就可以使用下面的命令在容器中运行应用程序。

PORT=8000 && docker run -p 80:${PORT} -e PORT=${PORT} loan-classifier

码头运行

看起来我们的容器化应用程序正在本地主机地址上运行。太好了,我们成功建造了码头。现在让我们的应用程序向公众发布。

Docker 图像和谷歌云注册表

GCP 云构建允许您使用 docker 文件中包含的指令远程构建容器。

一旦我们准备好 docker 文件,我们就可以使用 Cloud Build 构建我们的容器映像。

从包含 Dockerfile 文件的目录中运行以下命令:

gcloud builds submit --tag gcr.io/**PROJECT-ID**/**container-name**

注意:用您的 GCP 项目 ID 替换项目 ID,用您的容器名称替换容器名称。您可以通过运行命令gcloud config get-value project来查看您的项目 ID。

该 Docker 图像现在可在 GCP 集装箱注册处或 GCR 访问,并可通过云运行的 URL 访问。

使用 CLI 部署容器映像

如果您更喜欢使用 GUI,请跳过这一部分!

  1. 使用以下命令进行部署:
gcloud run deploy --image gcr.io/**PROJECT-ID**/**container-name** --platform managed

注意:用您的 GCP 项目 ID 替换项目 ID,用您的容器名称替换容器名称。您可以通过运行命令gcloud config get-value project来查看您的项目 ID。

2.将提示您输入服务名称和区域:选择您所选择的服务名称和区域。

3.您将被提示允许未认证的调用:如果您想要公共访问,响应y,并且n 限制对同一 google 项目中的资源的 IP 访问。

4.稍等片刻,直到部署完成。如果成功,命令行会显示服务 URL。

5.通过在 web 浏览器中打开服务 URL 来访问您部署的容器。

使用 GUI 部署容器映像

现在我们已经在 GCR 存储了一个容器映像,我们已经准备好部署我们的应用程序了。访问 GCP 云运行并点击创建服务,确保按要求设置计费。

选择您想服务的地区,并指定一个唯一的服务名称。然后通过分别选择未经身份验证或经过身份验证,在对应用程序的公共访问和私有访问之间进行选择。

现在我们使用上面的 GCR 容器图像 URL。粘贴 URL 或单击选择并使用下拉列表查找。检查高级设置以指定服务器硬件、容器端口和附加命令、最大请求数和扩展行为。

当您准备好构建和部署时,请单击创建!

从 GCR 选择一个容器图像

您将进入 GCP 云运行服务详细信息页面,在此您可以管理服务、查看指标和构建日志。

服务详情

单击 URL 查看您部署的应用程序!

优秀

恭喜你!您刚刚将一个打包在容器中的应用程序部署到云环境中。

您只需为请求处理过程中消耗的 CPU、内存和网络资源付费。也就是说,当你不想付费时,一定要关闭你的服务!

结论

正如我们所看到的,决定一个模型是好是坏有其复杂性和细微差别。请记住,我们已经用简单的数据解决了一个简单的问题。随着问题复杂性的增加,进行 EDA 和分析模型输出变得更加复杂和相关。一旦你有了一个“足够好”的模型,下一步就是为这个模型服务。

快速开发出任何像样的好模型都有巨大的商业和技术价值。拥有人们可以立即使用的东西和部署数据科学家可以稍后调整的软件的价值。部署软件可能是一个挑战,但这是一个越早越好的障碍;尽早部署,经常部署。此外,Docker 等服务简化了部署过程。

我们希望这些内容是有益的;让我们知道你想在软件、开发和机器学习领域了解更多的东西!

使用与熊猫数据框架的逻辑比较

原文:https://towardsdatascience.com/using-logical-comparisons-with-pandas-dataframes-3520eb73ae63?source=collection_archive---------0-----------------------

tPYTHON

标准比较操作符的易于使用的包装器

艺术作品软 _ 艺术作品

逻辑比较到处使用

Pandas 库为您提供了许多不同的方法,您可以将数据帧或系列与其他 Pandas 对象、列表、标量值等进行比较。传统的比较运算符(<, >, <=, >=, ==, !=)可用于将一个数据帧与另一组值进行比较。

但是,您也可以使用包装器来提高逻辑比较操作的灵活性。这些包装器允许您指定用于比较的,因此您可以选择在行或列级别执行比较。此外,如果您正在使用多索引,您可以指定想要使用哪个索引

在本文中,我们将首先快速地看一下与标准操作符的逻辑比较。之后,我们将通过五个不同的例子来说明如何使用这些逻辑比较包装器来处理和更好地理解您的数据。

这篇文章中使用的数据来自雅虎财经。我们将使用特斯拉股票价格数据的子集。如果您想继续,请运行下面的代码。(如果你对我用来获取数据的功能感到好奇,请滚动到最底部,点击第一个链接。)

*import pandas as pd# fixed data so sample data will stay the same
df = pd.read_html("[https://finance.yahoo.com/quote/TSLA/history?period1=1277942400&period2=1594857600&interval=1d&filter=history&frequency=1d](https://finance.yahoo.com/quote/TSLA/history?period1=1277942400&period2=1594857600&interval=1d&filter=history&frequency=1d)")[0]df = df.head(10) # only work with the first 10 points*

来自雅虎财经的特斯拉股票数据

与熊猫的逻辑比较

可供使用的包装器有:

  • eq(相当于== ) —等于
  • ne(相当于!= ) —不等于
  • le(相当于<= ) —小于或等于
  • lt(相当于< ) —小于
  • ge(相当于>= ) —大于或等于
  • gt(相当于> ) —大于

在我们深入包装器之前,让我们快速回顾一下如何在 Pandas 中执行逻辑比较。

使用常规比较运算符,将 DataFrame 列与整数进行比较的基本示例如下所示:

*old = df['Open'] >= 270*

这里,我们要查看“Open”列中的每个值是否大于或等于固定整数“270”。然而,如果你尝试运行这个,首先它不会工作。

你很可能会看到这个:

TypeError: '>=' not supported between instances of 'str' and 'int'

现在注意这一点很重要,因为当您同时使用常规比较操作符和包装器时,您需要确保您实际上能够比较这两个元素。请记住,在您的预处理过程中,不只是针对这些练习,而是在您分析数据的一般情况下:

*df = df.astype({"Open":'float',
                "High":'float',
                "Low":'float',
                "Close*":'float',
                "Adj Close**":'float',
                "Volume":'float'})*

现在,如果您再次运行原始比较,您将得到这个系列:

简单的逻辑比较示例

您可以看到该操作返回了一系列布尔值。如果您检查原始的 DataFrame,您会看到对于每一行的值是否大于或等于(>= ) 270,都应该有相应的“True”或“False”。

现在,让我们深入研究如何使用包装器做同样的事情。

1.比较两列是否不相等

在数据集中,您会看到有一个“Close”列和一个“Adj Close**”列。调整后的收盘价被修改以反映潜在的股息和拆分,而收盘价仅针对拆分进行调整。为了查看这些事件是否已经发生,我们可以做一个基本的测试来查看两列中的值是否不相等。*

为此,我们运行以下命令:

*# is the adj close different from the close?
df['Close Comparison'] = df['Adj Close**'].ne(df['Close*'])*

列不等式比较的结果

在这里,我们所做的只是调用“Adj Close**”列上的.ne()函数,并传递“Close ”,即我们想要比较的列,作为函数的参数。*

如果我们看一看产生的数据框架,您会看到我们已经创建了一个新列“Close Comparison ”,如果两个原始 Close 列不同,它将显示“True ”,如果它们相同,则显示“False”。在这种情况下,您可以看到每行上“Close”和“Adj Close”的值是相同的,因此“Close Comparison”只有“False”值。从技术上讲,这意味着我们可以删除“Adj Close”列,至少对于这个数据子集是这样,因为它只包含“Close”列的重复值。

2.检查一列是否大于另一列

我们经常想看看股票价格在一天结束时是否上涨。一种方法是,如果“收盘”价格高于“开盘价”,则显示“真”值,否则显示“假”值。*

为了实现这一点,我们运行以下命令:

*# is the close greater than the open?
df['Bool Price Increase'] = df['Close*'].gt(df['Open'])*

列大于比较的结果

在这里,我们看到,在 2020 年 7 月的前两周,当天结束时的“收盘”价格比当天开始时的“开盘价”高 4/10 倍。这可能没有那么多信息,因为它是一个很小的样本,但如果你将它扩展到几个月甚至几年的数据,它可以表明股票的整体趋势(上涨或下跌)。*

3.检查列是否大于标量值

到目前为止,我们只是在比较不同的列。还可以使用逻辑运算符将列中的值与标量值(如整数)进行比较。例如,假设每天的交易量大于或等于 1 亿,我们称之为“高交易量”日。

为此,我们运行以下命令:

*# was the volume greater than 100m?
df['High Volume'] = df['Volume'].ge(100000000)*

列和标量大于比较的结果

这次我们只需传递标量值“100000000”,而不是将一列传递给逻辑比较函数。

现在,我们可以看到,在 5/10 天的交易量大于或等于 1 亿。

4.检查列是否大于自身

之前,我们比较了每行中的“Open”和“Close”值是否不同。相反,如果我们将一个列的值与前一个值进行比较,以跟踪一段时间内的增加或减少,那将会很酷。这样做意味着我们可以检查 7 月 15 日的“Close”值是否大于 7 月 14 日的值。

为此,我们运行以下命令:

*# was the close greater than yesterday's close?
df['Close (t-1)'] = df['Close*'].shift(-1)
df['Bool Over Time Increase'] = df['Close*'].gt(df['Close*'].shift(-1))*

列大于比较的结果

为了便于说明,我包含了“Close (t-1)”列,这样您可以直接比较每一行。在实践中,您不需要添加一个全新的列,因为我们所做的只是将“Close”列再次传递给逻辑运算符,但是我们还对它调用了shift(-1)来将所有的值“上移一”。*

这里发生的事情基本上是从指数中减去 1,因此 7 月 14 日的值“向上”移动,这让我们可以将其与 7 月 15 日的真实值进行比较。结果,您可以看到 7/10 日的“Close”值大于前一天的“Close”值。

5.将列与列表进行比较

作为最后一个练习,假设我们开发了一个模型来预测 10 天的股票价格。我们将这些预测存储在一个列表中,然后将每天的“开盘价”和“收盘价”值与列表值进行比较。*

为此,我们运行以下命令:

*# did the open and close price match the predictions?
predictions = [309.2, 303.36, 300, 489, 391, 445, 402.84, 274.32, 410, 223.93]
df2 = df[['Open','Close*']].eq(predictions, axis='index')*

列和列表比较的结果

这里,我们比较了生成的每日股票价格预测列表,并将其与“Close”列进行了比较。为此,我们将“预测”传递到eq()函数中,并设置axis='index'。默认情况下,比较包装器有axis='columns',但是在这种情况下,我们实际上想要处理每列中的每一行。*

这意味着 Pandas 会将列表中的第一个元素“309.2”与第一个值“Open”和“Close”进行比较。然后,它将移动到列表中的第二个值和数据帧的第二个值,依此类推。请记住,列表和数据帧的索引都是从 0 开始的,因此,对于第一个数据帧列值,您将分别查看“308.6”和“309.2”(如果您想要仔细检查结果,请向上滚动)。*

基于这些任意的预测,您可以看到在“Open”列值和预测列表之间没有匹配。“Close”列值和预测列表之间有 4/10 匹配。*

我希望您发现这个关于熊猫使用包装器进行逻辑比较的非常基本的介绍很有用。请记住,只比较可以比较的数据(即不要尝试将字符串与浮点数进行比较),并手动复查结果,以确保您的计算产生了预期的结果。

往前走,比较一下!

***More by me:** - [2 Easy Ways to Get Tables From a Website](/2-easy-ways-to-get-tables-from-a-website-with-pandas-b92fc835e741?source=friends_link&sk=9981ddaf0785a79be893b5a1dd3e03dd)
- [Top 4 Repositories on GitHub to Learn Pandas](/top-4-repositories-on-github-to-learn-pandas-1008cb769f77?source=friends_link&sk=d3acc38062490a86ecb46875342224e6)
- [An Introduction to the Cohort Analysis With Tableau](https://medium.com/swlh/how-to-group-your-users-and-get-actionable-insights-with-a-cohort-analysis-b2b281f82f33?source=friends_link&sk=8b672c3feb79e194804f2a1c33660e19)
- [How to Quickly Create and Unpack Lists with Pandas](/how-to-quickly-create-and-unpack-lists-with-pandas-d0e78e487c75?source=friends_link&sk=32ea67b35fe90382dc719c1c78c5900c)
- [Learning to Forecast With Tableau in 5 Minutes Or Less](/learning-to-forecast-effectively-with-tableau-in-6-minutes-or-less-3d77a55930a0?source=friends_link&sk=9abdfd7533ee9a31ab8a036413450059)*

使用逻辑回归从基础创建二元和多类分类器

原文:https://towardsdatascience.com/using-logistic-regression-to-create-a-binary-and-multiclass-classifier-from-basics-26f5e1e92777?source=collection_archive---------14-----------------------

使用 Python 生成梯度下降优化的二元和多类分类器

照片由乔恩·泰森Unsplash 上拍摄

随着数据科学和机器学习成为工业和学术研究中许多领域不可或缺的一部分,掌握这些技术的基本知识对于识别数据趋势非常有用,尤其是在数据集规模快速增长的情况下。有了实验科学的背景,线性回归看起来总是相当直观,因为我们经常将实验数据与理论模型进行拟合,以提取材料属性。然而,制作一个分类器(根据之前的几个指标预测 A 队是否会击败 B 队),在我做了一些阅读之前,我并没有使用过或者有太多的经验。在本文中,我希望简洁地描述如何使用逻辑回归从头开始创建一个分类器。在实践中,您可能会使用诸如scikit-learntensorflow之类的软件包来完成这项工作,但是理解一些底层的方程和算法对于了解“幕后”发生了什么非常有帮助。

本文中的大部分工作都受到了吴恩达教授的 Coursera 上的机器学习课程的启发。

本文中的所有代码我们都将使用 Python,所以让我们导入我们将需要的所有包:

我们如何进行离散预测?

做离散预测是我们一直在做的事情,可能甚至没有过多考虑如何做。例如,你正在餐馆看菜单——你阅读不同菜单项的描述,记住你最近阅读的食物评论中的一些提示,可能会问服务员几个问题,并对你想点什么做出谨慎的决定。如果你从未在这家餐馆吃过饭,你实际上是在根据你认为你会喜欢的东西做出明智的预测。

为了在我们的预测算法中实现类似的行为,一个很好的起点是选择一个数学函数,它本质上给出一个二进制输出或 0 或 1。为此,我们可以使用逻辑sigmoid 函数,其形式如下:

我们可以用 Python 编写这个函数,并将其绘制如下:

如图所示,在 x = 0 附近,sigmoid 函数从接近 0 的输出快速变为接近 1 的输出。由于输出值关于 y = 0.5 是对称的,我们可以以此作为做出决策的阈值,其中 y ≥ 0.5 输出 1,y < 0.5 outputs 0.

To see this in action, let’s train some data!

产生数据

我们的场景如下——我们正在构建一个简单的电影推荐系统,该系统考虑了 0 到 5 之间的平均用户评分(所有用户)和 0 到 5 之间的平均评论家评分。然后,我们的模型应该基于我们的输入数据生成一个决策边界,以预测当前用户是否会喜欢这部电影,并向他们推荐这部电影。

我们将对 100 部电影进行随机用户和评论家评分:

现在,让我们生成分类—在这种情况下,要么用户喜欢这部电影(1),要么不喜欢(0)。为此,我们将使用以下决策边界函数。我们将该等式的右侧设为 0,因为它定义了逻辑函数何时等于 0.5:

请注意,在真实的数据集中,您不知道决策边界的函数形式是什么。出于本教程的考虑,我们正在定义一个函数,并向它添加一些噪声,以便分类不是“完美的”。

现在,我们可以绘制初始数据,其中橙色圆圈代表用户喜欢的电影,蓝色圆圈代表不喜欢的电影:

决定决策边界的质量

为了确定我们的决策边界有多好,我们需要为错误的预测定义一个惩罚,这将通过创建一个成本函数来实现。我们的预测如下:

P ≥ 0.5 时,我们输出 1,当 P < 0.5 时,我们将输出 0,其中 w₀、w₁和 w₂是我们正在优化的权重。

为了惩罚错误的分类,我们可以利用对数函数,因为 log(1) = 0 和 log(0) → -∞。我们可以用它来创建如下两个罚函数:

我们可以将它可视化,以便更清楚地看到这些罚函数的效果:

利用我们的输出 ( y )将为 0 或 1 的事实,我们可以在一个包含两种情况的表达式中优雅地组合这两个罚函数:

现在,当我们的输出为 1,并且我们预测接近 0(第一项)时,我们会招致一个陡峭的惩罚——类似地,当我们的输出为 0 并且我们预测接近 1(第二项)时,也会发生相同的情况。

我们现在可以把我们的罚函数推广到 m 个训练例子——第 i 个训练例子的标签用( i 表示)。我们还将总成本除以 m 得到平均惩罚(就像线性回归中的均方误差)。这个最终表达式也被称为成本函数。我们将我们的两个特征(用户评分和评论家评分)称为 x₁和 x₂.

最小化成本

为了找到最优决策边界,我们必须最小化这个成本函数,我们可以使用一种叫做 梯度下降 的算法来实现,这种算法本质上做两件事:(1)通过计算成本函数的梯度来找到最大下降的方向,(2)通过沿着这个梯度移动来更新权重值。为了更新权重,我们还提供了一个 学习率【α】,它决定了我们沿着这个梯度移动了多少。在选择学习率时有一个权衡——太小,我们的算法需要太长时间才能收敛;太大,我们的算法实际上会发散。关于梯度下降和学习率的更多信息可以在链接文章中找到。因此,对于每个权重,我们有以下更新规则:

谜题的最后一块是计算上面表达式中的偏导数。我们可以使用微积分中的链式法则将它分解如下:

现在,我们可以把它们放在一起得到下面的梯度表达式和梯度下降更新规则:

我们现在可以初始化我们的变量来最小化成本函数。

初始化变量

上面所有的梯度下降运算都有利于使用矩阵乘法和线性代数。首先,我们如下初始化变量:

我们现在可以如下重写用于权重的成本函数、梯度和更新函数的两个早期表达式,其中上标 T 表示矩阵的转置:

我们的变量可以初始化如下(我们将设置所有的权重等于 0 开始):

既然长长的数学之墙已经过去了,让我们来训练我们的分类器模型吧!

训练我们的模型

我们必须首先为成本函数和梯度下降更新定义函数。以下函数计算成本和梯度值,并返回两者:

当定义梯度下降函数时,我们必须添加一个名为num_iters的额外输入,它告诉算法在返回最终优化权重之前要进行的迭代次数。

现在,在做了所有这些艰苦的工作之后,下面的线训练我们的模型!

J, w_train = gradient_descent(X, y, w, 0.5, 2000)

在真实的情况下,我们会拆分我们的训练数据,以便交叉验证和测试我们的模型,但是出于本教程的目的,我们将使用所有的训练数据。

为了确保我们在每次迭代中降低成本函数的值,我们可以绘制如下图:

现在是关键时刻——我们可以画出我们的决策界限。我们将在原始图上覆盖它的方法是定义一个点网格,然后使用我们训练的权重计算 sigmoid 函数的预测值。然后我们可以画出预测值等于 0.5 的等高线。

Et,瞧!

我们现在可以编写一个函数,根据这个训练好的模型进行预测:

现在,让我们对新的例子做一些预测:

X_new = np.asarray([[1, 3.4, 4.1], [1, 2.5, 1.7], [1, 4.8, 2.3]])
print(predict(X_new, w_train))>>> [[1.]  
     [0.]  
     [1.]]

多类分类

现在我们已经完成了制作二元分类器的所有艰苦工作,将它扩展到更多的类是相当简单的。我们将使用一种称为 一对一分类 的策略,其中我们为每个不同的类训练一个二元分类器,并选择具有 sigmoid 函数返回的最大值的类。

让我们以二进制示例中的模型为基础,这一次用户可以给一部电影打 0 星、1 星或 2 星的分数,我们试图根据用户和评论家的分数来确定决策界限。

首先,我们再次生成数据,并应用两个决策界限:

我们的数据集如下所示,其中蓝色圆圈代表 0 星,橙色圆圈代表 1 星,红色圆圈代表 2 星:

对于我们训练的每个二元分类器,我们需要重新标记数据,以便我们感兴趣的类的输出设置为 1,所有其他标签设置为 0。例如,如果我们有 3 组 A (0)、B (1)和 C (2),我们必须制作三个二元分类器:

(1) A 设置为 1,B 和 C 设置为 0

(2) B 设置为 1,A 和 C 设置为 0

(3) C 设置为 1,A 和 B 设置为 0

我们有一个为每个分类器重新标记数据的函数:

现在,我们进行与前面的二进制分类部分相同的模型训练,但是分三次进行:

这一次绘制决策界限稍微复杂一些。我们必须为每个经过训练的分类器计算网格中每个点的预测值。然后,我们选择每个点的最大预测值,并根据哪个分类器产生了该最大值来分配其各自的类别。我们选择绘制的等高线是 0.5(0 到 1 之间)和 1.5(1 到 2 之间):

经过大量的预期,这里是我们训练有素的决策界限!

最后,我们来做一个预测函数。为此,我们将使用一个小技巧——我们将对每个示例中的每个分类器进行预测。然后,我们将把每组预测作为一个新列追加到预测矩阵中。然后,通过沿着每一行选择具有最大值的列索引,我们自动得到分类器标签!

X_new = np.array([[1, 1, 1], [1, 1, 4.2], [1, 4.5, 2.5]])
print(predict_multi(X_new, [w_class1, w_class2, w_class3]))>>> [[0]  
     [2]  
     [1]]

在那里!一个完全从零开始的多类分类器!

结束语

感谢阅读!这篇文章只是触及了逻辑回归和分类的表面,但是我希望你喜欢它。展示的例子可以在这里找到。我再次感谢吴恩达在 Coursera 上教授的机器学习课程给了我这篇文章很多灵感。

对多维时间序列数据使用 LSTM 自动编码器

原文:https://towardsdatascience.com/using-lstm-autoencoders-on-multidimensional-time-series-data-f5a7a51b29a1?source=collection_archive---------4-----------------------

埃菲社在 Unsplash 上拍摄的照片

实践中的深度学习

演示如何使用 LSTM 自动编码器分析多维时间序列

在本文中,我将展示一个非常有用的模型来理解时间序列数据。我已经将这种方法用于无监督的异常检测,但它也可以用作通过降维进行预测的中间步骤(例如,对潜在嵌入层和完整层进行预测)。

简而言之,这种方法将一个多维序列(想想多个计数的窗口时间序列,来自传感器或点击等)压缩成一个表示该信息的单一向量。有了有效的编码器/解码器,我们可以使用潜在向量作为多层感知器的输入,或者作为更大的多头网络中的另一组特征。

我不打算讨论 LSTMs 或自动编码器的细节。对于这些信息,我强烈推荐以下文章:

[## LSTM 和 GRU 的图解指南:一步一步的解释

嗨,欢迎来到长短期记忆(LSTM)和门控循环单位(GRU)的图解指南。我是迈克尔…

towardsdatascience.com](/illustrated-guide-to-lstms-and-gru-s-a-step-by-step-explanation-44e9eb85bf21) [## 应用深度学习-第 3 部分:自动编码器

概观

towardsdatascience.com](/applied-deep-learning-part-3-autoencoders-1c083af4d798)

当我们从潜在向量中重建原始时间序列时,通常会有一定程度的误差。对于训练有素的编码器/解码器,该误差可以提供很多信息。

对于异常检测,我们会查看误差的大小。 幅度 让我们了解输入时间序列的 不规则性 。我们可以用这个作为识别异常的代理。

数据

我使用了一组我在以前的文章中用过的数据(链接如下):

[## 用 XGBoost 和 Gini 杂质计算特征的重要性

使用 XGBoost 回归识别重要特征

samsachedina.medium.com](https://samsachedina.medium.com/calculating-a-features-importance-with-xgboost-and-gini-impurity-3beb4e003b80)

这些数据是 2020 年 10 月 26 日交易时段的分笔成交点数据。每一个分笔成交点代表一个价格变化,或者是证券的收盘价、买价或卖价。订单簿数据被快照并在每个分笔成交点返回。订单簿可能会“偏离分笔成交点”波动,但只有在产生分笔成交点时才会被记录,这样可以进行更简单的基于时间的分析。

下面是一个记录示例:

我们有一个时间字段、我们的定价字段和“md_fields”,它们代表以不同于当前要价/出价的价格增量卖出(“要价”)或买入(“出价”)的需求。

模型

我将创建一个“堆叠”自动编码器。数据被重新整形,允许我通过超参数调整来优化窗口大小。

查看我如何通过函数“generate_datasets_for_training”返回 train_x、train_y、test_x 和 test_y 的示例

模型从一个编码器开始:首先是输入层。输入图层是 LSTM 图层。接着是另一个较小尺寸的 LSTM 层。然后,我获取从第 2 层返回的序列——然后将它们提供给一个重复向量。重复向量获取单个向量,并以某种方式对其进行整形,以允许其被馈送到我们的解码器网络,该网络与我们的编码器对称。 注意,不一定要对称,但这是标准做法。

我们的最后一层是一个时间分布的密集层,它产生一个类似于我们输入的序列。

最后,我们计算基于原始输入的均方误差损失:由我们的最终层产生的序列和原始输入序列之间的误差。最小化这成为我们网络学习的优化过程。

这是我们最后的网络

请注意,中间层是一个重复向量,它获取 encoder_3(我们的潜在表示)的输出,并在我们的回看窗口中重复它的步数。

如果我们需要(1,4)的潜在表示,我们只需将编码器层压缩成那个形状。要压缩一个时间序列窗口,我们需要做的就是从最后一个编码层提取输出。

看到这个要点的例子,如何建立!

这是将时间序列数据表示为密集潜在表示的基本介绍。

我希望你喜欢这篇文章!

如果你喜欢这个,你可能会喜欢:

[## 约洛夫 4 十分钟后

在 10 分钟内获得视频注释

medium.com](https://medium.com/the-innovation/yolov4-in-10-minutes-715f016bcf42)

使用 LSTMs 预测股票价格

原文:https://towardsdatascience.com/using-lstms-to-predict-stock-prices-a9965dfec2c9?source=collection_archive---------23-----------------------

利用长短期记忆网络来赚钱

面对现实吧。我们是哑巴。

一只被蒙住眼睛的猴子比任何人都能更好地管理投资组合。不,我是认真的。无数研究表明,猴子在股票市场上一直跑赢人类。据称,一只猴子在一个季度内创造的利润比纽约证券交易所的交易员多 8 倍。一只毫无线索的灵长类动物如何打败华尔街所谓的“天才”?答案,狗屎运

人类一直试图衡量和预测股票价格,使用花哨的统计数据和趋势来计算。但事实是,人类无法理解影响股价的不同变量。我们无法综合一家公司的声誉、他们已经宣布和正在进行的项目、业绩记录、过去的股市数据,并将所有这些因素综合起来,以便在股市上做出明智的决策。我们不能考虑一家公司曾经存在的每一个变量和统计数据。但是,电脑可以。更具体地说,神经网络正是为此而设计的。

长短期记忆网络

数学意义上的神经网络是一个可微分函数,它接受输入并计算输出。本质上,它是你的标准 f(x ),但不是只有一个变量“x ”,而是有 1,000,000 个 x 或参数。我们的目标是优化这些参数,因为这样我们就可以输入任何输入并得到想要的输出。简而言之,这些都是神经网络,但是 LSTM 有一些特殊的性质。

别急,我来分解一下

长短期记忆神经网络由 LSTM 单元或细胞组成。这些单元进行特殊的计算,并将它们的输出作为输入传递给下一个单元。简而言之,LSTM 的主要目标是考虑之前传入输出的数据。像时间序列数据或股票市场数据这样的东西依赖于它自己过去的版本,使用 LSTM,它记住过去并试图预测未来。它是这样工作的。

数据是如何传播的

在股票市场数据和通常依赖于过去自身迭代的数据中,不同时间的数据是不同的。在某个时间,一块数据是 x。这被称为时间步长,数据被输入到一个 LSTM 单元分解成相应的时间步长。例如,第三 LSTM 单元采用第三时间步的数据或 X3。

快速批注: t-1 表示最后一个 LSTM 单元格的值, t 是当前 LSTM 单元格的输出。

细胞状态

细胞状态

C 的符号表示 LSTM 的单元状态。单元状态是通过每个单元自身路径传递的值的向量。当前单元可以在其计算中使用该单元状态,或者完全改变单元状态。正因为如此,细胞状态向量充当了神经网络的长期记忆部分。这是因为它与每个 LSTM 单位相互作用,因此在计算下一个单位的产量时可以将每个单位考虑在内。这就是 LSTM 长期记忆的来源。

输入门

输入门

输入门接收最后一个单元的隐藏状态( ht-1) 的输出和带标签的数据输入(Xt),并使用该信息来查看单元状态是否应该改变。它首先将数据输入和隐藏状态相乘,创建一个新的值向量。然后,它取这个向量的 sigmoid 和双曲线正切函数,并将这些结果相乘。如果乘积高于某个经过训练的参数,它就会被添加到细胞状态中,本质上是让细胞状态记住这个重要的信息。如果它没有被添加到单元状态,那么它可以被认为对网络的长期记忆不重要。简而言之,输入门决定了信息对长期而言是否重要。

忘记大门

遗忘之门

遗忘门接收前一时间步(ht-1)的隐藏状态输出和当前时间步(Xt)的数据输入。然后将它们相乘,并对其应用 sigmoid 激活。如果输出高于某个经过训练的参数,则单元状态完全重置为 0,基本上忘记了它的长期记忆。这就是为什么它被称为遗忘之门,因为它有能力忘记到目前为止所学的一切。这有助于时间序列预测,因为一条数据将作为当前趋势的重置,LSTM 需要将这一因素考虑在内。然而,LSTM 人很少能通过遗忘之门。

输出门

输出门

输出门为下一个单元(ht)准备下一个隐藏状态向量。它将最后一个隐藏状态向量和输入数据考虑在内,并对其应用一些函数。这是神经网络的短期记忆方面,因为它将新信息传播到下一个细胞。

请注意所有三个门是如何一起创建单元状态的。在所有单元结束时,细胞状态通过致密层传递,致密层理解所有长期记忆和关于数据的重要信息,以创建下一时间步的预测。一切都导致了这个细胞状态的产生,它是 LSTM 的心脏。训练 LSTM 网络是为了确保长期信息能够持续到最后。现在您对 LSTMs 有了很好的理解,让我们看看我是如何将它们应用于股票市场数据的。

股票市场预测者

*使用 Tensorflow 和 Keras 创建。

数据

这个项目使用的数据是苹果公司过去 5 年的股票价格。它被分解成每个 10 分钟的时间步长。神经网络需要样本来训练,需要有标签的数据,所以数据是以特定的方式输入的。输入是 50 个时间步长,标签(这是神经网络试图预测的)是第 51 个时间步长。神经网络尝试预测下一时间步的价格,并相应地创建数据来训练神经网络。

建筑

惊喜惊喜!这个神经网络我用了很多 LSTMs。每个 LSTM 中有 96 个单元,并将单元状态作为输入返回给下一个 LSTM。最后,它有 2 个密集层,接收 LSTM 层的输出并理解它。最后一个密集层里面有一个节点,表示输出一个数字,下一个时间步的预测值。它有超过一百万个可以优化的参数。脱落层用于确保神经网络不仅仅是记忆数据和输出。我知道,所有这些工作只是为了找到下一个时间步的下一个值。

model = Sequential([layers.LSTM(units=96, return_sequences=True, input_shape=(50,1)),layers.Dropout(0.2),
layers.LSTM(units=96, return_sequences=True),layers.Dropout(0.2),
layers.LSTM(units=96, return_sequences=True),layers.Dropout(0.2),
layers.LSTM(units=96, return_sequences=True),layers.Dropout(0.2),
layers.LSTM(units=96, return_sequences=True),layers.Dropout(0.2),
layers.LSTM(units=96),layers.Dropout(0.2),
layers.Dense(1)
])

培养

使用流行的 Adam 优化器和 MSE 损失函数来训练该模型。损失函数是模型执行得有多差,优化器找到函数的最小值,这是网络的最佳参数。跑了 100 多个纪元,达到亏损< 0.00001 (it performs pretty good).

The Results are In

So these are few examples of stocks it was used to predict. The blue shows the predicted price and the red shows the actual price.

On Randomly Generated Stock Data

Overall, the model works decently well… but that doesn’t mean you should use this on the stock market. The stock market is very volatile and this model only uses past trends of a stock to predict the next.

参考文献

https://colah.github.io/contact.html

https://unsplash.com/photos/Z05GiksmqYU

基于机器学习和人工智能的嘻哈歌曲分析

原文:https://towardsdatascience.com/using-machine-learning-and-ai-to-understand-hip-hop-songs-evolution-e819c2a5172d?source=collection_archive---------26-----------------------

我如何将自然语言应用到嘻哈歌词中,以找到不同的主题

由于我从小就开始听嘻哈音乐,所以我不得不处理与这种音乐类型相关的不同意见。对于一些人来说,所有的说唱歌曲都接近同一个主题,都是相似的。其他人会争论他们的顶级说唱歌手,想证明他们比我强。虽然我想用数据科学来展示我最喜欢的说唱歌手是如何在很大程度上为游戏做出贡献的,但我去年从事这个项目的目的是对抗流行观点,并使用机器学习来找到嘻哈歌曲的主题和类别。

我的数据

我想获得尽可能多的相关数据。我不想把任何有实力的艺术家排除在项目之外。利用美汤,我收集了来自ranker.com的 814 名说唱歌手名单;我很确定我抓住了 1979 年的大多数说唱歌手。我甚至惊讶地发现了沙奎尔·奥尼尔。😃

下一步是收集所有说唱歌手的歌词。我使用了约翰·W·米勒的精彩的歌词天才包来访问天才歌词 API。你可以查看他的回购协议了解更多信息。

因为我有艺术家——我创建了一个功能来搜索他们的歌曲!还有—随用随存!

最后,我必须连接到 Spotify 的 API 来获取关于这首歌的信息(长度、流行度、效价、节奏、可跳性等等。).因为我已经有了歌词和艺术家的名字,所以我创建了一个函数来获取每个组合的音轨 id 和音频特性:音轨、艺术家。我结尾的歌比歌词少;例如,在我做这个项目的时候,所有 Jay-Z 的歌曲在 Spotify 上都不可用。

用于收集 Spotify 音频特征的函数!

我将避免描述清理数据的麻烦。json 到字典,或删除非英文歌曲。经过收集和清理,我只剩下 Spotify 的 55k+歌词和 27k+歌曲功能。

主题建模

我不打算在这篇文章中进行探索性的分析。然而,在编辑所有歌词时,对顶部的词有一个想法将是有趣的。下图清楚地显示了大多数人是如何看待嘻哈歌曲的。很多脏话——我不得不做大量的清理工作,将一些单词归入“F”类;n '或 B '字——一些确实很常见但不一定翻译出嘻哈精髓的世界(女孩 ' / ' homie ' / ' 男孩 ' / ' '在字云里很容易看出来)

我妈是怎么听说唱歌曲的!!!

相反,我将自然语言处理(NLP)应用于歌词,以挑战词云结果。主要目的是在歌曲中寻找不同的主题。我采用了潜在的狄利克雷分配(LDA)。

什么是潜在狄利克雷分配?这是一种自动发现句子包含的主题的方式。这是一个语料库的生成概率模型。更详细地说,LDA 将文档表示为主题的混合的,这些主题以一定的概率吐出单词。

我期待从歌词中找到话题——经过清理和准备。我们必须记住,LDA 将以概率呈现不同的主题,但作为一名数据科学家,我的工作是定义每个主题。最后,每个歌词将根据较高的权重分配给一个主题。

我的 4 个主题的丰富多彩的表现!!

为了从 LDA 的结果中获益,我必须知道每个主题所涉及的内容。自发的方法是检查每个主题下的歌曲,并尝试在他们谈论的内容中找到相似之处。由于歌词的数量,这将会造成时间混乱,并且效率低下,因为这将需要另一个主题建模:)

主题 1 中超过 10k 的歌词—阅读歌词来理解主题不是一个好主意!

为了给出每个主题的名称,我分析了每个主题的前 30 个单词和一个代表性文本。我对 hip hop 帮助的了解(领域专长 yayyyy)——我将 4 个主题命名如下:

话题 0 :情感——感觉与生活

话题 1 :街头生活——牛肉和暴力(有点期待)

话题二:爱情和关系(各自为政

话题三:金钱、毒品和性

回顾-我在 2019 年 10 月熨斗学校科学展上的演示幻灯片

多年来的说唱

当主题建模的结果听起来不错时,我开始分析这些年来主题的演变。令人惊讶的是,在 2004 年前后,与“金钱/毒品/性”相关的歌曲有所增加。

90 年代左右的主要话题是“街头生活/牛肉/暴力”,它被解释为帮派之间或针对警察的明知故犯的争吵,或著名的西海岸对东海岸的对抗。然而,在 90 后,我们发现随着时间的推移,与“钱”相关的话题有了更好的分配。我认为这与音乐产业有关——艺术家必须创作(给歌迷他们想要的)来达到销售。

通过观察其他年度趋势,令人惊讶地发现了对音乐演变的更好描述。

更多作品——更少的单词和更短的歌曲!!!流式宝宝;)

上面的三幅图显示了产量是如何增加的。随着艺人发行更多单曲,新艺人很容易被发现,制作的歌曲数量也在增加。然而,歌曲的长度变得越来越短,歌曲中使用的词也越来越少。在一张专辑中塞进更多短小的歌曲比放一堆长歌更有利可图。

有了在线流媒体,艺术家不需要创作他们需要的热门歌曲。这种模式基于观点或倾听给他们报酬。因此,艺术家最好创作 50 首 2 分钟的歌曲,而不是像 CD 或磁盘时代那样创作 20 首每首 5 分钟的歌曲。我有点夸张,但你明白我的意思。虽然流媒体网站损害了艺术家的利益,因为它从他们的总销售额中扣除了一部分,但艺术家通过这种方式获得了更多的浏览量,并且仍然从订阅、广告和访问中获得版税。

我对这个项目很感兴趣,因为它结合了我的两个爱好:数据和音乐。我很乐意分享我从 EDA 中收集的两种不同类型的数据的更多发现。

想讨论就随便伸手!!!我的用户名是@fabricemesidor everywhere!!!

参考资料:

[## 潜在狄利克雷分配简介

假设你有下面这组句子:我喜欢吃西兰花和香蕉。我吃了一根香蕉和菠菜…

blog.echen.me](https://blog.echen.me/2011/08/22/introduction-to-latent-dirichlet-allocation/) [## 在线音乐流媒体对音乐产业的影响

音乐产业的经济在最近几年已经完全改变了,因为实体拷贝和在线销售…

globaledge.msu.edu](https://globaledge.msu.edu/blog/post/54474/effects-of-online-music-streaming-on-the) [## 是什么让一些 blink-182 歌曲比其他歌曲更受欢迎?第 1 部分-数据科学等。

在我上小学的一年圣诞节,我的隔壁邻居从圣诞老人那里得到了一个游戏方块,他…

jdaytn.com](http://jdaytn.com/posts/download-blink-182-data/)

如何使用机器学习模型直接从雪花中进行预测

原文:https://towardsdatascience.com/using-machine-learning-models-to-make-prediction-directly-from-snowflake-2471b2f71b68?source=collection_archive---------38-----------------------

使用雪花存储过程、Snowpipe、流和任务,允许用户通过 SQL 调用机器学习推理

图片来源: kennyzhang29 来自 Unsplash

通常,我们会面临这样的场景(最近我也是),数据科学家部署的模型按计划运行,无论是每小时一次、每天一次还是每周一次…您都明白这一点。然而,有时需要超出计划的结果来为会议或分析做出决策。

也就是说,有几种方法可以得到超出计划的预测…

获得超出计划的预测

  1. 用户可以使用一个笔记本实例,连接到数据存储,将数据卸载到 S3,引用数据进行预测,并将结果复制回数据存储。
  2. 开发人员可以构建一个模型托管 API,其中用户可以使用数据存储来提取数据,并发布到托管 API 上进行预测。
  3. 构建一个管道,允许用户使用 SQL 调用批处理预测来直接卸载数据。

因此,即使 Data Scientist & Co 可以为其他人实现一个批量预测应用程序,用于计划外的情况,也可以直观地让非技术用户更接近模型本身,并赋予他们从 SQL 运行预测的能力。

弥合在雪花上运行预测和 SQL 之间的差距

受亚马逊 Aurora 机器学习的启发,我花了几天时间思考如何弥合这一差距,并构建了一个架构,允许非技术用户舒适地使用 SQL 执行批量预测。这都是在 Snowflake 中使用存储过程、Snowpipe、流和任务以及 SageMaker 的批量预测作业(Batch Transform)来创建批量推理数据管道。

雪花机器学习-建筑设计

建筑结构图

  1. 用户以所需的格式将数据卸载到 S3,这将触发 Lambda。
  2. 调用 SageMaker 批处理转换作业是为了使用训练好的模型对数据进行批处理预测。
  3. 预测的结果被写回到 S3 桶中
  4. SQS 被设置在 S3 桶上,以将预测结果自动摄取到雪花上
  5. 一旦数据到达雪花,流和任务被调用。

卸载到 S3 —使用存储过程

S3 卸货流程图

创建输入表

为了让用户调用 Batch Transform,用户需要创建一个包含模型数据和强制字段的输入表,predictionid是作业的 uuid,record_seq是到达输入行的唯一标识符,一个空的prediction列是感兴趣的目标。

输入数据:酒店 _ 取消

卸到 S3

call_ml_prediction存储过程接受一个用户定义的作业名和输入表名。调用它会将文件(使用predictionid作为名字)卸载到/input路径的 S3 桶中,并在prediction_status表中创建一个条目。从那里,将调用批处理转换来预测输入的数据。

为了确保不提交多个请求,一次只能运行一个作业。为了简单起见,我还确保只有一个文件被卸载到 S3 上,但是 Batch Transform 可以处理多个输入文件。

预测状态表

预测—使用 SageMaker 批量转换

触发批量转换的流程图

触发 SageMaker 批量转换

一旦数据被卸载到 S3 存储桶/input,Lambda 就会被触发,从而调用 SageMaker 批处理转换来读入输入数据并将推理输出到/sagemaker路径。

如果您熟悉批量转换,您可以根据自己的喜好为输出预测文件设置 input_filter、join 和 output_filter。

批量转换输出

一旦批量转换完成,它就将结果作为/sagemaker路径中的.csv.out输出。另一个 Lambda 被触发,它将文件复制并重命名为.csv/snowflake路径,在那里 SQS 被设置为 Snowpipe 自动摄取。

结果——雪管、流和任务的使用

将数据绘制成雪花的流程图

通过雪管摄入

一旦数据被放到/snowflake路径上,它就通过 Snowpipe 被插入到prediction_result表中。为简单起见,由于 SageMaker 批处理转换保持了预测的顺序,因此行号被用作连接到输入表的标识符。您可以在批处理转换本身中执行后处理步骤。

数据流和触发任务

在 Snowpipe 传送数据后,在prediction_result表上创建了一个流,它将填充prediction_result_stream。这个流,确切地说是system$stream_has_data('prediction_result_stream,将被调度任务populate_prediction_result用来调用存储过程populate_prediction_result来填充hotel_cancellation表上的预测数据,前提是有一个流。唯一标识符predictionid也被设置为任务会话变量。

批量转换的结果

完成工作

在作业结束时,并且在populate_prediction_result完成之后,使用系统任务会话变量,下一个任务update_prediction_status 将预测状态从提交更新为完成。这就结束了整个“使用 SQL 运行批量预测”管道。

更新的预测状态

做得更好

Snowflake 通过 Snowpipe、Streams、Stored Procedure 和 Task 提供了强大的功能,创建了一个可用于不同应用程序的数据管道。当与 SageMaker 结合使用时,用户将能够直接从雪花发送输入,并与预测结果进行交互。

尽管如此,还是有一些愿望清单项目可以改善整体体验,那就是:

  1. 对于雪花:手动触发,或在 Snowpipe 摄取完成后触发任务的能力。这将保证任务向上完成流。
  2. 对于管道:能够从 AWS 端更新雪花的状态,让用户知道批量转换的进度

我希望你觉得这篇文章有用,并喜欢阅读。

关于我

我喜欢写中型文章,并与大家分享我的想法和学习。我的日常工作包括帮助企业构建可扩展的云和数据解决方案,以及尝试新的食物食谱。请随时与我联系,随便聊聊,只要让我知道你来自媒体

李帝努·亚玛

使用机器学习将您的图像转换为 Vaporwave 或其他艺术风格

原文:https://towardsdatascience.com/using-machine-learning-to-convert-your-image-to-vaporwave-or-other-artistic-styles-df6fb9aa60e0?source=collection_archive---------27-----------------------

TL;DR: 这篇文章介绍了一种流行的机器学习算法的机制,这种算法被称为神经风格转移(NST),它能够将你选择的任何图像转换为你最喜欢的艺术风格。该算法是著名的卷积神经网络的直接应用,并且巧妙地将问题框架化为对两个损失项的优化。凭借其简洁的公式,该算法提供了一种简单的方法来实现一个有趣的图像转换器(想想 DeepArt 或 Prisma)。深度学习中的任何主题都是巨大的,本文仅简要介绍 NST 算法。虽然它的续集将处理算法的实现怪癖和一些其他有趣的应用,但现在让我们获得算法背后的一些直觉,并享受玩它的乐趣。

Unsplash 上拍照

问题设置

我们的目标很明确:让一个图像 S 采用另一个图像 t 的风格。在这一点上,这个目标可能听起来有点高,你可能会有一些合理的问题,例如我们如何在神经网络中表示一个图像,以及我们如何量化风格,这些将在下面的部分中得到适当的回答。

数字表示

简单来说,一幅图像被表示为一个张量,可以认为是一个矩阵的推广。例如,大小为 512512 的彩色图像将被表示为大小为 512512*3 的张量(3-D 矩阵),数字 3 来自于任何颜色都可以被编码为 R-G-B 值的元组的事实,每个值的范围从 0 到 255。这个矩阵将在以后用作算法的输入。

卷积神经网络基础

由于该算法建立在卷积神经网络(CNN)架构上,因此事先澄清一些关于它的观点是有帮助的。

CNN 中与我们的任务相关的两个最重要的构件是卷积层和池层。我们首先来看看卷积层的内部工作原理。

那么我们如何从输入层到第一个卷积层呢?让我们看看下图:

在上图中,位于左上角的大小为 333 的子矩阵(为便于说明,此处仅显示了两个维度)将通过一个相同大小的滤波器,该滤波器通过对子矩阵应用卷积运算来转换子矩阵,其结果成为卷积层左上角神经元的激活。

但是什么是过滤器呢?现在你可以把它理解为一种识别描述图像的某些特征的方法:直角、曲率、纹理等。它通过将自身与输入子矩阵进行卷积来实现这一点。要获得更全面的治疗,请遵循维基百科页面中的提示。

将过滤器向右滑动一个像素,我们得到以下结果:

该滤波器将应用于每个 333 子矩阵,我们将有我们的第一个完整的卷积层。

您可以检查一下,如果我们对上述大小为 663 的输入矩阵使用大小为 333 的滤波器,那么得到的卷积层大小将为 441。然而,一般来说,滤波器的数量大于 1,这意味着我们可能要应用几个不同的滤波器,以便将输入矩阵转换为第一个卷积层。想象一下,我们将从 4 个不同的过滤器得到的大小为 441 的矩阵堆叠在彼此的顶部;我们将最终得到一个大小为 444 的卷积层,它又会成为下一层的输入,无论是另一个卷积层还是池化层。注意,在计算机视觉行话中,每个大小为 441 的滤波器输出可以称为一个特征图;因此,这里我们有 4 个特征地图。

池层的机制可以理解为降维。在下图中,池层的作用是将下一层中任何大小为 22 的子矩阵减少到 11。进行这种下采样的流行方法包括取最大值或平均值。

一般来说,CNN 的架构将包括卷积层和池层的交替。典型架构的示例如下:

VGG19 架构,一般用于神经风格转移,此处采用自

神经风格转移(NST)算法

清楚了基本原理之后,让我们开始研究算法的细节。

NST 利用了上面说明的 VGG19 神经网络,不包括右端的三个全连接层,它已经过预训练,可以使用 ImageNet 数据集执行对象识别。VGG19 附带了 PyTorch 和 TensorFlow 等流行的深度学习框架,因此您不需要实际自己实现它。

让我们回顾一下迄今为止我们所掌握的情况。我们有预先训练的 VGG19 CNN,一个图像矩阵 S 被转换成图像矩阵 T 的样式,以及在网络的每个中间层的中间图像矩阵S’(可以将S’的初始值设置为白噪声,或者简单地设置为 S )。

下一步是将整个问题公式化为优化任务。NST 将其分解为两个损失函数之和的最小化,即内容损失和风格损失。让我们开始吧。

乔纳森·福门托在 Unsplash 上的照片

直观上,内容损失量化了我们在某一层的中间图像与内容图像之间的距离。因此在每一层 l ,我们将S’的当前状态表示为 x ,将原始图像表示为 p ,并且进一步我们还有 x 的特征图,表示为 F ,以及 p 的特征图,表示为 P 。因此,层 l 处的内容损失简单地为:

在层 l,我们对 F 和 P 之间的平方误差求和,对所有特征图在 I 上循环,对给定特征图中的所有单元在 j 上循环

为了得到总的内容损失,简单地对所有层的项求和。

现在让我们来看看风格的丧失。这里,风格可以被宽泛地定义为不同特征地图之间的相关性。对于我们的中间图像 x ,让我们定义 G :

这一项表示在层 l 的两个特征图 I 和 j 之间的相关性。求和是在所有特征图单元上进行的。

类似地,我们可以为原始内容图像 p 定义一个矩阵 A 。那么我们在层 l 的风格损失可以定义为:

n 和 M 表示层 l 处的特征地图的数量和任何给定特征地图的大小

对所有层求和,我们得到总风格损失:

在实际操作中,上面的权重项 w 可以设置为所有层都相等:1/(层数),或者你可以根据原论文进行更精细的决策。

最终,总损失函数是内容损失和样式损失的加权和:aL(内容)+ bL(样式)** ,使用您最喜欢的优化器在每一层对 F(i,j)进行最小化。最终的 F 矩阵将是你的结果图像。

结果

PyTorch 提供了一些神经类型转换的示例代码,非常容易理解和实验。有关实施的更多信息,请参考下面的进一步阅读部分。

请注意,在将输入图像和样式图像提供给 PyTorch 实现之前,您需要使它们具有相同的大小。

现在是有趣的部分:作为乔尔乔·德·契里柯的粉丝,我做了以下实验,试图将他的作品变成类似蒸汽波的风格:

左:乔尔乔·德·契里柯。爱情之歌;右图:vaporwave 股票图像

使用左边作为内容输入图像,右边作为样式图像,我们得到如下结果:

同样,德·基里科的另一幅杰作具有与现代插画不同的化学风格:

左:乔尔乔·德·契里柯。一条街的神秘和忧郁;右:盖娜浩史。南部高速公路

这给出了以下结果:

请按照下面的指示探索进一步的神经风格转换,并通过将它们转换成一些意想不到的风格来赋予您的照片或图像新的生命。

进一步阅读

[1]使用卷积神经网络的图像风格转移

[2]https://papers with code . com/paper/a-neural-algorithm-of-artistic-style

【3】使用 PyTorch 的神经转移

使用机器学习来检测欺诈

原文:https://towardsdatascience.com/using-machine-learning-to-detect-fraud-f204910389cf?source=collection_archive---------7-----------------------

马克斯·本德在 Unsplash 上的照片

端到端机器学习项目的开始

内容

介绍

欺诈检测问题

数据

构建框架

介绍

机器学习是人工智能(AI)的一个子集,它为系统提供了自动学习和根据经验进行改进的能力,而无需显式编程。也就是说,我们(人类)有可能向计算机提供大量数据,让计算机学习模式,以便它可以学习如何在面临新的情况时做出决定——当我发现这一见解时,我立即知道世界即将改变。

它揭示了欺诈正在花费全球经济 3.89 万亿,损失在过去十年中上升了 56%。— 克罗英国

作为一名欺诈的受害者,想出办法来防止这种事情再次发生在我身上(以及其他任何人身上),这基本上促使我进入了一个与我习惯的领域完全不同的领域。

欺诈检测问题

在机器学习术语中,诸如欺诈检测问题之类的问题可以被框定为分类问题,其目标是预测离散标签 0 或 1,其中 0 通常暗示交易是非欺诈性的,1 暗示交易似乎是欺诈性的。

因此,这个问题要求从业者建立足够智能的模型,以便能够在给定各种用户交易数据的情况下准确地检测欺诈性和非欺诈性交易,这些数据通常是匿名的,以保护用户隐私。

由于完全依赖基于规则的系统不是最有效的策略,机器学习已经成为许多金融机构解决这一问题的方法。

使这个问题(欺诈检测)如此具有挑战性的是,当我们在现实世界中对其建模时,发生的大多数交易都是真实的交易,只有非常小的一部分是欺诈行为。这意味着我们要处理不平衡数据的问题——我关于过采样和欠采样的帖子是处理这个问题的一种方法。然而,对于这篇文章,我们的主要焦点将是开始我们的机器学习框架来检测欺诈——如果你不熟悉构建自己的框架,你可能想在完成这篇文章之前通读一下结构化机器学习项目

[## 过采样和欠采样

一种不平衡分类技术

towardsdatascience.com](/oversampling-and-undersampling-5e2bbaf56dcf) [## 构建机器学习项目

构建 ML 项目的模板指南

towardsdatascience.com](/structuring-machine-learning-projects-be473775a1b6)

数据

这些数据是由 IEEE 计算智能学会(IEEE-CIS)的研究人员收集的,目的是预测在线交易欺诈的概率,如二进制目标(T0)所示。

:本次比赛的数据部分是从 Kaggle 数据部分复制而来。

数据被分成两个文件identitytransaction,由TransactionID连接。并非所有交易都有相应的身份信息。

分类特征—交易

  • ProductCD
  • card1 - card6
  • addr1addr2
  • P_emaildomain
  • R_emaildomain
  • M1 - M9

分类特征—同一性

  • DeviceType
  • DeviceInfo
  • id_12 - id_38

TransactionDT特性是给定参考日期时间的时间增量(不是实际的时间戳)。

你可以从竞赛主持人的这篇帖子中了解更多数据。

文件

  • train _ {事务,身份}。csv —训练集
  • 测试 _ {事务,身份}。csv —测试集(您必须预测这些观察的isFraud值)
  • sample_submission.csv —格式正确的示例提交文件

构建框架

处理任何机器学习任务的第一步是建立一个可靠的交叉验证策略。

[## 交叉验证

验证机器学习模型的性能

towardsdatascience.com](/cross-validation-c4fae714f1c5)

注意:该框架背后的总体思想正是来自 Abhishek Thakur — Github

当面临不平衡数据问题时,通常采用的方法是使用StratifiedKFold,它以这样一种方式随机分割数据,即我们保持相同的类分布。

我实现了创建折叠作为preprocessing.py的一部分。

这段代码合并来自训练和测试集的身份和事务数据,然后重命名merged_test数据中的列名,因为 id 列使用“-”而不是“_”—这将在我们稍后检查以确保我们在测试中有完全相同的列名时导致问题。接下来,我们将名为kfold的列添加到我们的训练数据中,并根据它所在的文件夹设置索引,然后保存到一个 csv 文件中。

你可能已经注意到我们导入了config,并把它作为各种事物的路径。所有的配置都是另一个脚本中的变量,这样我们就不必在不同的脚本中重复调用这些变量。

在处理机器学习问题时,以允许快速迭代的方式快速构建管道非常重要,因此我们将构建的下一个脚本是我们的model_dispatcher.py,我们称之为分类器,而train.py是我们训练模型的地方。

先说model_dispatcher.py

这里,我们简单地导入了一个逻辑回归随机森林,并创建了一个字典,这样我们就可以通过运行逻辑回归模型的models["logistic_regression"]将算法调用到我们的训练脚本中。

列车脚本如下…

希望你可以阅读代码,但如果你不能,总结一下发生了什么,我们将训练数据设置为列kfold中的值,这些值等于我们通过的测试集的倍数。然后,我们对分类变量进行标签编码,并用 0 填充所有缺失值,然后在逻辑回归模型上训练我们的数据。

我们得到当前折叠的预测,并打印出 ROC_AUC

[## 理解 AUC-ROC 曲线

深入了解 AUC-ROC…

towardsdatascience.com](/comprehension-of-the-auc-roc-curve-e876191280f9)

注意:就目前情况来看,代码不会自己运行,所以当我们运行每个 fold 时,我们必须传递 fold 和 model 的值。

让我们看看我们的逻辑回归模型的输出。

### Logistic Regression # Fold 0 
ROC_AUC_SCORE: 0.7446056326560758# Fold 1
ROC_AUC_SCORE: 0.7476247589462117# Fold 2
ROC_AUC_SCORE: 0.7395710927094167# Fold 3
ROC_AUC_SCORE: 0.7365641912867861# Fold 4
ROC_AUC_SCORE: 0.7115696956435416

这些都是相当不错的结果,但让我们使用更强大的随机森林模型,看看我们是否可以改进。

### Random Forest# Fold 0 
ROC_AUC_SCORE: 0.9280242455299264# Fold 1
ROC_AUC_SCORE: 0.9281600723876517# Fold 2
ROC_AUC_SCORE: 0.9265254015330469# Fold 3
ROC_AUC_SCORE: 0.9224746067992484# Fold 4
ROC_AUC_SCORE: 0.9196977372298685

很明显,随机森林模型产生了更好的结果。让我们向 Kaggle 提交一份最新报告,看看我们在排行榜上的排名。这是最重要的部分,为此我们必须运行inference.py

注意:提交给 Kaggle 的过程超出了本次讨论的范围,因此我将在排行榜上公布评分模型及其表现。

鉴于这一分数被转换为 Kaggle 的私人排行榜(因为这是公共排行榜上的分数),我们在 Kaggle 的私人排行榜上的排名是 3875/6351(前 61%)。虽然,从 Kaggle 的角度来看,这看起来不太好,但在现实世界中,我们可能会根据任务来决定这个分数。

然而,这个项目的目标不是想出最好的模型,而是创建我们自己的 API,我们将在后面的帖子中回来。

为了构建快速迭代的快速管道,我们拥有的代码是可以的,但是如果我们想要部署这个模型,我们必须做大量的清理工作,以便我们遵循软件工程最佳实践

[## 数据科学家应该知道软件工程的最佳实践

成为不可或缺的数据科学家

towardsdatascience.com](/data-scientist-should-know-software-engineering-best-practices-f964ec44cada)

包裹

检测欺诈在现实世界中是一个非常常见且具有挑战性的问题,提高准确性对于防止在商店进行真实交易时客户卡被拒绝的尴尬非常重要。我们已经建立了一个非常简单的方法,使用分类变量的标签编码,用 0 填充所有缺失值,以及一个随机森林,没有任何调整或方法来处理数据中的不平衡,但我们的模型仍然得分很高。为了改进模型,我们可能希望首先查看随机森林模型中的重要特征,并删除不太重要的特征,或者我们可以使用其他更强大的模型,如光梯度增强机器和神经网络

注意:在写这个脚本的时候,模块不是最好的,但是它的格式允许我快速迭代。在未来的工作中,我计划将这个模型作为 API 部署在云服务器上。

让我们继续 LinkedIn 上的对话…

[## Kurtis Pykes -人工智能作家-走向数据科学| LinkedIn

在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有两个工作列在他们的…

www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)

使用机器学习来区分什么是真实的,什么不是

原文:https://towardsdatascience.com/using-machine-learning-to-distinct-whats-real-and-what-is-not-9c1c74f73c8c?source=collection_archive---------42-----------------------

超现实世界中假新闻检测的神经网络方法

照片由 Unsplash 上的 nij wam Swargiary 拍摄

这篇文章是作为 2020 年后变得越来越不现实的想法出现的。这个世界正变得越来越陌生,我们从互联网上的各种来源接收到越来越多的信息,其中一些充其量是不诚实的。知道你读到的是假的还是真正离奇和令人震惊的能力变得越来越难。距离美国大选还有不到一个月的时间,能够从假新闻中辨别出真实是很重要的。

我将尝试使用一种机器学习(ML)技术来将标题分类为虚假与否,并将其与人类进行比较。

人类探讨

我认为我们应该从实验开始。我会用两个标题,一个来自一篇假文章(来自洋葱),另一个来自一篇真正的文章。

答案并不明显,遗憾的是,这并不是唯一的例子。你可以在文末找到答案但是不要宠坏自己。

我决定再做一个小实验。我选了六个头条(三假三真)。然后我上了 Instagram,在我的粉丝中做了一个民意调查。我知道,这个样本不仅仅是有偏见的,因为它只代表了我的朋友,尽管如此,它仍然是一个指标。

人类集体知识的成功率(准确率)为 0.5 。实际上比扔硬币和依靠纯粹的运气好不了多少。

让我们看看“机器”将会如何表现。

机器学习方法

GitHub 的一名用户从两个来源收集数据,一个是“假”新闻(来自《洋葱》),另一个是真实但怪异的新闻。

“假”标题来自《洋葱新闻》。对于那些不知道“洋葱”的人来说,这是一个美国恶搞网站,以讽刺的口吻报道真实和虚构的事件,模仿真实的新闻机构。不是想骗人,是想娱乐人。所以,这不完全是坏的假新闻,我们只是使用这个数据集,因为我们知道我们可以很容易和客观地将它们归类为假的。

真正的标题来自一个名为 r/NotTheOnion 的子编辑,它包含了来自真实世界的各种超现实的标题,用户可能会将其误解和/或希望为洋葱新闻。

该数据集被广泛使用,并且有很多实验在数据上完成,其中一些比我的更准确。

我决定实现一种不同于我在网上作为辅助项目找到的技术,来提高我的 NLP(自然语言处理)技能。

工艺管道

在对数据进行预处理后,我制作了一个 Tf-idF 并训练了一个神经网络来对新闻进行分类。

但是首先让我们对数据集有一些基本的了解。

我们有 24k 个标题,其中 62.5%为非洋葱文章,37.5%为洋葱文章。

让我们更深入地了解这个项目的技术方面。如果你不感兴趣,就直接跳到结果里。

对于该项目,我们将需要以下进口

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_validatefrom keras.wrappers.scikit_learn import KerasClassifier
from keras.layers import  Dropout, Dense
from keras.models import Sequential
from keras import backend as K
import tensorflow as tf
import plotly.graph_objects as goimport matplotlib.pyplot as plt
import plotly.express as px
import numpy as np
import pandas as pd
import nltk
import re
from wordcloud import WordCloudnltk.download('punkt')
nltk.download('stopwords')
ENG_SW = set(nltk.corpus.stopwords.words('english'))

现在我们应该开始数据的预处理。对于这一部分,我们将使用 NLTK 套件。

首先,我们只保留小写字母数字字符,并丢弃特殊字符。然后,我们开始一个过程,在这个过程中,我们对单词进行标记,然后清除无用的单词,最后我们对单词进行词干处理。这是一个标准的 NLP 过程,NLTK 可以很容易地帮助我们完成。

df = pd.read_csv("onion-or-not.csv") def prepossessing(df):df["text"] = df["text"].apply(lambda x : x.lower())df["text"] = df["text"].apply(lambda x :  re.sub('[^a-zA-z0-9\s]','',x))df["text_list"] = df["text"].apply(lambda x : nltk.word_tokenize(x))df["cleaned_list"] = df["text_list"].apply(lambda x: [word for word in x if word not in ENG_SW])df["stemmed_cleaned_list"] = df["cleaned_list"].apply(lambda x : [nltk.PorterStemmer().stem(i) for i in x])df['text_edited'] = df['stemmed_cleaned_list'].apply(lambda x : " ".join(x))return df['text_edited']df_X = prepossessing(df)
df_Y = df['label']

现在我们已经完成了基本的文本预处理,在进行下一步之前,让我们可视化两个标签的单词云。

不是洋葱

洋葱

下一步是分割数据集并生成数据的TF–IDF

X_train, X_test, y_train, y_test = train_test_split(df_X, df_Y, test_size=0.25, random_state=seed)print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape) vectorizer_x = TfidfVectorizer()X_train_tfidf = vectorizer_x.fit_transform(X_train).toarray()
X_test_tfidf = vectorizer_x.transform(X_test).toarray()

在我们有了 tf-idf 之后,我们将继续我们的模型。

数据集很简单,因此我们将构建一个小型的基本模型。

def Build_Model_DNN_Text(shape, optimizer='Adam', nLayers= 1, neurons=16, dropout=0.85):"""Build_Model_DNN_Tex(shape, nClasses,dropout)Build Deep neural networks Model for text classificationShape is input feature space"""model = Sequential()# number of hidden layersmodel.add(Dense(64,input_dim=shape,activation='elu'))model.add(Dropout(dropout))for i in range(0, nLayers):model.add(Dense(neurons,input_dim=neurons,activation='elu'))model.add(Dropout(dropout))model.add(Dense(1, activation='sigmoid'))model.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=['accuracy',tf.keras.metrics.AUC(),tf.keras.metrics.Precision(),tf.keras.metrics.Recall()])return model

然后我们训练我们的模型

model_DNN = Build_Model_DNN_Text(X_train_tfidf.shape[1])history = model_DNN.fit(X_train_tfidf, y_train,validation_data=(X_test_tfidf, y_test),epochs=100,batch_size=4096,verbose=2,callbacks=[tf.keras.callbacks.EarlyStopping(monitor='val_accuracy', mode = "max", patience=10)])

结果

我们训练的结果

模型精度图

模型损失图

模型回忆图

模型精度图

让我们运行我的 Instagram 实验的标题,看看它与人群相比如何。

titles = [{"text" : "Coronavirus homeschooling: 77 percent of parents agree teachers should be paid more after teaching own kids, study says", "label" : 0},{"text" : "Police Department Celebrates Fourth Of July By Using Fireworks For Crowd Control ", "label" : 1},{"text" : "ICE Director: ICE Can't Be Compared To Nazis Since We're Just Following Orders", "label" : 0},{"text" : "Hackers Obtain Data Of 45 Million Target Customers Revealing What They’ve Done In Store Bathrooms", "label" : 1},{"text" : "US Military Could Lose Space Force Trademark to Netflix Series", "label" : 0},{"text" : "Trump Blames China For Acting Too Late In Coordinating U.S. Coronavirus Response", "label" : 1}]exp = pd.DataFrame(titles, columns=['text','label'])exp_x = prepossessing(exp)exp_x_tfidf = vectorizer_x.transform(exp_x).toarray()model_DNN.evaluate(exp_x_tfidf, exp['label'])

我们评价的准确率比人群好 0.83。

结论

我们实现了一个基本模型,得到了一些下降结果,比人类实验更好。如果我们有更大的数据集和更好的技术,我们可以轻松达到 90%以上的准确率。感谢您抽出宝贵的时间,并且我很乐意讨论任何反馈

该项目的 GitHub 可以在这里找到。

页(page 的缩写)S

不是洋葱

[## 特朗普将高尔夫奖杯献给飓风受害者

特朗普总统周日将一座高尔夫奖杯献给了最近席卷德克萨斯州的强大飓风的受害者…

thehill.com](https://thehill.com/homenews/administration/353355-trump-dedicates-golf-trophy-to-puerto-rico-hurricane-victims)

洋葱

[## 众议院指责奥卡西奥-科尔特斯在国会发言时使用性别歧视的诽谤

华盛顿——众议院投票通过了一项谴责这位新生的女议员行为的决议

www.theonion.com](https://www.theonion.com/house-censures-ocasio-cortez-for-using-sexist-slur-on-f-1844498607)

利用机器学习对抗冠状病毒

原文:https://towardsdatascience.com/using-machine-learning-to-fight-the-coronavirus-7543c3d2381a?source=collection_archive---------32-----------------------

机器学习已经成为一项强大的技术,推动了医疗保健、机器人、数据挖掘、网络安全等领域的进步。最近,深度学习技术的出现使得开发更复杂的架构和新的探索领域成为可能——自然语言处理、计算机视觉等。

新型冠状病毒(冠状病毒)疫情最近肆虐全球,已造成超过 100 万病例( 来源 )。运输系统和机场在识别冠状病毒病例时遇到了特别的困难(T5 来源 T7)。几个潜在阳性的个体也可能是无症状的,并且可能表现出很少或没有冠状病毒的症状,尽管是携带者。这些人可能会影响其他人,这些人出现并发症的风险更高,最终可能需要医院护理和通过通气进行重症监护( 来源 )。目标是通过最经济的方法限制病例数量和传染病的传播,因为这将降低医院的处理能力。

解决病毒问题的一个关键方法是在分子水平上分析其复制周期。构建冠状病毒疫苗的过程需要对冠状病毒的复制进行分子抑制。我们从描述新型冠状病毒的复制周期开始。病毒首先与宿主细胞的 ACE2 受体结合。然后病毒进入细胞,并开始将其病毒 RNA 释放到细胞中。这种病毒 RNA 基因组最初被翻译成特殊的 pp1a 蛋白(多聚蛋白 1a)。这些蛋白质然后被蛋白水解成复制酶,基因组的其余部分被转录成亚基因组 mRNAs。这些 mRNAs 然后被翻译成病毒的特定功能蛋白(刺突蛋白、膜蛋白等。).这些功能性蛋白质在能区室中加工后自我组装,并通过胞吐作用释放。这种复制过程使病毒得以迅速传播,并表明它会在患者的呼吸系统和免疫系统中引起许多健康问题。

新型冠状病毒粒子(来源 — CCO 公共领域使用)

如上所述,目前阻止病毒传播的生物医学努力试图通过限制特定酶继续复制过程的能力,在分子水平上抑制病毒。这些努力特别耗时,并且需要对分子进行密集的生化密度泛函理论(DFT)模拟。因此,我们目前只能试图在未来几个月内限制该病毒的传播,因为该病毒的生物分子实验和抑制性研究需要时间来处理。为了了解谁在瞬间感染了病毒并限制感染,我们必须关注有症状的个体,并能够限制其传播,以减少有症状和无症状的新个体的数量。

热成像技术是一种关键的方法,可以用来帮助辨别谁感染了病毒,谁开始出现症状。热成像属于热成像科学领域。这些相机检测红外辐射,并通过不同的颜色表达不同水平的辐射。热辐射与温血动物的体温升高直接相关。下面给出了热成像的一些例子。

人体热成像(来源 — CCO 公共领域使用)

如果患者在感染病毒的早期阶段开始出现发烧的迹象,这将导致体温上升,并随后发烧。在热成像相机上,我们会看到有症状的病毒携带者上半身有更多的辐射(更残忍的红色)。

机器学习作为模式识别的最前沿方法已经变得无处不在,因此应该可以将数据集插入特定的机器学习算法中,并实现对发烧(可能还有病毒)个体的系统分类。目前,在交通、商业和贸易的主要公共中心,在不造成更多感染的情况下为所有人部署检测是非常低效和昂贵的。通过使用远距离相机,利用热成像扫描通过某个区域的个人,我们可以了解谁可能发烧,谁可能没有发烧。这将全部基于实时处理输入数据的机器学习算法。

可能对该问题有用的一些示例算法包括朴素贝叶斯机器学习(概率)、支持向量机、K-最近邻和随机森林模型。有趣的是,我们可以在这种情况下直接应用计算机视觉。计算机视觉是深度学习的一个子集,它利用图像或视频输入,通过深度神经网络提取特征并进行分类。下面给出一个深度卷积神经网络的例子。

卷积神经网络(来源: Sumit Saha

总之,我们可以利用计算机科学和机器学习的力量,朝着恢复疫情之后的世界迈出一步。许多开源代码和数据集已经在网上开发出来,一些研究小组已经开始开发信息学算法来检测冠状病毒。像下面这样的算法使用 CT 扫描数据来模拟感染病毒的风险。(例如。https://www . medrxiv . org/content/10.1101/2020 . 02 . 14 . 20023028 v3

在过去的几年中,医学科学与信息学技术的结合势不可挡,医学成像、基因组学和肿瘤学等领域也因计算机科学的进步而发生了转变。通过信息学成像和分子化学为今天的冠状病毒做准备也将在未来帮助我们。

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

使用机器学习寻找亚马逊的下一个履行中心

原文:https://towardsdatascience.com/using-machine-learning-to-find-amazons-next-fulfillment-center-2a7b44cc4663?source=collection_archive---------92-----------------------

现实世界中的数据科学

假设:巴西。但是具体在哪里呢?

亚马逊应该在哪里建立下一个履行中心?我们可以利用机器学习来帮助回答这个问题。

图片来自谷歌地图

首先,让我们定义业务案例:

  • 履行中心的一个主要目的是实现亚马逊的核心价值主张:快速 delvery。这意味着对于客户来说,位于中心位置非常重要。
  • 据估计,2020 年,亚马逊拥有美国电子商务市场份额的近 44%,第二大竞争对手的份额为 7%。让我们放眼美国之外,发掘尚未开发的全球市场机会。
  • 让我们从现有的亚马逊国家中进行选择,以利用基础设施和法规遵从性。

图片来自维基百科

接下来,让我们探讨亚马逊履行中心与全国人口的比率。比率越小,中心需要担心的客户就越少,交付速度就越快。

2020 年 5 月从维基百科提取的数据

例如,亚马逊美国目前的比例是每 300 万人有 1 个履行中心。这意味着旧金山湾区中心的 1 个履行中心使亚马逊能够在当天完成购物项目并开车送货。

看上面的数据,哪个国际国家的增长潜力最大?巴西似乎是一个不错的选择:它的总中心数量最少,人口最多,每 1.05 亿人有 1 个中心,也是亚马逊最近宣布扩张和感兴趣的地方。

假设这些都是正确的因素,那么巴西到底在哪里呢?我们可以使用机器学习 k-means 聚类来识别潜在位置。从结论开始,这里是潜在的位置。

小圆圈代表巴西所有的 387 个城市。橙色的大圆圈代表 10 个集群的质心,这是潜在的履行中心位置。

下面是获得结果的代码:

# Import libraries
import pandas as pd
from sklearn.cluster import KMeans
import plotly.graph_objects as go# Load and explore data of world cities from [https://simplemaps.com/data/world-cities](https://simplemaps.com/data/world-cities)
data = pd.read_csv('Data/worldcities.csv')
data.head()

# Find how many cities in Brazil
data.loc[data.country == 'Brazil'].count()# Filter data to only have Brazil
brazil = data[data.country.isin(['Brazil'])]
print(brazil.shape)
brazil.head()

# Use k-means to find clusters 
brazil2 = brazil[['lat', 'lng']]
kmeans = KMeans(n_clusters = 10)
kmeans.fit(brazil2)
y_means = kmeans.predict(brazil2)
centers = kmeans.cluster_centers_# Plot on map
fig = go.Figure(data = go.Scattergeo(
    lon = brazil2['lng'],
    lat = brazil2['lat'],
    mode = 'markers',
    marker = dict(
        size = 3,
        opacity = 0.8,
        reversescale = True,
        autocolorscale = True,
        symbol = 'circle',
        line = dict(
            width = 1,
            color = ['rgba(102, 102, 102)']
        ),
        cmin = 0,
        color = y_means,
        colorbar_title = "City Clusters"
    )
))fig.add_trace(go.Scattergeo(
    lon = centers[:,1],
    lat = centers[:,0],
    mode = 'markers',
    marker = dict(
        size = 8,
        line = dict(
            width = 1,
            color = 'orange'
        ),
        color = 'orange',
    )
))fig.update_layout(
    title = "Brazil City Clusters and Centroids ",
    geo = dict(
        scope = "south america",
        showland = True,
    )
)# Print to new page: fig.write_html('first_figure.html', auto_open=True)
fig

小圆圈代表巴西所有的 387 个城市。橙色的大圆圈代表 10 个一组的质心,这是潜在的履行中心位置。

如果我们将聚类数增加到 25,我们会得到这样的结果:

从 plot ly(plotly.com/python/scatter-plots-on-maps)生成的地图图像

为了达到与亚马逊美国相同的履行中心比率,亚马逊巴西将需要 70 个中心。我们可以在聚类中加入其他因素,如人口变量。

# Use k-means to find clusters 
brazil2 = brazil[['lat', 'lng', '**population**']]
kmeans = KMeans(n_clusters = **70**)
kmeans.fit(brazil2)
y_means = kmeans.predict(brazil2)
centers = kmeans.cluster_centers_

我们会得到这个:

接下来的步骤:
从这里开始,我们应该利用领域知识来更好地了解客户人口统计、电子商务采用、基础设施等。这只是一个初步的假设,需要更深入更精细的分析。你可以在这里看到完整的代码:github.com/allenjiang/mona-lisa-ai

图片来自维基百科

使用机器学习提高客户保持率

原文:https://towardsdatascience.com/using-machine-learning-to-improve-customer-retention-ee742087a3fd?source=collection_archive---------28-----------------------

来源: GenHQ

将购物车放入 Instacart

本博客由 Nicki (Nicola) Kornbluth、Daniel Moskowitz、Sanchya Sahay 和 Metika Sikka 为哥伦比亚大学商业分析项目的一门商业分析课程撰写。

当我们在 2020 年 1 月开始这个项目时,在线杂货似乎是一个令人兴奋的研究行业。几十年来,食品杂货业一直保持着年增长率(即使是在经济衰退时期),网上食品杂货的市场份额也在不断增长。此外,由于习惯驱动的消费者行为和无与伦比的数据访问,在线杂货数据似乎是预测分析的一个很好的候选对象。

gifs.com 制作的 GIF

我们一点也不知道,这个行业将会发生前所未有的变化:

虽然下面的发现可能无法解释当前的购物行为,但我们的分析侧重于保留营销策略,这将是在线杂货店大流行后计划的关键。

在疫情之前,杂货商的大部分增长来自家庭支出的增加,而不是新人们的支出。平均而言,大多数家庭每周至少购买一次食品杂货。

这告诉我们,要取得成功,网上杂货店需要他们的客户建立一种习惯,而且要快。虽然收购在零售业中总是一个因素,但在杂货业中,增长依赖于留住你的客户并增加他们的终身价值。

我们从业内最大的参与者之一— Instacart 的角度来探讨这个问题。下面描述的所有分析都利用了*insta cart 在线杂货购物数据集 2017 。”*

insta cart 在线杂货购物数据集 2017 的结构

对数据的初步观察证实了我们的怀疑:许多顾客只下了几笔订单,但忠诚者的尾巴很长。我们希望能帮助长出这条尾巴。

资料来源:2017 年 Instacart 在线杂货购物数据集

我们将精力集中在两个方面:

  • 减少订单之间的时间。通过让顾客更频繁地购物,我们希望增加他们的订单数量,并成为他们的固定购物点。
  • 理解和促进再订购行为。重新排序是习惯的关键标志,所以我们想要培养这种行为。

来源: GIPHY

缩短订单之间的时间

通过减少订单之间的时间,Instacart 可以确保它从用户的杂货预算中获得越来越多的份额。

为此,我们创建了一个模型来预测用户下一次购物的时间。通过找到导致更频繁订单的因素,Instacart 可以鼓励用户采取类似的行为。他们还可以更好地把握营销信息的时机,在顾客与竞争对手购物之前赢回顾客。

车型

我们创建了许多功能来更好地了解用户的购物模式。这些分为三类:最近的订单、购物者类型的“标志”和累积行为。

我们对订单时间的驱动因素感兴趣,所以我们想要一个易于解释的模型。在快速浏览了决策树之后,我们把精力集中在线性回归上。我们的平均误差是 4.29,这意味着我们通常能够在半周内确定用户的下一个订单。

更重要的是,我们学到了一些有趣的见解。例如,购买餐具室商品的用户倾向于在他们的订单之间有更长的时间,而在早上或工作日订购的用户倾向于订购得更快。

建议

基于这些见解,我们建议 Instacart 采取以下措施来提高订单频率:

  • 专注于推动工作日早上的订单
  • 取消前几个订单的最小订单量,以培养习惯
  • 尝试向用户介绍新的部门
  • 在营销材料中突出冲动购买

预测重新排序行为

网上杂货商长期以来一直在宣传方便的信息。不幸的是,这种优势也是他们最大的弱点——客户经常在平台之间切换,选择最有效的平台。这意味着建立品牌忠诚度很难,但却是留住客户的关键。

来源: 广告世界亚马逊设计隐蔽**

在杂货店购物的忠诚度很大程度上是基于习惯,所以促进重新订购是我们努力的一个很好的地方。

为了实现这一目标,我们建立了一个模型,预测用户在下一次购物车中重新订购他们之前购买的任何商品的概率,以便 Instacart 可以更好地服务客户,并在客户生命周期的早期建立忠诚度。

型号

对于这个模型,我们创建了一个约 850 万行的用户-产品级矩阵。我们还开发了一组新的 27 个特性,包括产品级、用户级和用户-产品级。由于数据集的规模很大,为了提高效率,我们转向 Google Cloud 在虚拟机上运行 R。

我们尝试了几种不同的技术来构建这个模型。因为我们的数据集是不平衡的,所以我们将性能与 F1 分数进行了比较。在此基础上,我们的最佳模型是针对不平衡类调整的随机森林和 XGBoost 树。

然而,由于集合模型的结果很难解释,我们在 Log-Lasso 中寻找一些帮助。这些系数向我们展示了每个特征如何直接影响结果。我们从我们的集合模型中用可变的重要性将它分层,以推断驱动和阻止重新排序的特征。**

应用程序

Instacart 可以利用该模型的预测:

  • 产品订阅:关注最有可能再次订购产品的用户,并以较低的价格提供订阅服务,以确保留住客户。
  • 库存管理:根据再订购行为向合作商店提供需求预测。
  • 有针对性的营销:利用这些预测进行营销,向重新订购产品的可能性中高的用户发送促销信息。在营销中加入补充产品。
  • 提高利润:为再订购概率高的人群推荐和推广高利润替代品。

来源: GIPHY

TL;速度三角形定位法(dead reckoning)

对网上杂货店来说,留住顾客一直是关键,但后疫情时代,这将决定他们生意的成败。我们使用多种机器学习工具来帮助 Instacart 通过专注于减少订单之间的时间和培养再订购行为来吸引和留住他们的客户。(或者像我们在这个项目中的口头禅一样,“搅拌黄油,而不是顾客。”)

你可以在我们的 GitHub repo 上看到这个项目的代码和演示:【https://github.com/dsm2173/instaCART

使用机器学习预测新冠肺炎的增长

原文:https://towardsdatascience.com/using-machine-learning-to-model-the-growth-of-covid-19-2f3b0af304bb?source=collection_archive---------27-----------------------

用数据科学对抗冠状病毒

Unsplash 上的融合医学动画拍摄的照片

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

背景

新冠肺炎,由新型冠状病毒病毒引起的疾病,已经用了 2020 年的时间,在六个月内,已经导致美国超过 13 万人死亡。由于过去几个月大多数州宣布封锁迫使许多数据科学家呆在室内,该社区的成员正在寻找为抗击冠状病毒做出贡献的方法。虽然有许多研究正在进行,试图通过预测蛋白质结构或通过非侵入性程序(如 CT 扫描)对冠状病毒的病例进行分类来寻找冠状病毒的疫苗,但还没有许多旨在直接模拟冠状病毒生长的公共模型。

然而,肯定有模型的应用,旨在直接模拟冠状病毒的生长。虽然这些模型可以用来帮助城市制定关于锁定和重新开放某些业务的政策,但它们也可以通过帮助医院预测他们在给定特定数据的情况下第二天所需的资源,对医院极其有益。例如,医院可以根据对冠状病毒病例的本地预测和之前所需床位或医生数量的数据,预测第二天需要的床位或医生数量。

问题陈述

我的目标是开发能够合理预测第二天冠状病毒生长的公共模型。这个目标可以分为两个部分:

  1. 建立一个模型,根据前 n 天的冠状病毒病例数和死亡数预测第二天的冠状病毒病例数。
  2. 建立一个模型,根据过去 k 天的冠状病毒病例数和死亡数预测第二天的冠状病毒死亡数。

虽然在对我的模型进行严格测试后,我知道 n 和 k 应该是相同的值,但当我第一次定义这个问题时,我假设 n 和 k 可以是不同的数字,以便不限制改进我的单个模型的可能性。

数据集

由于冠状病毒的增长一直是公众感兴趣的话题,而不仅仅是数据科学家或利基商业部门,因此不难找到这一话题的数据。我选择使用新冠肺炎数据库下纽约时报 Github 中的美国国家数据,因为它每天更新,而且非常容易下载。它也不包含丢失的值,所以数据预处理很简单。该数据集包含三列,分别是日期、冠状病毒病例总数和自 2020 年 1 月 21 日以来冠状病毒死亡总数。

包含我在这个项目中使用的数据集的存储库可以在这里找到,并且我从该存储库中使用的确切 csv 文件可以在这里找到。

数据预处理

一旦我们下载了我们的数据,制作一些图来理解数据是一个好习惯。通过这个简单的过程,我们可以看到我们的数据是否具有季节性,并为我们提供一些指导,说明对于这个回归问题应该考虑什么模型。让我们来看看全国冠状病毒病例和死亡人数随时间变化的曲线图。

美国冠状病毒病例和死亡时间图。

从上面的图表中我们可以看出,全国冠状病毒病例和死亡总数以不同的速度持续增加,这表明使用自回归模型是合理的。由于两个图形的形状都指示病例和死亡之间的某种关系,因为两个图形具有相似的形状,所以使用病例数和死亡数作为输入来预测下一天的病例数或死亡数是合理的。

既然我们已经决定了我们的模型将使用什么样的输入和输出,那么是时候创建那些输入和输出了。让我们来看看创建这些的代码段。

#df is a dataframe that contains a list of dates, cases, and deaths
#inputs are a list of COVID-19 cases
#outputs are a list of COVID-19 deaths
past = 5
s = (len(inputs) , past  * 2)
betterinputs = np.zeros(s)
betteroutputs = np.zeros(len(inputs))
for i in range(len(inputs) - past):
  temp = np.zeros(past * 2)
  temp[0 : past] = inputs[i : i+ past]
  temp[past:] = outputs[i : i + past]
  betterinputs[i] = temp
  betteroutputs[i] = inputs[i+past] #when predicting cases
  #use betteroutputs[i] = outputs[i+past] when predicting deathsbetterinputs = betterinputs[0:len(df) - past]
betteroutputs = betteroutputs[0:len(df) - past]

一旦我们创建了输入和输出数组,将数据分成训练集和测试集是很重要。这种划分让我们可以测试我们的模型有多好,以及它的泛化能力有多强。让我们看看下面创建这个训练和测试分割的代码。

split = int(0.8*len(betterinputs))
X_train, X_test, y_train, y_test = betterinputs[:split], betterinputs[split:], betteroutputs[:split], betteroutputs[split:]X_train = np.expand_dims(X_train, axis=2)
X_test = np.expand_dims(X_test, axis=2)

模型

基准

既然我们已经有了可以输入到机器学习模型中的输入和输出,现在是时候开发一个基准模型作为健全性检查,以确保我们不会不必要地使事情过于复杂。我使用的基准模型是输出今天的病例数或死亡数,作为明天的病例数或死亡数的预测值。这是一个适合这项任务的基准模型,因为尽管全国冠状病毒死亡和病例的总数很大,但全国冠状病毒死亡和病例的变化相对较小。让我们看看如何创建我们的国家冠状病毒死亡人数和输出的基准模型,并可视化其准确性。

benchmark = []
for i in range(1, len(betteroutputs)):
  benchmark.append(betteroutputs[i])
sum = 0
for i in range(len(betteroutputs) - 1):
  sum += abs(float(betteroutputs[i]) - float(benchmark[i]))
print(sum / float(len(benchmark)))from matplotlib import pyplot as plt
plt.semilogy(benchmark, label = "benchmark")
plt.semilogy(betteroutputs , label = "betteroutputs")
plt.semilogx(inputs, label = "cases")
plt.title('model accuracy')
plt.ylabel('deaths')
plt.xlabel('cases')
plt.legend()

此图像表示上面代码段的输出。

从上图中我们可以看到,我们的简单基准模型预测的平均死亡人数非常接近病例总数的真实值。在超过 100,000 例总死亡的范围内,804.5 例死亡的平均值表明误差小于 0.8%,这通常被认为是非常好的。全国冠状病毒病例数的同一基准模型平均约为 40,000 例,这表明误差不到 2%。虽然应该有一种方法来开发更好地模拟新冠肺炎增长的模型,但这些基准模型可以用来消除其他更多参数和复杂的体系结构。

里脊回归

虽然我试图使用 Tensorflow 的序列模型和默认的内核正则化和激活函数“relu”来创建几个 MLP,以模拟全国冠状病毒死亡和病例的数量,但我的模型在大多数情况下的表现都比基准差。当我减少 MLP 的层数并最终达到一个单一的密集层时,我的模型的表现仍然比基准测试差。正是在这一点上,当我意识到 L2 正则化可能是非常有益的,我把我的模型结构转换为岭回归。L2 正则化,也称为岭惩罚,强制机器学习模型中的参数较小,因为随着参数增加,损失增加,这种正则化通常用于降低模型过拟合的可能性。一旦我更改了我的模型以包含这种正则化,我的模型的表现明显优于基准模型,尤其是在案例预测方面。让我们看看我的两个模型的训练代码。

from sklearn.linear_model import RidgeCV
model = RidgeCV(cv=2)
model.fit(X_train, y_train)

既然我的模型已经适合训练数据,现在是时候预测全国冠状病毒病例和死亡人数的测试数据。我选择通过计算冠状病毒病例和死亡的平均数来衡量我的模型的误差。下面是预测全国冠状病毒病例和死亡的测试数据的代码段,根据上述指标计算误差,并绘制预测值和实际值的比较图。

predict = model.predict(X_test)
print("{}".format(np.linalg.norm(predict - y_test, 1)/len(y_test)))plt.title("Model Accuracy")
plt.plot( days, predict, label = "predicitons")
plt.plot( days, y_test,  label = "real values")
plt.xlabel("Days after First United States Coronavirus Case")
plt.ylabel("National Cases of Coronavirus")
plt.legend()

这是冠状病毒病例预测器的上述代码段的输出。

正如我们从上述代码段的输出中看到的,案例的模型预测平均只偏离真实值 2,869,这表明误差不到 0.2%,比我们的案例基准精确 10 倍,并且被普遍认为是非常好的。同一个模型输出的实际死亡人数平均误差为 489 人,误差约为 0.4%,几乎是我们死亡人数基准的两倍。

未来预测

既然我们已经使用该模型之前未见过的输入预测了冠状病毒死亡和病例,并且知道该模型可能基于其对测试数据的预测的误差而具有潜力,那么最终是时候为未来数据创建预测了。让我们看看创建输入数据的代码和对明天全国冠状病毒病例或死亡的预测。我还包括输出日期,以提供我预测结果的参考。

from datetime import date
size = (2 , past  * 2)
finalInput = np.zeros(size)
temp = np.zeros(past * 2)
temp[:past] = inputs[-past:]
temp[past:] = outputs[-past:]
finalInput[0] = temp
finalInput[1] = tempfuturePrediction = model.predict(finalInput)
futurePrediction = futurePrediction[0]
print("Prediction for tomorrow's national coronavirus deaths : " + str(int(futurePrediction)))print("Today's date : " + str(date.today()))

我希望我的模型能很好地预测明天!

注意:虽然我的模型有办法提前一天以上预测全国冠状病毒病例和死亡人数(通过更改输入标签或使用第二天死亡人数和病例数的预测作为全国冠状病毒死亡人数或病例数的第二天模型的输入),但我的模型只打算提前一天预测,这样效果最好。

模型缺陷和后续步骤

虽然我的模型目前对第二天冠状病毒病例和死亡人数的预测相当好(从其测试准确性可以看出),但我的模型存在一些缺陷。首先,我的模型具有线性系数,因此很自然,我的模型对当前的线性数据预测得非常好。然而,如果全国冠状病毒病例和死亡人数没有继续呈现线性关系,我的模型可能不会那么准确,而这正是最需要像我这样的模型的时候。然而,虽然我不确定如何使用相同的架构来极大地改进这个模型,但我相信我可以尝试一些其他方法来改进我的模型:

  1. 我想测试使用 L2 核正则化的 MLP 是否比我的岭回归模型表现得更好;该模型还能够预测冠状病毒病例和死亡的非线性关系。
  2. 我想在另一个疫情上使用迁移学习开发一个模型,并将其与我的岭回归进行比较;这也将解决我当前模型只有线性系数的问题。该模型在预测未来多天的死亡和病例方面也应该更加准确,并且还可以用于模拟冠状病毒病例和死亡总数的下降。

虽然我试图开发这些模型来与我的原始模型进行比较,但我希望我现有的模型可以用作当地医疗保健系统和决策者的 24 小时规划工具。虽然我使用国家数据创建了我当前的模型,但我相信它可以很容易地使用县或州的数据进行调整,以满足某个机构的特定需求。

最后…

谢谢你一路看完博客!我很乐意听到任何关于我的分析或项目的评论。您可以在我的 github 上找到并下载我的项目的完整笔记本。

欢迎在下面留言,或者通过我的电子邮件 anaiysomalwargmail dot com 联系我。

使用机器学习个性化用户体验

原文:https://towardsdatascience.com/using-machine-learning-to-personalize-user-experience-f5b6abd65602?source=collection_archive---------21-----------------------

如何在 TensorFlow 2 中使用神经网络预测客户意图

图片由斯蒂芬·道森拍摄

电子商务网站,比如有很多用户的店铺和平台,都是为了满足客户的需求而设计的。通常,网站对每个客户的行为都是一样的。然而,这种“一刀切”的方法并不总能满足所有情况的需要。了解顾客的意图有助于改善旅程,例如走捷径或提供建议,并使之成为更好的整体体验。本文展示了如何使用现有的客户行为数据来创建一个能够预测意图的机器学习模型。

数据保密

我个人不喜欢像谷歌和脸书这样的广告技术公司大量关注在线活动。尽管如此,我认为只要数据不被共享或链接到外部服务,个人网站可以在不侵犯隐私的情况下使用个性化技术。无论这些数据是用于改善客户体验,还是所有活动都在互联网上被追踪,以从广告中获取利润,这都有所不同。此外,任何个性化都应该是选择退出。

客户的数据之旅

通常,用户在网站上的意图可以通过查看他们过去的交互来理解。具体来说,这意味着用户留下了一系列关于其页面浏览和交互历史的事件。事件可以是用户进行搜索查询、调出文章页面或收到电子邮件。这些数据构成了使用以下技术的基础。因此,第一步是收集或提取这些数据。通常,原始数据已经存储在 web 服务器或数据库中,然后需要对其进行提炼才能使用。

举例:

三种不同的用户事件流

这张图片代表了用户到达网站的三个不同的旅程。在这种情况下,这是一个简单的网上商店,对于这个例子来说,这是一个非常简单的旅程。用户 1 可能正在寻找特定的产品,而用户 2 可能正在浏览页面,而用户 3 刚刚购买了一些东西。从一个简单的意图开始,我们希望预测用户是否会购买。

培训用数据

第一步是为每个事件分配一个事件 id,将一些相似的事件聚集到一个事件 id 中也是有用的。这可以手动完成,或者用 sci-kit learn 中的标签编码器完成。最好从 1 开始作为第一个 id,因为 0 用于填充。

我们从每个事件都是一个数字开始,我们的数据只是一系列数字。我们的分类器的输入必须有固定的大小,这意味着每个事件序列的长度必须相等。为此,我们将数据填充到预定义的长度。默认情况下,pad_sequences 用零填充缺失的事件,并在序列开始之前填充它们。如果序列比所需长度长,它会截断序列的开头。结果就是我们的 x。

import numpy as np
import tensorflow as tfnum_events = 8 # example
seq_len = 10 # example

events = [
     [1, 2, 1, 2, 1], # user1 
     [3, 4, 2, 4, 1], # user2
     [1, 5, 6, 7]] # user3

x = tf.keras.preprocessing.sequence.pad_sequences(events, seq_len)

现在我们需要找到您的 y。这高度依赖于用例,但是在这个例子中,我们将使用来自另一个系统的信息,它告诉我们客户购买了一些东西。通常这也只是一个来自上面的事件。用户 3 买了东西,所以他的目标标签是 1。

y = [0, 0, 1]
------------------
(x, y)
 Output:
(array([[0, 0, 0, 0, 0, 1, 2, 1, 2, 1],
        [0, 0, 0, 0, 0, 3, 4, 2, 4, 1],
        [0, 0, 0, 0, 0, 0, 1, 5, 6, 7]]), [0, 0, 1])

请注意,在实际应用中,唯一事件的数量可能有数千个,整个事件流的长度通常有数百个。对于每个用户和会话,点击流中的事件数量可能会有很大不同。总有一天,我们必须裁员。确切的数字取决于数据,但 90%应该是一个很好的起点。

时间序列数据中的窗口

在预测意图的情况下,将数据划分为时间窗口是很重要的。一个窗口(X)表示时间 t0 之前的点击数据,第二个窗口表示 t0 之后的数据,这是我们预期目标事件 Y 发生的地方。与下图相比,点击数据不是一个连续的值,但窗口方法的思想是在数据中移动窗口,这也为一个用户创建了许多序列。例如,我们可以使用 6 个小时的窗口来预测客户是否会在接下来的 2 个小时内购买商品,通过浏览完整的每日数据,我们可以获得几个序列(X 和 Y)。

用于事件流建模的滑动窗口
【优步用神经网络进行时间序列极端事件预测。

然而,最重要的事情是确定,你的事件不是自我实现的预言。如果包含了一个事件,这个事件泄露了标签,那么你的模型就不是很有用(例如,包含购买预测的支付按钮点击)。没有排除哪些事件的通用规则,但是如果您的分类器在这样的任务上表现非常好,您可能有一个泄漏的特征。意图预测永远不会给你高精度的模型,因为事件数据通常不够干净和具体。

系统模型化

现在是时候将数据输入人工神经网络了。每个事件在神经网络中都有一个内部的表示,称为嵌入。这种表示在训练时由网络学习。网络学习建立一个嵌入空间,其中每个事件基于其与其他事件的相似性来定位。使用这样的表示使事件具有可比性(见单词嵌入)。此外,我们可以处理许多独特的事件,而不必处理高维向量。不同事件的数量与 NLP 中的词汇大小相对应。

在完成从事件 id 到嵌入表示的转换(嵌入层)之后,序列必须以某种方式减少到单个向量。LSTMs 是这类任务的标准方法。以下示例中的附加遮罩层从序列中移除了零。

在这个例子中,我们生成 1000 个随机示例序列,这显然会收敛到 50%的准确度,然而它显示了潜在的想法。

import randomnum_events = 1000
seq_len = 100y = np.random.choice(2, 1000, replace=True)
x = np.random.randint(num_events, size=(1000, seq_len))net_in = tf.keras.layers.Input(shape=(seq_len,))
emb = tf.keras.layers.Embedding(num_events, 8, input_length=seq_len, mask_zero=True, input_shape=(num_events,))(net_in)
mask = tf.keras.layers.Masking(mask_value=0)(emb)
lstm = tf.keras.layers.LSTM(64)(mask)
dense = tf.keras.layers.Dense(1, activation='sigmoid')(lstm)
model = tf.keras.Model(net_in, dense)
model.compile('adam', 'binary_crossentropy', metrics=['acc'])
model.summary()history = model.fit(x, y, epochs = 50, validation_split=0.2)

用卷积替换 LSTMs

近年来,卷积层在序列分类任务上也表现出良好的性能。这个想法就是用一个 1D 卷积来遍历这个序列。对于卷积网络,我们有几个“并行”卷积连接。在 Keras 中,我们可以将这种卷积序列分类应用于该模型:

import randomnum_events = 1000
seq_len = 100y = np.random.choice(2, 1000, replace=True)  
x = np.random.randint(num_events, size=(1000, seq_len))net_in = tf.keras.layers.Input(shape=(seq_len,))
emb = tf.keras.layers.Embedding(num_events, 8, input_length=seq_len, mask_zero=False, input_shape=(num_events,))(net_in)
c1 = tf.keras.layers.Conv1D(256, 3)(emb)
p1 = tf.keras.layers.GlobalMaxPooling1D()(c1)
c2 = tf.keras.layers.Conv1D(128, 7)(emb)
p2 = tf.keras.layers.GlobalMaxPooling1D()(c2)
c3 = tf.keras.layers.Conv1D(64, 11)(emb)
p3 = tf.keras.layers.GlobalMaxPooling1D()(c3)
c4 = tf.keras.layers.Conv1D(64, 15)(emb)
p4 = tf.keras.layers.GlobalAveragePooling1D()(c4)
c5 = tf.keras.layers.Conv1D(64, 19)(emb)
p5 = tf.keras.layers.GlobalAveragePooling1D()(c5)
c = tf.keras.layers.concatenate([p1, p2, p3, p4, p5])
bn = tf.keras.layers.BatchNormalization()(c)
dense = tf.keras.layers.Dense(128, activation='relu')(bn)
out = tf.keras.layers.Dense(1, activation='sigmoid')(dense)model = tf.keras.Model(net_in, out)
model.compile('adam', 'binary_crossentropy', metrics=['acc'])
model.summary()history = model.fit(x, y, epochs = 50, validation_split=0.2)

真实数据的结果

我使用的数据集是大约 1300 万个事件流,通过滑动窗口方法导出,类不平衡大约是 1:100。不幸的是,我不能分享数据集,只能分享结果。

不过,根据这些数据,可以将 LSTMs 与 Conv1D 架构进行比较。事实证明,CNN 的方法在许多方面都优于 LSTMS。首先,卷积的计算速度更快,因此它的训练速度更快。LSTMs 对超参数更敏感,因此,模型通过卷积变得更稳健,此外,甚至精度也略有增加。在下面显示的分类器的特征中,在精确度/召回率上有显著的差异,在 ROC 曲线上有轻微的差异。因此,我建议使用 CNN 的方法。

实验的 AUC 和精确召回曲线

小费和技巧

在现实中,数据并不完美,我们经常会有重复的事件、爬虫和机器人或其他噪音。在开始对序列进行分类之前,请确保您的数据是正确的。过滤掉离群值。合并重复事件,并按有意义的不活动时间分割用户的会话。你也可以在模型中引入时间(例如,在嵌入向量的顶部放置类似“自上次事件以来的时间”的东西)。不仅可以使用最大序列长度,还可以使用最小序列长度。如果目标事件非常罕见,负序列的欠采样是一种选择。

本帖首发此处

使用机器学习来预测车祸

原文:https://towardsdatascience.com/using-machine-learning-to-predict-car-accidents-44664c79c942?source=collection_archive---------14-----------------------

来源:朱利安·刘

一个用例

R 道路交通事故在每年报告的严重伤害事故中占有相当大的比例。然而,确定导致此类事件的具体条件往往具有挑战性,这使得地方执法部门更难以应对道路事故的数量和严重程度。我们都知道,车辆的某些特性和周围环境起着关键作用(发动机容量、路况等。).然而,许多问题仍然悬而未决。这些因素中哪些是主导因素?与司机的技术相比,有多少是外部因素造成的?

我们利用机器学习和英国的道路事故数据库来澄清这些问题,并具体提供对两个主要领域的影响:

  1. 首先,我们开发了一个风险评分,该评分仅基于从个人和车辆数据中收集的信息,量化了驾驶员发生致命/严重事故的可能性。该分数可用于影响驾驶规则和规定,并告知驾驶员增加其事故风险的因素。
  2. 其次,我们分析了情境信息(如道路类型、天气状况等。)来估计事故的严重程度。这些见解将有助于政府更好地理解事故的根源,并采取措施减少事故。

数据

我们使用英国交通部的 22 万+事故报告,涵盖 2018 年。对于每份报告,我们都有在事故现场收集的信息,包括:

  • 伤亡特征(如性别年龄所属地区类型)
  • 情境变量(如天气道路类型光照条件)
  • 事故描述符(例如严重性警察在场)
  • 车辆描述符(如年龄动力类型型号)

总的来说,运输部提供的数据可以分为驾驶员信息和外部信息(例如事故位置光线条件),驾驶员信息可以进一步细分为车辆和个人数据。

驾驶员得分

为了了解驾驶员风险因素,我们使用每位驾驶员的独特特征创建了一个驾驶员得分。每个司机都可以输入信息,包括他们的年龄和车辆类型,以获取描述他们发生严重事故风险的值。此外,该模型能够告知驾驶员有关其风险的主要因素。

例如,对于一些司机来说,风险的主要原因可能是拥有一辆旧车,而对于其他人来说,可能是他们生活在农村地区,那里的道路条件通常很差。有了这些信息,个别司机可以做出更明智的决策,例如,购买风险较低的车辆。

为了开发一个估算该分数的模型,我们只关注事前特征,这些特征在事故发生之前是已知的。如果观察到的事故严重性较低或没有造成人员伤亡,我们将目标变量定义为 0,如果事故造成严重或致命后果,我们将目标变量定义为 1(有关如何制定风险评分的更多信息,请查看本文)。我们针对驾驶员和车辆特征训练了多个模型,以便能够比较它们的性能。

使用的模型有:

模型性能的比较。由于类别分布严重不平衡,我们使用 ROC 曲线下面积(AUC)来比较模型的性能。

从上表可以看出,最优分类树实现了最高的样本外性能。此外,与 Random Forest 和 XGBoost 不同,oct 提供了近乎完全的可解释性,因为它不是一种集合方法。在下图中,我们可以看到 OCT 决策树的一个分支,它提供了合理的标准,并且与人类的直觉预期相似。在这个例子中,确实,模型显示如果事故涉及发动机容量高于 200cc 的摩托车,并且车辆的车龄超过 20 年,那么事故很可能是严重的。

决策树的最高分支之一。预测 1 表示致命事故,0 表示非致命事故。

通过预测发生严重事故的概率,我们可以将该概率用作风险得分,该得分将成为驾驶员得分。通过这个分数,我们能够突出风险较高和风险较低的驱动因素。通过使用高度可解释的模型,我们可以通过检查决策树和变量重要性来理解驱动大部分得分的特征。

描述统计学

然而,驾驶员风险只是故事的一部分。然后,我们继续分析与事故相关的描述性统计数据。特别是,我们使用了事故发生时的天气状况、道路上的照明状况和道路状况本身等事后信息,以更好地了解英国各地发生事故的司机。通过这样做,我们不仅能够了解驾驶员如何降低风险(部分使用他们的驾驶员得分),还能够了解外部因素如何发挥作用。

通过了解驱动事故风险的外部因素,政府可以通过首先瞄准事故的主要驱动因素来优先考虑支出。例如,如果我们发现照明条件比道路条件本身更重要,交通部可以分配其有限的预算,优先考虑照明条件,然后是道路质量。

我们使用了相同的目标变量,但这次不是在预测设置中,也使用了不同的模型。与上一节类似,我们在使用 5 重交叉验证训练模型时进行了分层采样:

逻辑回归

  • 套索正则化器,超参数网格在 1.0 和 3.0 之间,步长为 0.1。

手推车

  • 最小样本分割在 3 和 11 之间变化
  • 最小样本叶数在 5 和 13 之间
  • 每次分割的最大特征数“无”、“sqrt”或“log2”。

随机森林

  • 自举等于“真”
  • 最大特性或者“sqrt”或者“log2”
  • 最小样本叶数5 或 10
  • 树木数量400、800 或 1000。

XGBoost

  • 学习率在 0.001,0.01 和 0.1
  • 树木数量分别为 2500、2000 和 1500 棵
  • 最小样本叶数在 4、8 或 12。

在训练模型之后,我们选择使用梯度增强分类器,其样本外 AUC 为 0.72,准确度为 0.87,这实现了最佳性能。分析决策树和从中得出的见解,我们可以突出一些关键方面。首先,从下图可以看出,光事故因一天中的时间不同而不同,在高峰时段发生的频率更高。不同的是,无论一天中的哪个小时,严重或致命事故的分布都比较均匀。

全天事故严重程度直方图。

此外,我们报告了英国每个警察辖区的严重事故与轻微事故的比率,蓝色的农村地区和红色的大都市地区突出显示了这一比率。相对于轻微事故的数量,圆圈越大的位置发生的严重事故越多,反之亦然。直观地说,像伦敦这样的城市比一般的圈子要大,因为在这样一个大城市,交通和拥堵水平更高,导致更严重的事故。

英国严重事故与轻微事故的比率。圆圈越大,比率越高。城市用红色标出,农村用蓝色标出。

然而,有趣的是注意到英国不同的地方,尽管是农村,却有和伦敦一样高的严重到轻微事故的比率。这可以为地方政府和更广泛的政府提供有用的见解,显示英国哪些地区风险最高,哪些地方的改善和政策需要优先考虑。例如,靠近英格兰边境的兰开夏郡和威尔士北部地区。我们的模型显示,这些地区是最容易发生严重事故的地区,最近不同的新闻媒体也证实了这些地区是最危险的地区。

摘要

  • 我们分析了英国各地的交通事故数据,以寻找能够推动旨在拯救生命的决策的见解。
  • 我们解决了这个问题,首先开发了一个驱动因素分数,给每个驱动因素分配一个风险等级。然后,每个司机能够了解他们是否处于危险中,最重要的是,什么特征是主要因素。
  • 其次,我们分析了驾驶员无法控制的外部信息,比如路况。我们能够突出全英国应该优先考虑的领域,将政府资金集中在这些高度优先的领域。
  • 这两项分析让我们进一步了解事故的根本原因,让司机和政府能够防患于未然。

要阅读更多类似的文章,请关注我的TwitterLinkedIn或我的* 网站 *

使用机器学习预测国家人口

原文:https://towardsdatascience.com/using-machine-learning-to-predict-country-population-550f9e5f3e24?source=collection_archive---------23-----------------------

基于 50 多年的人口记录来预测下一年人口的简单线性回归模型。

照片由岩田良治Unsplash 上拍摄

机器学习已经成为最近的热门话题之一。有很多开发和研究正在进行,以保持这一领域向前发展。在本文中,我将演示一个简单的线性回归模型,该模型将预测一个国家未来几年的人口。

我不打算在这里详细解释线性回归,因为在 Medium 中有许多文章和许多在线课程提供了对线性回归的深入讨论。我将简单地展示和解释一个使用 python 中的线性回归开发的小项目。这个项目中使用的数据集是从世界银行收集的。

此项目需要以下库。

import pandas as pd
import numpy as np
*from* sklearn.linear_model *import* LinearRegression
*import* re
*import* json

要忽略代码输出中的警告,可以使用警告模块,但这是可选的。

*import* warnings
warnings.filterwarnings("ignore")

导入必要的库之后,现在是将数据加载到熊猫数据报的时候了。下载后我把人口文件保存为 pop.csv。

df = pd.read_csv('pop.csv')
df.head()

这将产生以下结果:

df.head()

从上图可以看出,在将数据传递给线性回归模型之前,还需要进行一些预处理。有几个不必要的列,如指标代码、指标名称和本项目不需要的其他列。在此之前,我想根据国家名称列选择一个国家。

bd=df.loc[df['Country Name']=='Bangladesh']
bd.drop(['Country Name','Country Code','Indicator Name','Indicator Code'],axis=1,inplace=True)
bd = bd.T
bd.head()

我以前在这里。loc 选择孟加拉国,选择后,我删除了四列,这样我就有了所有年份的列和人口的行。我调换了数据框架,这样我就只有两列,即年份和人口。下图是上述代码的结果。

但是没有显示列名,而是显示 18。此外,年份显示为一个索引。对于自回归(AR)模型来说,如果年份被显示为指数,那就很好了。但是对于线性回归,我希望 year 作为一个不同的列,而不是一个索引。因为

y = mx + c

这是一个简单的线性回归公式,其中 y 是预测变量或因变量, x 是自变量, m 是斜率或系数, c 是截距。在这个项目中, x 是年份, y 是预测人口。因此,下面的代码将帮助我准备过程和重命名列。

bd.dropna(inplace=True)
bd=bd.reset_index().rename(columns={18:'population','index':'year'})
bd.head()

这将产生以下结果:

现在,我可以使用这个数据框架来训练线性回归模型,并获得所需的输出。

x = bd.iloc[:, 0].values.reshape(-1, 1)
y = bd.iloc[:, 1].values.reshape(-1, 1)
model = LinearRegression().fit(x, y)
y_pred = model.predict([[2019]])
y_pred

在这段代码中,我将我的年份和人口转换为 2D 数组,这是 LinearRegression 中需要使用的。然后我简单的调用了模型,在模型中拟合 x 和 y。之后我用 model.predict()预测了下面这个结果。

array([[1.65040186e+08]])

现在,所有这些都很好,但我有 100 多个国家的人口信息,我只限于一个国家使用上面的代码块。上面的代码块将作为下面代码块的主干,它将显示原始项目的实现。这个项目将接受国家和年份的用户输入,之后,我可以像以前一样做一些预处理,稍微调整一下,并使用线性回归模型来显示预测的结果。

def main():
    country = input("Please input the country name: ").lower()
    year = int(input("Please input the year to predict: "))
    df = pd.read_csv('pop.csv')
    lists, df = country_list_gen(df)
    if country in lists:
        df = selecting_country(df, country)
        model = prediction_model(df)
        result = prediction(model,year)
        print(f"\n Result: {country.upper()} population in {year} will be {result:,d}")
    else:
        print('kindly check country name spelling from country_list.json')

if __name__ == "__main__":
    main()

上面的代码是我的脚本的主要功能。首先,它接受用户输入的国家名称,并将其转换成小写字符串。之后它也会以年份为输入,转换成整数。然后使用 pandas read_csv()将原始 CSV 文件加载到 dataframe 中。然后,它执行下面的函数。

def country_list_gen(df):
    df.rename(columns={'Country Name':'country_name'},inplace=True)
    df['country_name'] = df['country_name'].apply(lambda row: row.lower())
    lists = df['country_name'].unique().tolist()
    with open('country_list.json','w', encoding='utf-8') as f:
        json.dump(lists, f, ensure_ascii=False,indent=4)
    return lists, df

这是一个将所有唯一的国家名称存储在 JSON 文件中的功能,这将帮助用户检查该国家是否可用,以及是否有任何拼写错误。该函数将原始数据帧作为输入参数,并重命名 country_name 列。之后,它将使用。应用()和 lambda 函数。然后,所有唯一的国家名称被转换成列表,并保存为 country_list.json 文件。最后,它将列表和修改后的数据帧返回给主函数。为了便于阅读,我再次给出了主要的函数代码。

def main():
    country = input("Please input the country name: ").lower()
    year = int(input("Please input the year to predict: "))
    df = pd.read_csv('pop.csv')
    lists, df = country_list_gen(df)
    if country in lists:
        df = selecting_country(df, country)
        model = prediction_model(df)
        result = prediction(model,year)
        print(f"\n Result: {country.upper()} population in {year} will be {result:,d}")
    else:
        print('kindly check available country name and thier spelling from country_list.json')

现在,返回的列表包含可用的国家名称,因此执行一个简单的 if-else 语句。如果用户输入的国家名称在列表中不可用,或者如果我的列表中有拼写错误,那么它会指示用户查看 country_list.json 文件。但是,如果名称已经匹配,那么它将执行以下功能。

def selecting_country(df,country):
    df = df.loc[df['country_name']==country]
    df.drop(['country_name','Country Code','Indicator Name','Indicator Code'],axis=1,inplace=True)
    df = df.T
    df.dropna(inplace=True)
    df = df.reset_index()
    return dfdef prediction_model(df):
    x = df.iloc[:, 0].values.reshape(-1,1)
    y = df.iloc[:, 1].values.reshape(-1,1)
    model = LinearRegression().fit(x,y)
    return modeldef prediction(model, year):
    return int(model.coef_[0][0] * year + model.intercept_[0])

selecting _ country 函数获取国家名称并过滤数据帧,然后丢弃不必要的字段,转置并重置数据帧的索引,并将其返回给主函数。然后,主函数调用 prediction_model,并将数据帧作为参数发送。在这里,我不关心重命名列名,因为我将它们转换为 x 和 y 的 2D 数组。之后,调用 LinearRegression()并用 x 和 y 拟合模型。然后,拟合的模型被发送到主函数。然后,主函数将这个模型与用户在预测函数中提示的年份一起传递。该预测函数只需从模型中获取系数和截距,并使用“y=mx+c”来导出预测的人口。

下面给出了两种不同场景的示例输出,一种是正确的国家名称,另一种是错误的拼写。

正确名称

拼写错误

这是我在一小时内完成的项目。这是一个非常简单和容易实现的项目,并且对线性回归模型有实际的理解。我来自孟加拉国,这就是我在这篇文章中使用孟加拉国作为国名的原因。你可以从 GitHub 访问并查看完整代码。你可以在这里看到我以前的文章,它是关于使用免费 API 从公共 IP 地址获取地理位置信息的。

非常感谢您阅读我的文章。我希望,当我做这个项目和写这篇文章的时候,你学到了一些新的东西。

利用机器学习预测梦幻足球积分

原文:https://towardsdatascience.com/using-machine-learning-to-predict-fantasy-football-points-72f77cb0678a?source=collection_archive---------16-----------------------

一个幻想足球贸易分析仪使用 RNN LSTM,ARIMA,XGBoost 和破折号

NFL 梦幻足球是一种游戏,其中足球迷扮演职业足球队的教练或总经理的角色。参与者起草他们的初始球队,选择每周比赛的球员,并交易球员,以便在一个赛季中每周与其他球队比赛。获胜队是由职业运动员的真实统计数据决定的。

交易分析器的目标

交易分析器的目标是确定在 2019 年足球赛季的任何一周,一名或多名球员的拟议交易是“好”还是“坏”交易。一笔好的交易被定义为用你队中的一名球员交换一名有着更高期望的梦幻足球分数的球员。因此,目标是确定 2019 赛季每个星期每个职业足球运动员的预期梦幻足球积分。然后可以在玩家之间比较期望的分数。

点击这里看 2019 NFL 奇幻足球交易分析

积分制

交易分析器使用 ESPN 标准联盟规则来确定梦幻足球点数。一支球队由 7 个位置的 9 名先发球员组成。这 7 个位置是四分卫(QB),跑卫(RB),外接员(WR),紧逼端(TE),踢球者(K)和团队防守(DF)。在虚拟足球中,一支球队的防守阵容被认为是一个“球员”。

积分制

数据

用于建模的数据来自 、ArmchairAnalysis.com。该数据由 28 个不同的 excel 表格中的 700 多个数据点组成,提供了自 2000 年以来 NFL 中每个球员的 20 年详细统计数据。

原始数据最初在 Excel 中进行处理,以便链接和提取所有 2019 年 NFL 球员的相关数据。经过处理的数据构成了初始建模数据的基础。根据初始建模数据,为每个 2019 年球员的每个赛季的每场比赛计算实际的梦幻足球积分。模型的训练数据使用了截至 2018 赛季每个球员的所有可用数据,2019 年的数据用作测试数据。例如,如果一名球员在 2014 年开始职业生涯,那么 2014-2018 年的数据用于训练数据,2019 年的数据用于测试数据。

建模

共有四个模型被用于预测 653 名 2019 年 NFL 球员的预期梦幻足球积分。这些数据本质上是时间序列数据,玩家拥有 0 到 20 年(20 个赛季 x 16 场比赛= 320 个数据点)的数据。

平均值:使用每个 2019 年球员的平均值建立基线测量。第一周的平均值是使用 2018 赛季球员(不包括新秀)的平均每场比赛分数建立的。第 2 周到第 17 周是使用他们前几周实际绩效的平均值计算的(例如,第 2 周使用第 1 周的实际绩效,第 3 周使用第 1 周和第 2 周实际绩效的平均值,等等)。).这将被认为是天真的方法,也是普通的梦幻足球参与者经常使用的方法。

XGBoost: 一个 XGBoost 模型被用来预测 2019 赛季第一周新秀球员的表现。2019 年的 106 名新秀很难预测,因为他们没有专业经验。使用 515 名非新秀球员(不包括防守)的新秀年统计数据作为训练数据,计算 2019 赛季第 1 周预期幻想点数,验证 MAE 为 28.93,R 平方为 0.49,测试 MAE 为 30.75,R 平方为 0.43。这些是微弱的结果,但需要一个起点。第 2 周到第 17 周是使用他们前几周实际绩效的平均值计算的,与基线相同(例如,第 2 周使用第 1 周的实际绩效,第 3 周使用第 1 周和第 2 周实际绩效的平均值,等等。).

ARIMA:ARIMA 模型最初用于预测 2019 赛季每周的老将表现。只有经验超过 3 年且在这 3 年中至少获得 50 分的资深玩家才使用 ARIMA 运行,因为模型需要大约 50 个数据点(3 个赛季 x 16 场比赛= 48 个数据点)才能表现良好。使用 ARIMA 对大约 246 名资深玩家进行建模,虽然结果非常好(参见评估选项卡)并且超过了基线,但神经网络模型显示出更好的结果。

递归神经网络,长短期记忆(RNN-LSTM):RNN-LSTM 模型用于预测 2019 赛季每周的老将表现。只有拥有 3 年以上经验和 3 年内至少 50 分的资深球员才使用 RNN-LSTM 进行比赛,因为该模型需要尽可能多的数据,3 年似乎是一个很好的平衡。大约 246 名经验丰富的球员使用 RNN-LSTM 模型,并将结果与 ARIMA 模型进行比较。RNN-LSTM 模型优于基线平均值和 ARIMA 模型,是最终应用程序中使用的模型。

备注:

最终结果:最终结果综合了新秀成绩(XGBoost+平均— 106 人)、3+年的老将(RNN-LSTM —包括防守在内 278 人)和剩下的有< 3 年经验的老将(平均— 269 人)。这在“评估”选项卡中被称为“神经网络”模型,而“ARIMA”是对 3 年以上的退伍军人仅使用 ARIMA 的相同结果组合。

每周预测:每个模型都计算本季度剩余几周的预测。然后将剩余的每一周相加,得出玩家期望的梦幻足球点数。例如,在第 1 周
中,预期的梦幻足球分数是所有 16 场比赛预测(整个赛季)的总和,在第 2 周中,分数是剩余 15 场比赛的总和,等等。

再见周:一个足球赛季实际上有 17 周,每个球队在赛季中都有一个随机的再见周。在“再见周”,每个玩家前一周的预测都会被简单地继续下去。

伤病:伤病在梦幻足球中起着巨大的作用。当一名球员受伤时,他的分数基本上降到零。没有可靠的方法来预测运动员是否或何时会受伤。虽然原始数据确实提供了一些关于伤病的细节,但它仍然没有提供球员何时从伤病中恢复的良好信息。在所有的模型中,当一名球员处于受伤的预备队状态时,他被认为是缺席了整个赛季。

代码

该项目的所有代码、数据和相关文件都可以在我的 GitHub repo 中访问。自述文件提供了 repo 目录和文件的详细信息。

RNN-LSTM 模型

虽然预测梦幻足球积分的完整代码相当复杂,但让我们以一名球员为例,仔细看看核心的 RNN-LSTM 代码。我们要用艾伦·罗杰斯,绿湾包装工队的四分卫。Aaron 是一个优秀的测试案例,因为他在 2005 年开始了他的 NFL 职业生涯,提供了 15 年的数据。此外,他在 2019 赛季没有受到任何重大伤害。

每个 2019 NFL 球员的实际积分存储在 original_df 数据帧中。数据帧的前五列包含每个玩家的描述性特征。接下来的 320 列包含每个球员从 2000 年到 2019 年每场比赛的实际梦幻足球积分(每个赛季 16 场 x 20 个赛季= 320 个数据点)。这些值是 NaN 直到球员的第一个 NFL 赛季。所以 Aaron Rodgers 的实际数据直到第 85 栏才开始(16x 5 = 2005 赛季前的 80 场比赛加上 5 个描述栏)。

原始 _ 测向数据帧

对 RNN-LSTM 预测函数 lstm_pred 的初始调用发生在名为 main 的函数中。该功能的输入包括:

player=‘Aaron Rodgers’(这是玩家的名字)
n_periods = 16(在本例中,我们从赛季初开始,预测 2019 赛季的所有 16 场比赛)
col = 85 (Aaron 的 2005 年数据从数据帧的第 85 列开始)

pred_points = lstm_pred(player, n_periods, col)

利用这三个输入,让我们仔细看看 lstm_pred。

def lstm_pred(p, np, c):
    series = make_series(p, c)
    X = series.values
    supervised = timeseries_to_supervised(X, 1)
    supervised_values = supervised.values

    # Split data into train and test-sets
    train, test = supervised_values[0:-np], supervised_values[-np:]

    # Transform the scale of the data
    scaler, train_scaled, test_scaled = scale(train, test)

    # Fit the model
    lstm_model = fit_lstm(train_scaled, 1, 100, 1)

    # Forecast the entire training dataset to build up state
    train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
    lstm_model.predict(train_reshaped, batch_size=1)

    # Walk-forward validation on the test data
    yhat_sum = 0
    for i in range(len(test_scaled)):
        # Make one-step forecast
        X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
        yhat = forecast_lstm(lstm_model, 1, X)
        # Invert scaling
        yhat = invert_scale(scaler, X, yhat)
        # Sum the weekly forecasts
        yhat_sum = yhat_sum + yhat

    return yhat_sum

让我们浏览一下这段代码,以便更好地理解它是如何工作的。前四行只是为模型准备数据。

series = make_series(p, c)
X = series.values
supervised = timeseries_to_supervised(X, 1)
supervised_values = supervised.values

函数 make_series 只是将 Aaron 从 2005 年到 2019 年存储在 dataframe 中的实际点数转换成一个 python 序列。然后,系列值存储在 X 中。现在, X 值代表了 Aaron 的梦幻足球点数的时间序列。函数 timeseries_to_supervised(X,1) 获取时间序列 X 并创建一个包含 X 我们的监督学习输入模式和 y 我们的监督学习输出模式的数据帧。 y 值只是将 X 系列向后移动一个周期。然后将 Xy 值存储在 numpy 数组 supervised_values 中。我们对这四行代码(及其功能)所做的是从原始 _df 数据帧中提取 Aaron 的 fantasy football point production,并将其转换为监督学习的形式。代表 2019 赛季的 ssupervised _ values的尾端在下面。第一列代表 X 值,第二列代表 y 值。

....
 [ 1.04 12.92]
 [12.92 14.36]
 [14.36 14.3 ]
 [14.3  25.48]
 [25.48  9.42]
 [ 9.42 18.32]
 [18.32 44.76]
 [44.76 28.1 ]
 [28.1  12.94]
 [12.94 10.02]
 [10.02  9.46]
 [ 9.46 28.12]
 [28.12 11.4 ]
 [11.4  14.42]
 [14.42  9.34]
 [ 9.34 19.02]]

下一行代码将 supervised_values 拆分为 traintest 将最近 16 场比赛(2019 赛季)拆分为测试集,并将 2005-2018 赛季作为训练集。

train, test = supervised_values[0:-np], supervised_values[-np:]

一旦代码被拆分,使用 scale 函数对数据进行标准化。scale 函数使用范围为(-1,1)的 MinMaxScaler。建议对数据进行规范化,因为这将使神经网络的学习更容易,并应确保值的大小或多或少相似。

scaler, train_scaled, test_scaled = scale(train, test)

我们现在准备通过函数 fit_lstm 拟合 RNN-LSTM 模型,参数如下:
train:train _ scaled
batch _ size:1
nb _ epochs:100
神经元: 1

lstm_model = fit_lstm(train_scaled, 1, 100, 1)

fit_lstm 函数是 RNN-LSTM 魔术发生的地方。简而言之,该函数使用一个连续的 Keras API,包括一个 LSTM 层和一个密集层。使用的损失函数是具有“adam”优化算法的“均方误差”。该函数的实际机制相当复杂,对于那些希望获得进一步信息的人,请参见杰森·布朗利 的文章 中的 LSTM 模型开发部分中关于该函数的详细描述。

为了获得更好的结果,文章建议使用更大数量的历元(1000–4000)和神经元(1–5),但是由于运行数百个玩家的预测需要时间和资源,100 个历元和 1 个神经元以资源较少的方式提供了非常好的结果。

我们现在准备使用模型的 lstm_model.predict 函数进行预测。

train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
lstm_model.predict(train_reshaped, batch_size=1)

该函数需要一个 3D numpy 数组作为输入,在我们的例子中,这将是一个一个值的数组(前一个时间步长的观测值),并输出一个一个值的 2D numpy 数组。在预测测试数据之前,我们需要通过预测训练数据中的所有值来播种初始状态。上面代码的第一行将数据重新整形为单值 3D numpy 数组,第二行使用训练数据设置网络的初始状态。

我们所剩下的就是逐步通过测试数据,对 16 场比赛进行单独的预测,并反转比例以得出最终的单独预测。然后将个人预测相加,以创建亚伦 2019 赛季的预测幻想足球积分。

yhat_sum = 0
for i in range(len(test_scaled)):
    # Make one-step forecast
    X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
    yhat = forecast_lstm(lstm_model, 1, X)
    # Invert scaling
    yhat = invert_scale(scaler, X, yhat)
    # Sum the weekly forecasts
    yhat_sum = yhat_sum + yhat

亚伦罗杰斯赛季的预测结果如下。本赛季的总预测是 293 分(四舍五入),他的实际得分是 282 分——本赛季 4%的误差还不错!(每次运行代码时,结果会略有不同)

yhat =  13.550041856765747
yhat =  15.615552995204924
yhat =  17.195858060121534
yhat =  18.015674024820328
yhat =  20.544238206744193
yhat =  18.375679302811623
yhat =  19.83470756918192
yhat =  18.937192922234534
yhat =  21.3260236287117
yhat =  20.56778145074844
yhat =  18.166402292251586
yhat =  16.316439254283903
yhat =  19.98121999800205
yhat =  18.90507374048233
yhat =  18.95989200234413
yhat =  16.812043523192404

yhat_sum =  293.10382082790136

对模型预测的评估

递归神经网络— LSTM 模型(NN)优于基线平均值和 ARIMA 模型。虽然 NN 和 ARIMA 模型都具有较低的 MAEs,但它们都在赛季中途开始收敛,因为所有模型都包含当前赛季的实际点,并且未来预测的数量减少,从而增加了所有模型的准确性。

模型的真实准确性显示在正确预测百分比图中。每个星期,每个玩家的“好”或“坏”交易的预测结果都要与其他玩家的交易结果进行比较,并与实际结果进行比较。神经网络显然比其他模型做出了更好的预测,2019 赛季的总体平均正确率为 85.59%,而 ARIMA 模型为 84.72%,基线平均模型为 83.17%。

Dash App

由于本文侧重于该项目的机器学习方面,Dash 应用程序的细节将不被涵盖。有关创建 Dash 应用程序的详细信息,请参见我的文章 如何创建交互式 Dash Web 应用程序

我欢迎建设性的批评和反馈,请随时给我发私信。

在推特上关注我

这篇文章最初出现在我的 GitHub Pages 网站上

梦幻足球原始数据来源:*ArmchairAnalysis.com
RNN-LSTM 模型开发:用 Python 中的长短期记忆网络进行时间序列预测作者:杰森·布朗利图片来源: Pixabay*

使用机器学习来预测 Fitbit 睡眠分数

原文:https://towardsdatascience.com/using-machine-learning-to-predict-fitbit-sleep-scores-496a7d9ec48?source=collection_archive---------8-----------------------

凯特·斯通·马西森在 Unsplash 上的照片

随机森林、极端梯度推进以及它们在预测 Fitbit 睡眠分数时的表现

在本文的第 1 部分的中,我解释了我们如何从 Fitbit 获取睡眠数据,将其加载到 Python 中,并对数据进行预处理,为进一步的分析做好准备。在这一部分中,我将解释我们如何以及为什么将数据分为训练集、验证集和测试集,我们如何为我们的机器学习模型选择特征,然后训练三个不同的模型:多元线性回归、随机森林回归器和极端梯度推进回归器。我将简要解释这些模型是如何工作的,并定义性能度量来比较它们的性能。让我们开始吧。

将数据分为训练集、验证集和测试集

在我们使用我们的数据做任何进一步的分析之前,我们需要将整个数据集分成三个不同的子集:训练集、验证集和测试集。下图很好地展示了这一过程:

培训、验证和测试数据

测试集也被称为保留集,一旦我们从剩余数据中分离出来,我们就不会再接触它,直到我们训练和调整我们的机器学习模型,达到我们认为它们会在他们从未见过的数据上表现良好的程度。

我们将剩余的数据分成训练集和验证集。这允许我们根据训练数据训练我们的模型,然后根据验证数据评估它们的性能。理论上,我们可以调整我们的模型,并根据验证数据再次评估它们,从而找到提高模型性能的方法。这一过程通常会导致过度拟合,这意味着我们过于关注训练我们的模型,使其在验证集上表现良好,但在从未见过的数据集(如测试集)上表现不佳。

在本文的第 3 部分,我解释了我们如何减少过度拟合,同时确保模型仍然表现良好。现在,我们将遵循上述方法,将数据集简单地分为训练集、验证集和测试集。

我希望以这样的方式分割数据,即训练集占总数据集的 60%,验证集和测试集各占 20%。此代码实现了正确的百分比拆分:

在第一次测试拆分中,test_size 参数设置为 0.2,这将数据拆分为 80%的训练数据和 20%的测试数据。为了将 80%的训练数据分成训练和验证数据,并确保验证数据是原始数据集大小的 20%,test_size 参数需要为 0.25 (20%是 80%的四分之一,即 0.25)。

在继续之前,我想强调一件重要的事情。在执行任何进一步的转换(例如缩放数据)之前拆分数据是至关重要的,因为我们希望防止任何关于测试集的信息溢出到我们的训练和验证集中。数据缩放通常使用关于数据集整体的统计数据来完成,例如均值和标准差。因为我们希望能够衡量我们的机器学习模型在他们从未见过的数据上的表现,所以我们必须确保来自测试数据的信息不会影响缩放或任何其他转换的完成方式。

扩展功能、定义性能指标和基线

虽然对于本项目中的机器学习模型,不需要进行特征缩放,但在比较不同模型及其性能时,缩放特征被认为是最佳实践。

在这段代码中,我使用了 MinMaxScaler,它适合于训练数据,然后用于缩放训练、验证和测试数据:

绩效指标

接下来,让我们定义一些可以用来评估和比较我们的模型的性能度量。因为睡眠分数是一个连续的变量(虽然只有整数睡眠分数是可能的),手头的问题是一个回归问题。对于回归问题,有许多不同的性能指标,在此分析中,我将使用平均绝对误差、均方误差和 R 平方。此外,我计算模型预测的准确性。

准确性通常用作分类问题中的性能度量,而不是回归问题中的性能度量,因为它指的是模型做出的正确预测的比例。在这个分析中,我使用回归模型的准确性的方式是不同的。回归模型的准确性是预测的睡眠得分与实际睡眠得分平均相差多远(以百分比计)的量度。例如,如果实际睡眠得分为 80,模型的准确度为 96%,这意味着平均误差为 4%,则模型预计会对睡眠得分做出 76.8(80-(80 x 0.04))到 83.2 (80 + (80 x 0.04))的预测。

下面是评估模型性能的函数,它将手头的模型、测试特征和测试标签作为输入:

但是我们如何知道这些不同的度量标准的分数是好是坏呢?比如 90%的准确率是好是坏?R 平方呢?为了有一个参考点,我们将首先提出一个基线模型,我们可以比较所有后来的模型及其性能。

基线性能

为了评估我们将要建立的机器学习模型,我们希望有一个基线,以便我们可以比较它们的性能。通常,基线是一种基于简单规则生成预测的简单方法。对于我们的分析,基线模型总是预测训练集的中值睡眠分数。如果我们的机器学习模型不能超越这个简单的基线,它将是相当无用的。

让我们看看基线的性能是什么样的:

虽然准确性看起来不错,但看看其他绩效指标就会发现一个非常不同的故事。R 平方为负,这强烈表明模型性能极差。

现在,我们已经将数据分成不同的子集,扩展了功能,定义了性能指标,并提出了一个基线模型,我们几乎准备好开始训练和评估我们的机器学习模型。在我们继续我们的模型之前,让我们首先选择我们想要在那些模型中使用的特性。

使用套索回归的特征选择

阅读完标题后,您可能会有两个问题:为什么我们需要选择要素以及 Lasso 回归到底是什么?

特征选择

仅选择可用功能的子集有多种原因。

首先,特征选择使机器学习算法能够更快地训练,因为它使用更少的数据。其次,它降低了模型的复杂性,使解释模型变得更容易。在我们的案例中,这很重要,因为除了准确预测睡眠得分,我们还希望能够了解不同的特征如何影响睡眠得分。第三,特征选择可以减少过拟合,从而提高模型的预测性能。

在本文的第 1 部分中,我们看到睡眠数据集中的许多特性是高度相关的,这意味着我们使用的特性越多,模型中出现的多重共线性就越多。一般来说,如果我们只关心模型的预测性能,这不是问题,但如果我们希望能够解释模型,这就是问题。特征选择也将有助于减少一些多重共线性。

有关功能选择的更多信息,请参见这篇文章。

套索回归

在我们继续讨论套索回归之前,让我们简要回顾一下线性回归的作用。拟合线性回归通过为每个特征变量选择系数来最小化损失函数。这样做的一个问题是,大系数会导致过度拟合,这意味着该模型在训练数据上表现良好,但在从未见过的数据上表现不佳。这就是正规化发挥作用的地方。

Lasso 回归是一种正则化回归,它通过损失函数中的附加项来惩罚回归系数的绝对大小。套索回归的损失函数可以写成这样:

Lasso 回归的损失函数

损失函数的第一部分相当于线性回归的损失函数,它使残差平方和最小。附加部分是惩罚项,它惩罚系数的绝对值。从数学上讲,这相当于最小化残差平方和,同时约束绝对系数值之和必须小于预先指定的参数。此参数决定正则化的量,并导致某些系数收缩到接近或完全为零。

在上面的等式中,λ是决定罚分强度的调整参数,即收缩量。设置λ=0 将导致线性回归的损失函数,并且随着λ增加,越来越多的系数被设置为零,因此剩余的系数被 Lasso 回归“选择”为重要的。

对训练数据拟合套索回归,并绘制结果系数,如下所示:

Lasso 回归算法将床上时间和浅睡分钟的系数降低到接近于零,认为它们没有其他四个特征重要。这很方便,因为如果我们在模型中包含所有要素,我们将面临主要的多重共线性问题。让我们从数据集中去掉这两个特征:

现在我们已经选择了一组四个特征,我们可以继续建立一些机器学习模型,这些模型将使用这四个特征来预测睡眠分数。

多元线性回归

总之,多元线性回归(MLR)用于估计一个因变量和两个或更多自变量之间的关系。在我们的例子中,它将被用来估计睡眠分数与睡眠分钟数、清醒分钟数、快速眼动睡眠分钟数和深度睡眠分钟数之间的关系。注意,MLR 假设这些变量之间的关系是线性的。

让我们训练一个 MLR 模型并评估它的性能:

所有的性能测量都比基线模型好得多(感谢上帝)。尤其是精度似乎非常高,但这可能会产生误导,这就是为什么考虑多种测量方法非常重要。回归性能最重要的度量之一是 R 平方。一般来说,R 平方度量的是由自变量解释的因变量方差的比例。因此,在我们的情况下,这是一个衡量睡眠分数差异有多少是由我们的特征解释的。大约 0.76 的值已经很不错了,但是让我们看看是否可以通过使用不同的模型做得更好。

回归统计

在我们继续学习其他机器学习模型之前,我想看一下我们训练数据的多元线性回归的回归输出:

关于回归输出,需要注意以下几点:

  1. 所有 p 值都具有统计学意义。
  2. 睡眠分钟数、快速眼动睡眠分钟数和深度睡眠分钟数具有正系数,这意味着这些变量的增加会增加睡眠分数。
  3. 醒着的分钟数有一个负系数,表明醒着的时间越长,睡眠得分越低。
  4. 根据系数的大小,快速眼动睡眠似乎比深度睡眠对睡眠分数有更大的积极影响。

回归输出为理解不同的睡眠统计如何影响睡眠得分提供了一个良好的起点。睡眠时间越长,睡眠得分越高。这是有道理的,因为更多的睡眠(直到某一点)通常是有益的。同样,更多的快速眼动和深度睡眠时间也会增加睡眠得分。这也是有意义的,因为这两个睡眠阶段都提供了重要的恢复益处。对于睡眠分数的计算,Fitbit 似乎认为 REM 睡眠比深度睡眠更重要(系数的数量级更高),这对我来说是回归分析最有趣的结果之一。最后,醒着的时间越长,睡眠得分越低。再说一遍,这完全有道理,因为在一个人的睡眠窗口期花更多的时间醒着表明坐立不安,并剥夺了睡眠时间提供的恢复能力。

对于那些有兴趣了解不同睡眠阶段和睡眠重要性的人,我强烈推荐马修·沃克的《我们为什么睡觉》。这是一本写得非常精彩的书,有着引人入胜的实验和见解!

尽管如此,需要注意的是,由于要素之间的相关性,上述输出的可解释性有些有限。在多元线性回归中,系数告诉你当自变量增加一个单位时,在所有其他自变量保持不变的情况下,因变量预计会增加多少。在我们的例子中,因为独立变量是相关的,我们不能期望一个变量改变而其他变量不变,因此不能以这种方式可靠地解释系数。在解释你的模型时,总是要注意多重共线性!

让我们看看其他机器学习模型的表现是否优于多元线性回归。

随机森林回归量

随机森林是最受欢迎的机器学习模型之一,因为它们能够在分类和回归问题上表现良好。简而言之,随机森林是一种通过引导聚合利用多个决策树的集成技术,也称为“装袋”。那到底是什么意思?

为了更好地理解这一点,我们首先需要理解决策树回归是如何工作的。

决策树回归

顾名思义,决策树以树状结构的形式构建预测模型,可能如下所示:

预测游戏时间的决策树

在上面的例子中,决策树基于各种特征迭代地分割数据集,以便预测玩游戏将花费多少小时。但是树如何知道首先分割哪些特征,以及在树的更下面分割哪些特征呢?毕竟,如果我们改变用于进行分割的特征的顺序,预测可能会不同。

在回归问题中,决定在特定结点上分割数据集的最常用方法是均方误差(MSE)。决策树尝试使用不同的特征来分割数据集,并计算得到的 MSE。导致最低 MSE 的特征被选择用于手边的分割。这个过程一直持续到树到达一片叶子(一个端点)或预定的最大深度。最大深度可用于减少过度拟合,因为如果允许决策树继续直到它找到一片叶子,它可能会严重过度拟合训练数据。以这种方式使用最大深度被称为树的“修剪”。

决策树有两个主要限制:

  1. 贪婪——决策树并不总是全局最优的,因为我们假设创建决策树的最佳方式是找到将导致 MSE 最大程度降低的特征,而不考虑次优分割是否会导致更好的分割(“贪婪”策略)。
  2. 过度拟合-树的结构通常过于依赖于训练数据,经常修剪树不足以克服这个问题。

随机森林解决了这两个限制。

随机森林

正如随机森林中的“森林”所暗示的那样,它们由许多决策树组成,并且它们的预测是通过对森林中每个决策树的预测进行平均而得到的。把这想象成一种民主。在一个重要问题上只有一个人投票可能不能代表整个社区的真实感受,但从社区中随机选择的许多成员那里收集投票可能会提供准确的代表。

但《随机森林》中的“随机”到底代表了什么?

在随机森林中,每个决策树都是使用训练集中随机选择的数据点子集创建的。这样,每棵树都是不同的,但是所有的树仍然是从相同的训练数据的一部分创建的。子集通过替换随机选择,这意味着数据点被“放回袋子”中,并且可以再次被挑选用于另一个决策树。

除了为每棵树选择不同的随机子集,随机森林中的决策树在每次分裂时只考虑随机选择的特征的子集。为即将进行的分割选择最佳特征,并且在下一个节点,评估一组新的随机特征,等等。

通过使用这些“打包”技术构建决策树,随机森林很好地解决了单个决策树的局限性,并设法将孤立的弱预测器转变为组中的强预测器,类似于投票示例。

Python 中的随机森林回归

使用 Python 中的 scikit-learn 库,大多数机器学习模型都是以同样的方式构建的。首先,您初始化模型,然后在训练集上训练它,然后在验证集上评估它。代码如下:

与多元线性回归类似,随机森林的表现远远好于基线模型。也就是说,它的 R 平方和精度低于 MLR。那么,围绕随机森林的所有宣传是关于什么的呢?

这个问题的答案可以在这里找到(提示:超参数优化):

[## 交叉验证和超参数调整:如何优化你的机器学习模型

非常准确地预测 Fitbit 的睡眠分数

medium.com](https://medium.com/@bennerjonas10/cross-validation-and-hyperparameter-tuning-how-to-optimise-your-machine-learning-model-13f005af9d7d)

极端梯度推进回归器

与随机森林类似,梯度增强是一个集成学习器,这意味着它基于一组单独的模型(通常是决策树)来创建最终模型。与随机森林相比,梯度增强的不同之处在于集合方法的类型。随机森林使用“Bagging”(前面描述过),梯度增强使用“Boosting”。

梯度推进

梯度推进背后的一般思想是,通过对具有错误预测和高误差的实例赋予更多权重来依次构建各个模型。因此,该模式“从过去的错误中吸取教训”。

该模型通过梯度下降最小化成本函数。在每一轮训练中,弱学习者(决策树)做出预测,并与实际结果进行比较。预测和实际结果之间的距离代表模型的误差。然后,误差可用于计算梯度,即损失函数的偏导数,以计算出在哪个方向上改变模型参数以减小误差。下图显示了这是如何工作的:

梯度下降

这些调整的速率(上图中的“增量步幅”)可通过超参数“学习速率”进行设置。

极端梯度推进

极端梯度增强通过计算成本函数的二阶偏导数来改进梯度增强,这有助于获得成本函数的最小值,并使用类似于使用 Lasso 回归描述的高级正则化,这改进了模型的泛化。

在 Python 中,训练和评估极端梯度增强回归变量遵循与随机森林回归变量相同的拟合和评分过程:

性能指标非常接近随机森林,也就是说,它的表现还不错,但仍然不如我们过去的多元线性回归。

从这里去哪里?

到目前为止,我们还没有在随机森林或极端梯度推进回归中提供任何超参数。各自的库为每个模型的超参数提供了合理的默认值,但没有一个尺寸适合所有情况。通过调整一些超参数,我们有可能极大地提高这两个模型的性能。

此外,对于我们的性能评估,到目前为止,我们只依赖于模型在一个相对较小的验证集上的性能。因此,性能在很大程度上取决于该验证集在整体上对睡眠数据的代表性。

在本文的第三部分,我解决了这两个问题,并提高了随机森林和极端梯度推进回归器的性能。看这里:

[## 交叉验证和超参数调整:如何优化你的机器学习模型

非常准确地预测 Fitbit 的睡眠分数

medium.co](https://medium.com/@bennerjonas10/cross-validation-and-hyperparameter-tuning-how-to-optimise-your-machine-learning-model-13f005af9d7d)

利用机器学习预测未来比特币价格

原文:https://towardsdatascience.com/using-machine-learning-to-predict-future-bitcoin-prices-6637e7bfa58f?source=collection_archive---------5-----------------------

如何利用深度学习预测未来

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

有没有可能预测明天的比特币价格?或者如果这个跳跃太远,那么 20 分钟后的价格呢?

我将使用长短期记忆 (LSTM) RNN 机器学习模型来预测 20 分钟后的比特币价格,仅仅依靠简单的历史金融数据。

我写这篇文章,部分是作为一个指南,部分是作为一个练习,探索 LSTM 模型在比特币价格预测中的潜在用途。因此,我可能会跳过一些基本原理,因为这些很容易在其他地方找到。

免责声明:我的经验主要来自好奇心、个人和专业能力的实际应用,以及对有效市场的兴趣。这可能意味着使用的一些术语和方法可能与其他术语和方法不同。

LSTM 模式简介

LSTM 模式到底是什么?简而言之,这是一种能够学习长期依赖性的递归神经网络。与我们使用以前的经验来告知(最好是更好的)未来结果类似,LSTM 模型使用更新门和遗忘门来随机记住和忘记历史信息片段,以告知他们的预测。

像这样使用历史“背景”的能力使得这些模型在许多其他应用中特别适合于预测目的。

为了建立更深入的理解,请看一看这个伟大的资源,它深入研究了这种类型的神经网络的确切机制。

我们将使用什么工具

对于这个练习,我使用 NumpyPandas 来处理数据,使用 Keras / Tensorflow 来实现机器学习功能。为了调试和更好地呈现代码,我使用了 Jupyter 笔记本。

收集所需的数据

为了训练我们的模型,我们需要训练数据。任何金融定价数据在这里都足够了,只要它可以以分钟为间隔获得,并且大小合理。我使用的是大约 23 天的财务数据,时间间隔为一分钟。

我自己使用北海巨妖 API 收集了这些数据,所以它很可能包含缺口。但这只是为了举例说明。你可以在这里找到我的数据和完整的源代码。

数据准备

首先,我们从导入所有必需的包开始,加载数据集并删除我们不感兴趣的行。

我们将数据集分成训练集和测试集,并标准化其特征。标准化是很好的实践,因为它减少了在某些特征的方差可能高于其他特征的情况下的过度拟合。

LSTM 模型要求我们按块组织数据。我们的数据以一分钟为间隔进行分组,我们将使用 50 分钟的数据块来预测下一个数据块。

模型

现在是时候训练我们的模型了。我们选择我们想要使用的模型类型;在这种情况下是连续的,我们决定超参数。

我使用的模型相对简单,包含 5 个隐藏层,每个隐藏层有 50 个神经元,每个隐藏层之间有一个漏失。我们使用均方误差损失函数,Adam 优化器,将批量大小设置为 32,并遍历该网络 10 个时期。

决定超参数更多的是艺术而不是科学,值得测试出多种选择,以了解什么最适合您的测试数据和生产。优化你的超参数超出了本文的范围,但是网上有一些很好的资源。

现在我们有了一个可以用来建立预测的模型,我们可以看看它在测试数据中的表现。下图绘制了要价和出价的测试数据(y_test)预测(y_hat)。正如我们可以看到的,预测值与训练数据非常匹配。

酷!所以这意味着我现在可以预测比特币的价格了?

不,不是真的。不幸的是。

这些模型的预测能力绝对令人印象深刻,但如果你仔细观察预测,你会发现它与测试数据非常接近。这种预测似乎密切跟踪价格的变化,但往往不能正确预测这些变化发生时的价格。这对于任何你想用它的交易策略来说都不是非常有用。

预测未来

如果我们想预测 20 分钟后的价格,一种选择是在最近的历史数据间隔内运行模型,将预测连接到历史数据数组的末尾,然后将该数组反馈到模型中,继续这一过程,直到我们有 20 个预测的价格预测块。

下面的代码正是这样做的,并根据测试数据绘制了买价和卖价。不出所料,它并没有真正告诉你太多,这个特定的预测错误地预测了价格上涨。

但是对于剩余的测试数据来说,情况会是这样吗?由于测试数据由 6,661 行组成,我们有 133 个窗口可以预测交易。

让我们预测剩余的 20 分钟,并对 n=4 和 n=0 时的 ask_price 的累积差值求和。

下图显示了预测的有利可图的交易和实际交易的累积收益,其中一个人以 n=0 的要价买入,以 n=4 的要价卖出。

有趣的是,给你带来实际净收益的交易似乎超过了会导致亏损的交易。有了 100 美元的本金,你将获得 31 美元。仅仅过了 4.5 天。那可是天文数字的回报!

好得难以置信。

任何曾经在交易所购买 BTC 或任何金融工具的人现在都会告诉我,你实际上不能在市场上以要价卖出。买卖价差会阻止我们从价格上涨中获利,只有当资产的增值高于价差时,我们才能从交易中获利。

一旦我们把这个模型应用到我们的预测中,我们的金鹅开始失去一些羽毛。我们同样的 100 美元仍将升值约 3.40 美元!仍然是令人印象深刻的 1318%的年回报率。

现在让我们加入交易成本。我们将采取自由的假设,我们可以在市场上交易,只收取 0.1%的低费用——这是为交易量非常大的交易者保留的。

很快就会发现,我们的金鹅根本不是金子。我们的模型只能识别 3 个潜在的盈利交易,同样的 100 美元现在会变成 2.26 美元的亏损。

结论

上面的练习表明,简单的金融数据在预测价格的短期变化方面具有一定的预测能力,但是由于没有从这些信息中获利的实际机会,所以从交易的角度来看,这种特定的模型是相对无用的。

LSTM 模型等工具正变得越来越容易获得,大型团体和机构正在通过更好的数据和卓越的处理能力来拓展这些模型的能力。这导致市场将越来越多的信息整合到资产价格中,使得套利机会变得很少。

建立一个具有战胜市场的预测能力的模型不是一件简单的事情,如果有人能够轻松地使用这些工具赚钱,我肯定不会在这里与你分享。

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

使用机器学习预测 NBA 全明星赛,第 1 部分:数据收集

原文:https://towardsdatascience.com/using-machine-learning-to-predict-nba-all-stars-part-1-data-collection-9fb94d386530?source=collection_archive---------49-----------------------

一个端到端的深度调查,以定量研究 NBA 全明星选拔。

照片由 Abhishek Chandra 拍摄(Unsplash)

成为 NBA 全明星到底需要什么?作为一个长期的篮球迷,这是一个有趣且值得探究的问题。

本文将介绍我用来构建解决这个问题所需的历史数据集的各种数据搜集技术。

链接查看建模、预测和解释!

如果你想开门见山,跳过数据收集过程,直接去 第二部分:建模 看预测!

链接到回购!

点击 此处 查看我的 Github 上的完整回购(附自述)!

背景和动机

NBA 全明星赛是一年一度的表演赛,展示了联盟中的 24 名顶级球员。炫目而迷人,这一事件是全明星周末的最大亮点,一线名人经常成群结队地前来观看这一事件。

鉴于该联盟由全球排名前 450 位的球员组成,入选全明星是一项重大荣誉——许多球员甚至在被选中之前就为自己设定了一个雄心勃勃的目标。当我们在一个球员的职业生涯结束时评估他的遗产时,我们经常把他们的全明星选择带入谈话中,作为衡量他们伟大的一种方式。

考虑到这一点,除了潜在的工资奖金影响之外,人们还希望选拔系统是公平、公正的,并植根于某种客观性。然而,可悲的是,事实并非如此。

首发由球迷、球员和媒体投票决定,而主教练选择替补。这种主观过程尽最大努力选择在当前赛季有精英个人表现记录的候选人,但它也不是没有缺陷。像球队记录和社交媒体存在这样的事情会泄露到这个决定过程中,每年公布名单后,球迷们都会不可避免地抗议,抱怨他们最喜欢的球员被错误地冷落。

从本质上来说,我们可能会期望被选中的球员仅仅是大家一致认同的“联盟 24 名最佳球员”… 但是我们到底如何定义“最佳”?显然,像得分和篮板这样的比赛数据很重要,但是比赛场地并不总是公平的。对于任何一个长期的 NBA 球迷来说,球队排名在我们如何评价球员个人方面起着很大的作用,这不是什么秘密;很明显,如果你的球队发现自己排名垫底,就很难获得球迷和同行的尊重和认可。

这种对失败球队的负面偏见让许多想成为全明星的人无法入选,比如 2016-2017 赛季的卡尔-安东尼·唐斯,但这种影响很难量化描述。与好球队相比,差球队的球员的球门柱要窄多少?同样的担忧也出现在伤病上。错过延长时间的玩家将会发现很难得到一个位置,即使他们在实际比赛时给出了很高的数字。但是什么才算是“延长时间”?我们用来做这个决定的强制截止值到底在哪里?

为了试图揭开一些迷雾,客观地了解成为 NBA 全明星需要什么,我去挖掘相关数据,并应用机器学习技术来具体模拟这一选择过程。

蓝图

在机器学习中,有一个被称为“ 垃圾输入,垃圾输出” 的概念——意思是一个模型的输出只会和驱动它的数据一样好。如果我们想要准确而有洞察力的预测,我们必须确保我们提供的数据集是一致的、有用的,并且来源可靠。

我们将需要收集几个赛季的历史数据,但在我们拿出任何 Jupyter 笔记本之前,我们需要坐下来思考一下全明星选择中需要考虑的所有可能的特征。对于一个给定的玩家,什么信息可以帮助我们做出决定?

首先,我们需要传统的数据,比如得分,篮板,助攻等等。我们可以增加一些高级的统计数据,包括使用率、真实命中率、防守胜率和 PIE。PIE 代表玩家影响力评估器nba.com的 PER(玩家效率等级)版本,作为玩家表现的总括指标。

投票时的团队排名也很重要,所以我们也会用到它。对于受伤和错过的时间,我们可以比较个人和团队的比赛次数,并提取适当的百分比。

至关重要的是,我们只考虑前半个赛季。

声誉和粉丝偏爱是需要考虑的重要因素,但在这里很难实现。在 Instagram 和 Twitter 等平台上抓取玩家的社交媒体存在是(a)一个非常复杂的数据争论挑战,( b)由于社交媒体的疯狂变化,不可能可靠地向后扩展——此外,它直到 2010 年才真正存在。

在这个地方,我们可以用一个便宜的声誉代理:这个球员在职业生涯中有多少次入选全明星赛。它将与年轻和未经证实的球员斗争,但似乎这个功能可以做足够的工作来获得球迷和媒体的长期欢迎。

我们的目标是构建一个数据集,包含自 1996 年以来每个赛季的每个 NBA 球员的所有这些特征(最早的赛季是在stats.nba.com追踪的)。至关重要的是,我们只考虑赛季的前半段,因为这是挑选球员的时候,之后的一切都无关紧要。我选择每年的 1 月 21 日作为截止日期,因为这大概是开始做出选择决定的时间。

我们的输出标签将简单地表示该球员是否被选中(1)参加那年的全明星赛(0)。这是我们的基本事实。尽管 2020 年 ASG 已经发生,并且名单也早已确定,但我将从标记的数据集中忽略当前赛季(2019-20),以便我们可以稍后运行模型,并将预测与已知结果进行比较。

一旦我们有了一个满意的模型,我们就可以窥视黑盒子,定量地探索全明星标准的内部运作,以及它们之间到底是如何相互作用的,希望能够平息我们的好奇心。

构建数据集

获取数据

Stats.nba.com 的将是我们满足需求的首要资源。它支持在自定义日期范围内查询统计数据的功能,这对于我们想要做的事情是必不可少的。他们的网页本质上是动态的(AJAX ),所以从他们那里抓取并不简单——稍后会有更多的介绍。

来自这个资源的数据可以追溯到 1996-97 赛季,为我们的模型提供了一个不错的训练数据池。

例如,来自两个不同时代的 NBA 观众可能对防守有不同的侧重。因此,1996-1997 年的临界值在这里是完全可以接受的。

2018-2019 赛季前半段球员的“传统”表。在这里链接到表

传统高级、防御牌桌之间,我们可以得到我们所需要的大部分。接下来是团队排名和团队游戏(我们需要设计游戏百分比)。我决定使用团队会议排名而不是总排名,因为出于讨论的目的,会议内部排名通常是人们最关心的。所有这些都可以在团队表中找到。

2018-2019 赛季中途的东部“球队”表。在此链接到表格

请注意,在球员表中,球队以他们的简写符号(例如,CHI)列出,但是在球队表中使用全名(例如,芝加哥公牛)。当我们将这些合并在一起时,我们需要创建某种查找结构来链接它们。

剩下的就是球员的全明星选择历史,这是我们的输出标签和“之前全明星选择的数量”功能所需要的。幸运的是,这不依赖于赛季中的任何日期过滤器——所以我们可以使用另一个很好的篮球统计资源:篮球参考 这些网页是静态的 HTML,这意味着从这些网页中抓取会更加轻量和简单。

2015 年 ASG 花名册(东部),也列出了入选但因伤不能参赛的球员。在此链接到表格

这里值得注意的是,我们不应该忽视那些入选全明星但由于受伤而无法参加的球员。即使他们没有正式占据一个花名册的位置,他们最初被选中,并将在整个分析中被视为全明星,以及他们的替代者。

随着游戏的发展,联赛节奏在过去几年里稳步增长,所以我们将通过除以节奏来标准化适用的游戏统计数据。我们可以很容易地从篮球参考资料中得到每年的联盟平均速度。

由于停摆,1999 年没有全明星赛,我们在构建数据集时将完全跳过 1998-1999 赛季。

抓取网页:美丽的 Soup 和硒

由于位于 Basketball Reference 的网页是静态的(意味着内容直接嵌入到其 HTML 中),我们可以简单地使用requests库来加载 HTML 文档。

然后我们将使用BeautifulSoup来解析文档,并有选择地提取我们感兴趣的数据。使用pd.read_html( … )方法,我们可以将数据加载到一个熟悉的熊猫数据框架中,然后我们开始比赛。

即使我们的标签数据集将从 1996-1997 赛季开始,我们也需要比这更早的全明星阵容数据。例如,哈基姆·奥拉朱旺在 96 年是一名活跃的球员,但他的第一次全明星选择是在 85 年。我们用 1970 年来初始化我们的 ASG 刮擦,以确保我们绝对没有错过任何人的机会。

我们在这里构建的数据结构将是一个集合字典,将球员姓名映射到他们入选全明星的所有年份的集合。我们pickle这个字典(序列化它)并导出它以备将来调用。

对于那些好奇的人来说,下面是实现这一切的代码:

用于从篮球参考中抓取历史全明星出场数据的脚本。

此导出数据结构的预览。

下一步,我们需要从 stats.nba.com 收集表格数据,这是事情变得有点复杂的地方。这些网页有一个使用 AJAX (异步 JavaScript 和 XML)的动态实现,所以显示的表格在 HTML 文档中是找不到的。

相反,网页会向 API 端点发送一个请求(附带数据过滤器),并返回一个 JSON 响应。我们必须经历识别相关端点的过程,然后自己手动管理数据。这将变得令人难以置信的乏味,因为现在我们讨论的是在给定一个日期范围内的每个玩家的高级统计数据,而不是给定一大堆盒子分数。

所以我们有两个真正的选择:

  • 使用一个预建的实用程序来做这件事,像[nba-api](https://pypi.org/project/nba-api/) 阅读所有的文档来学习功能
  • 使用自动化浏览器软件,如 Selenium WebDriver

尽管这是一种更暴力的方法,但为了节省时间,我还是选择了使用 Selenium。

使用 Selenium ChromeDriver 进行网页抓取。

Selenium 是一个允许我们调用自动化浏览器(WebDriver)的框架,使用 Python 中的 API,我们可以控制这个浏览器导航到相关网页。经过一段延迟时间后,一旦从后端收到响应,我们就可以最终加载 HTML 文档(包含重要数据)并像以前一样使用BeautifulSoupPandas

在这一过程中,我还遇到了一些困难,但大多数问题都与保管有关(就像夏洛特黄蜂队在前山猫队时代被称为 CHH,后来被称为查),这些问题都通过评论得到了解释。

剧本将从 1996 年到今天的每个赛季为每个 NBA 球员刮stats.nba.com。数据集经过适当的处理和合并,然后使用我们之前创建的全明星查找字典进行扩充。两个数据集以 csv 格式导出,一个包含从 1996-97 赛季到 2018-19 赛季的所有带标签的数据,另一个包含当前赛季的未带标签的数据

超过 200 行,这个脚本有点太长了,不能在这里发布,但是可以在回购的这里找到。

下面是这两个数据集的前几行,其中许多要素隐藏在视图之外:

带标签的数据集(9595 条记录),为了适合此处,许多要素隐藏在视图之外。

2019–2020 年的未标记数据集(486 条记录)。

原始特征的完整列表:

  • 年份(季节开始的日历年)
  • 平均值。速度(该赛季联盟平均速度)
  • 运动员
  • 团队会议排名
  • GP(玩过的游戏)
  • 团队 GP(团队参加的比赛)
  • w(赢的游戏-玩家必须玩过)
  • 每场比赛得分
  • 每场篮板数
  • AST(场均助攻数)
  • 每场比赛抢断次数
  • BLK(每场比赛盖帽数)
  • 每场比赛失误数
  • TS%(真实拍摄百分比)
  • 下午 3 点(每场比赛 3 分)
  • 防御赢股
  • USG%(使用率)
  • 玩家影响力评估器
  • 之前在 ASG 出现过
  • 和去年一样?

这些游戏统计的正式定义可以在 https://stats.nba.com/help/glossary/找到。

接下来…

在本次深度探讨的第二部分中,我们将使用我们在此构建的数据集来研究模拟NBA 全明星选拔流程。感谢阅读!

使用机器学习预测 NBA 全明星赛,第 2 部分:建模

原文:https://towardsdatascience.com/using-machine-learning-to-predict-nba-all-stars-part-2-modelling-a66e6b534998?source=collection_archive---------43-----------------------

一个端到端的深度调查,以定量研究 NBA 全明星选拔。

照片由科利昂·布朗(Unsplash)拍摄

回顾

这是由两部分组成的系列文章的第 2 部分,我们的目标是将数据科学方法应用于 NBA 全明星选拔流程。在第一部分中,我们使用BeautifulSoupSelenium使用网络搜集技术来收集这些数据。

在这一部分,我们将探索模型的构建、评估和解释。虽然建立一个准确而精确的模型是我们的主要任务,但是我们最终还是希望能够从系统中获得某种程度的可解释性,也就是说看到它所看到的

我将在本文中介绍许多关键概念,但为了简洁起见,我不会覆盖每一行代码。完整的笔记本(有详细的评论)可以在我的 GitHub 上的这里找到。

预处理

处理异常值

我们的首要任务是解决训练数据中的统计异常值。在这种情况下,一个离群值是任何球员的选择决定不被他们在那个赛季的表现所证明,经常导致球迷和媒体的广泛反对。

如果留在训练集中,这些记录可能会混淆模型,并对我们的预测和洞察力产生负面影响。

这些玩家分为以下两类(或两类都有):

答:这位球员有着漫长而辉煌的职业生涯,在暮年被选为告别球员,尽管他没有达到全明星级别(例如,德克·诺维茨基入选 2019 年 ASG,这是一个非常尊重/致敬的选择)

B: 该球员在赛季中缺席了太多比赛,只是因为压倒性的球迷偏爱才被选中(例如,姚明在 2010-2011 赛季,在可能的 44 场比赛中只参加了 5 场)

训练数据中符合这些标准中的任何一个的全明星(由异常低的 PIE 分数或所玩游戏的数量来表示)被从训练集中清除。由于数据集非常小,这是我们可以手动执行的事情——否则我们将求助于类似于 Tukey 异常值检测的东西。

总共, 13 个球员赛季被取消——完整的名单(包括理由)可以在笔记本中找到

调整游戏统计数据

本质上,这不是一个时间序列预测问题,但我们仍然有一些不随时间静止的特征。如果任其发展,这有可能扭曲模型并导致更差的结果。

平均速度由球队每场比赛使用的控球次数来定义,这在不同的 NBA 时代有所不同(例如,04 年的 90 比 18 年的 100)。因此,2018 年场均 30 分的球员应该和 2004 年场均 27 分的球员保持同样的标准。

因此,我们将所有速度相关的特征(得分、篮板、助攻、抢断、盖帽、失误、3pm)除以该赛季的联盟平均速度,以纠正这一问题。这样,我们就不会拿苹果和橘子做比较了。

在这一点上,我们创建了 Play Pct。特征,通过将个人在赛季中参加的比赛除以团队的比赛。我们还删除了赛季中少于 7 场比赛的所有记录,因为这些会导致其他比赛数据的极端值,可能会破坏模型。

我们现在有 15 个候选特征用于建模。作为一个剧透,并不是所有这 15 个都被证明是有帮助的预测者,但为了完整起见,我会把它们都列在这里。未绑定的特性最终在最终模型中被删除。

  • 调整后的分数(调整后的点数)
  • 调整后的篮板球
  • 调整后的 AST(调整后的助攻)
  • 调整后的 STL(调整后的抢断)—丢弃
  • 调整后的 BLK(调整后的块)
  • 调整后的 TOV(调整后的失误)—下降
  • 调整后的下午 3 点(调整后的 3 个指针)—丢弃
  • DEFWS(防御性赢股)
  • TS%(真实投篮命中率)—下降
  • USG%(使用率)
  • PIE(玩家影响估计器)
  • 打百分之。
  • 团队会议排名
  • ASG 之前的亮相
  • 和去年一样?—已丢弃

探索性数据分析

在我们开始将机器学习算法应用于这个问题之前,了解我们的数据很重要。这包括比较全明星和非全明星之间的属性分布,以及查看不同变量如何相互作用。

为了孤立地查看每个特性,我们可以使用来自seaborn库中的violinplot方法。我将展示 15 个图中的 2 个,以及用于生成它们的代码。

为每个要素生成分类分隔的小提琴图。

这里最有趣的是团队等级绝对重要。入选全明星赛的球员更有可能来自一支好球队,而不是一支差球队。

理论上,这似乎是合理的。一个“明星”球员应该能够将他们的天赋转化为团队的成功,但这种思路可能会让我们对一个球员过于挑剔,因为有些事情超出了他们的控制,比如队友和教练。如何处理个人和团队成功的分歧是 NBA 球迷争论的话题,尤其是在试图就联盟 MVP 达成一致的时候。

稍后,我们将进一步探讨这一点。

我们可以使用成对散布矩阵来查看我们的特征如何相互作用。为此,我们使用pairplot

最后,我们可以使用主成分分析来粗略地看看我们的两个类实际上有多不同。我们应用主成分分析将特征空间的维数从 15 降低到 2,同时保留尽可能多的信息和可变性。绘制这两个主要组成部分揭示了我们数据中的一些内在结构和类别分离。

我们使用来自sklearnpca方法来实现这一点。为了减少主成分的随意性,我们将输入特征缩放到单位方差并移除平均值。

仅使用两个主成分,我们仍然能够保留来自 15 个原始特征的总可变性的一半以上。从这个二维投影来看,这两个类别似乎彼此合理地分开了。

为了澄清,我们在这里仅使用 PCA 进行数据可视化——我们不会使用这些主要成分进行建模。PCA 可以用于提高模型训练速度,因为特征较少,但由于我们的数据集相对较小,训练运行时间在这里不是问题。

系统模型化

对标记数据集进行分区

我们将标记的数据集分成 3 组:训练、验证和测试。

模型将直接使用训练数据来调整其内部参数。验证数据将用于指导该过程,监控学习过程,并帮助避免过度拟合。然后,一旦模型被冻结,我们可以转向测试数据来评估模型并计算不同的基准。

关于 SMOTE 的快速注释

在处理任何分类问题时,我们必须确保我们的类是平衡的。如果我们不考虑阶级的不平衡,这个模型将会对大多数阶级产生固有的偏见——这是我们绝对不想看到的。

在我们数据集的训练分区中,我们有 368 名全明星5409 名非全明星。随之而来的是,给每个球员贴上非全明星标签的粗略的一揽子策略仍然可以达到 93.6% 的准确率。很明显,我们想劝阻模型不要这样做,所以我们应该平衡这两个类。

为此,我们有三个选择:

  • 欠采样多数类(丢弃数据点)
  • 对少数民族类进行过采样(创建合成数据点)
  • 欠采样和过采样的组合

就性能而言,这些技术实际上没有太大区别,但过采样是实践中最常见的方法。

对于这种情况,我们将采用一种叫做 SMOTE 的方法——合成少数过采样技术。SMOTE 通过在随机数据点和邻近数据点之间进行线性插值,在一个类内创建合成数据点。重复这一过程,直到类别达到平衡。实际上,我们将使用 SMOTE 的一个变体,称为 Borderline SMOTE,它专注于类边界附近的过采样数据点。

这将有望使模型更精确地辨别边缘情况。

至关重要的是,我们只对训练数据进行过采样,而不去碰验证和测试集。分割之前的过采样将导致信息在我们的三个分区之间泄漏,并导致模型具有夸大的准确性指标。

调整 XGBoost 超参数

对于这个监督学习问题,我们将调用的机器学习算法是 XGBoost,它是梯度推进决策树的一种实现。对于涉及非结构化数据的数据集,人工神经网络通常会优于其他算法。但是对于包含表格数据的较小数据集(就像这个),像 XGBoost 这样的决策树框架通常占主导地位。XGBoost 的文档可以在这里找到。

与许多其他 MLA 一样,XGBoost 需要几个不同的超参数来控制学习过程,如学习速率、最大树深度和树的数量。我们将应用一个简单的网格搜索来找到最优的学习率,并使用提前停止轮次来对抗过度拟合。对于这个问题,我们将耐心参数设置为 10。如果验证 AUC 分数达到 10 个周期而没有改善,我们终止该学习率的训练并记录最高 AUC。

AUC 是二元分类问题的综合性能度量。AUC 代表曲线下面积,特别是 ROC (受试者操作特征)曲线。ROC 曲线比较了不同阈值设置下的 TPR(真阳性率)和 FPR(假阳性率),说明了分类器在不同区分水平下的诊断能力。

AUC 可以采用[0,1]范围内的值。AUC 为 1 表示该模型能够很好地区分阳性和阴性类别,而 AUC 为 0.5 是我们从随机猜测的模型中预期的值,它没有区分能力。AUC 为 0 意味着模型一直在反转类别(完全区分,但方向错误)。

我们希望我们的模型实现尽可能接近 1 的 AUC。

模型性能和评估

该模型在对训练集中的数据点进行分类方面做得更好,因为这是直接从中学习的数据。通过验证性能,可以了解模型在处理以前从未见过的数据时表现如何。

然而,验证集仍然影响我们在模型调整过程中所做的决定(通过早期停止回合)。当我们计算 F1 分数等分类指标时,我们希望使用在训练期间完全隔离的数据,这就是测试集发挥作用的地方。这些指标将代表模型如何推广到“野生”数据。

在测试数据集上运行该模型,我们获得以下混淆矩阵、ROC 曲线和分类度量:

蓝色虚线表示预测能力为零(随机猜测)的模型的预期性能。红色曲线说明了我们调优的 XGB 模型在不同阈值水平下的诊断能力。由于我们的 AUC 非常接近 1,我们的模型在区分这两个类别方面非常强大。

指标:

  • 精度: 0.975151
  • 精度: 0.784314
  • 召回: 0.842105
  • F1 得分: 0.812183
  • 日志。损失: 0.103545
  • ROC AUC:0.989187

这些指标是使用二进制分类阈值 0.835 计算的(针对最高 F1 分数进行了优化)。

2019-2020 赛季全明星预测

现在我们有了一个工作模型,我们可以将它应用于当前赛季的球员数据集。这里使用的分类模式略有不同——我们将从每场会议中选择 12 名预测当选概率最高的球员,而不是使用我们的静态阈值 0.835。

小细节:实际上,这 12 个花名册位置的细分是有限制的:6 个前场球员(F/C),4 个后卫(G),2 个通配符。然而,有了这两个通配符,再加上不断增加的球员位置之间的流动性,这个限制是相当不重要的。安排 12 名球员扮演这些角色几乎总是可能的。

事不宜迟,以下是预测:

东部联盟:

西部联盟:

我们做得怎么样?

由于 2020 年全明星赛早已过去,我们可以看看我们有多少选择是正确的。

在每次会议中,我们有 11 个选择是正确的,一个选择是错误的。

在东部,我们的模型错误地选择了扎克·拉文作为全明星球员,这将是他有史以来的第一次选择。相反,特蕾·杨被选中了。有趣的是,我们的模型真的不喜欢 Trae 被选中的前景,他在会议中排名第 21 位,甚至低于托拜厄斯·哈里斯和尼古拉·武切维奇。我们将在下一节研究这个问题。

看看西方的预测,这个模型选择了保罗·乔治作为花名册上的一个位置,却错过了克里斯·保罗。实际上,如果我们看一下克里斯·保罗的预计概率,模型 真的 没有想到他会被选中。同样,我们将在下一步探讨这些决策。

所以总的来说,我们得到了正确的 22/24 选秀权,这对 91.67% 有利。不可怕,考虑到我们没有纳入社交媒体指标或球员对那个赛季的叙述。

模型解释

我们有一个满意的模型,它似乎能做出相当准确的预测。但到目前为止,模型本身相当不透明。我们可以观察它的最终输出概率,但是这个模型实际上是如何工作的呢?哪些特性在决定输出方面起着最大的作用?为什么模特不喜欢克里斯·保罗和特蕾·杨的全明星前景?

本质上,有些模型比其他模型更容易直接解释。例如,对于线性回归模型,每个特性的影响由其 beta 系数表示。但是对于更复杂的模型,比如 XGBoost,就没有这么简单了。

幸运的是,已经有成熟的技术来评估每个特性在这些模型中的影响。

这里我们将使用的是 SHAP 值。SHAP(SHapley Additive explaints)是“一种解释任何机器学习模型输出的博弈论方法”。我们将用 PDP(部分相关性图)来补充这一分析,以了解当我们保持其他输入不变时,不同的特征究竟如何改变预测。

毫不奇怪,(玩家影响评估者)的分数对预测有最大的平均影响。得分紧随其后,这正说明了得分对于在 NBA 获得“明星”地位是多么重要。

但是这里真正有趣的是团队会议等级特性。对于 NBA 球迷来说,众所周知,一个球员在团队中的成功在他们的印象中扮演着重要的角色,尤其是当他们和其他球员比较时。如果球员 X 和球员 Y 都有相同的数据,但是 X 的球队在联盟中排名第一,而 Y 的球队排名第 13,那么很难证明 Y 是更好的球员。显然这是一个广泛的概括,但这一点仍然成立。

最吸引人的部分(至少对我来说)是这个特征在决定全明星地位上的绝对份量。也许我们不应该谴责表现不佳的球队中的明星球员,就像我们谴责他们希望为更好的球队效力一样。对粉丝的忠诚是重要的,但这真的应该优先于一个球员努力争取他们无可否认应得的认可吗?

这一发现(以及其他发现)在 SHAP 细节和部分依赖图中得到了进一步的支持。

测试数据集中的每个玩家在图中的每一行添加一个点。圆点的颜色表示该玩家的属性是高还是低,横向位置表示该属性值对预测概率的影响程度。

PIE 得分在 10 到 13 之间的模型影响增加非常显著。这个范围可以说是成为全明星的一个“驼峰”。

随着球队排名越来越差,全明星入选概率很快就会减少。

颜色代表基于饼图和团队会议等级交互的模型影响。

上面的情节真的很能说明问题。控制所有其他特征,该模型将以大致相同的方式处理以下两个玩家:

  • 馅饼值为 20 的玩家(例如卢卡·东契奇),团队排名为 15
  • 一个馅饼值为 11(例如德文特·格雷厄姆)而团队排名为 1 的玩家

当然,这两个特性并不是完全独立的;举起更高的馅饼应该有助于你的球队攀升排名,我认为这是选民和记者在忽视糟糕球队的球员时下意识坚持的逻辑——“如果他们真的是明星,他们的球队就不会在第 15 名。”我认为这个论点是正确的,但它有可能被过度应用。最近记忆中的许多冷嘲热讽都属于这一类,一个主要的例子是过去几年的德文·布克。(是的,他参加了今年的比赛,但最初没有入选——只是作为伤病替补。)

回到我们的模型,这就是为什么 Trae Young 在我们的东部联盟预测中排名如此不利。亚特兰大老鹰队在东部排名倒数第一,历史上像这样的垫底球员很少出现全明星。

然而,Trae Young 是这个模型没有考虑到的一个非常独特的例子。老鹰队在赛季前半段饱受伤病困扰,约翰·科林斯因 PED 禁赛 25 场也于事无补。所有这些都加上一个潜在的事实,老鹰队是一支年轻,缺乏经验的球队,压倒性的失败似乎是不可避免的。用联盟中几乎任何一个非一线队的球星来交换特蕾·杨,他们都不太可能做得更好。

Trae Young 今年的全明星选择得到了几乎一致的认可,但出于上述原因,这就是为什么模特错过了他。

如果一名球员在赛季中长时间缺阵,他们被选中的可能性开始下降。

特定玩家的 SHAP 力情节

我们可以进一步扩展 SHAP 分析,研究特定玩家的模型决策。通过使用 force plots,我们可以为联盟中的每个球员生成个性化的成绩单,概述他们赛季的哪些方面帮助或阻碍了他们的全明星概率。

这些是最有见地的边缘案例——在全明星边界附近的球员,他们有可能以任何一种方式做出案例。

菲尼克斯太阳队在联盟中排名第 11 位,这对德文布克的模型输出产生了重大的不利影响。

探索克里斯·保罗的独特情况

不可否认,克里斯·保罗是一名伟大的球员。虽然他已经接近职业生涯的暮年,但他仍然不断地为他适合的任何球队增加重要的价值。许多分析家将保罗列入了西部全明星预备队的候选名单,而他们是对的——他得到了一个当之无愧的位置。但他的表现和价值在历史上很难跨越到统计表上——所以,在纸面上,他的数据通常不会非常令人印象深刻。因为“数字”几乎是我们的模型所看到的全部,所以他的选择概率很低也就不足为奇了。

此外,本赛季围绕克里斯·保罗有一个引人注目的故事。当他在 7 月份从休斯顿火箭队被交易到 OKC 雷霆队以换取拉塞尔·维斯特布鲁克时,每个人都立即否定了雷霆队的这个赛季。人们预计雷霆队将进入一个重建和重组的时代,并将在接下来的几年里萎靡不振。

但这并没有发生。相反,克里斯·保罗带领雷霆成为了臭名昭著的西部联盟中最好的球队之一,并得到了广泛的赞誉。当然,整个团队和组织都应该为这一成就而受到赞扬,但保罗无疑在翻转雷霆队本赛季的剧本中扮演了首席指挥官的角色。

我们在这里开发的预测模型显然无法捕捉像这样的失败者的叙述。虽然该模型通常是准确的,但重要的是要意识到它的缺点,并找出哪里可能出错。

总之…

这整个项目仅仅源于好奇。作为一个长期的 NBA 球迷,宣布全明星阵容是我每个赛季都期待的事情。但是这些年来,一些精选(和非精选)让我挠头,想要答案。这种好奇心最终达到了一个临界点,于是我决定将这个问题框定为一个数据科学问题,并揭示一些情况。

这个项目对我个人来说是一次很好的学习经历,尤其是在模型解释和可解释性方面。应用技术将“黑盒”模型分解成更有意义和更容易理解的东西是一项非常有价值的努力,并真正丰富了它所提供的见解。从动态网页中抓取数据也是一个有趣的挑战。

感谢您花时间阅读这篇文章,并跟随我从头到尾研究这个问题。如果您对这个项目有任何问题,或者对潜在的改进有任何建议,请在评论部分告诉我-我总是渴望拓宽我的数据科学技能集,探索新的想法。

使用机器学习预测莱茵河水位

原文:https://towardsdatascience.com/using-machine-learning-to-predict-rhine-water-levels-44afce697074?source=collection_archive---------11-----------------------

LSTM 模型如何极大地改善了我的预测

长短期记忆(LSTM)模型是一种功能强大的神经网络,非常适合预测与时间相关的数据。莱茵河的水位正好属于这一类:它们随着时间的推移而变化,取决于一系列变量,如雨水、温度和阿尔卑斯山的积雪。

莱茵河是欧洲的命脉。几个世纪以来,它一直是将货物运往德国、法国、瑞士和中欧的主要通道。然而,随着气候变化,河流水位可能会变得更加多变。因此,准确预测河流水位是从航运公司到大宗商品交易商和工业集团等一系列行为者的首要关切。

一艘满载煤炭的驳船在莱茵河航行(来源:https://commons . wikimedia . org/wiki/File:Coal _ barge _ Chilandia _ on _ Rhine - looking _ south . jpg

与经典的基于回归的模型不同,LSTMs 能够捕捉不同变量之间的非线性关系;更准确地说,这些变量之间的序列相关性。这篇博客关注的是使用 LSTMs 预测莱茵河的问题,而不是这些模型背后的理论。

眼前的问题

我们在这里寻求解决的问题如下:我们希望尽可能精确地预测德国西部关键阻塞点 Kaub 的第二天水位。

我们有从 2000 年 1 月 2 日到 2020 年 7 月 27 日的历史每日数据,相当于 7513 次观测。数据集包括 15 个不同的类别,显示为列:

  • “日期”:观察的日期
  • “Kaub”:Kaub 水位每天的差异,以厘米为单位——这是我们试图预测的“y”值(来源:WSV)
  • 莱茵费尔登:瑞士莱茵费尔登的水流量的绝对值,单位为立方米每秒(来源:符拔)
  • “Domat”:靠近莱茵河源头的 Domat 的水流量的绝对值,单位为立方米每秒(来源:符拔)
  • “precip_middle”:莱茵河沿岸 20 个气象站记录的平均日降雨量,单位为毫米(来源:DWD)
  • “avgtemp_middle”:在相同站点记录的平均温度,单位为摄氏度
  • “最高温度 _ 中间值”:在相同站点记录的最高温度
  • “mintemp_middle”:相同站点记录的最低温度
  • “precip_main”:莱茵河主要支流美因河沿岸 8 个气象站记录的平均日降雨量,单位为毫米(来源:DWD)
  • “avgtemp_main”:在相同站点记录的平均温度,单位为摄氏度
  • “maxtemp_main”:在相同站点记录的最高温度
  • “mintemp_main”:在相同站点记录的最低温度
  • precip _ neck ar:neck ar 沿岸 7 个气象站记录的平均日降雨量,也是莱茵河的主要支流,单位为毫米(来源:DWD)
  • “avgtemp_neckar”:在相同站点记录的平均温度,单位为摄氏度
  • “maxtemp_neckar”:在相同站点记录的最高温度
  • “mintemp_neckar”:在相同站点记录的最低温度

请注意,变量的选择完全是我的,是基于我处理莱茵分析的经验。无论是使用经典回归模型还是神经网络,选择正确的输入是时间序列分析中最重要的步骤之一。如果选择的变量太少,模型可能无法捕捉数据的全部复杂性(这称为欠拟合)。相比之下,如果选择太多输入,模型很可能会过度适应训练集。这很糟糕,因为这可能意味着模型很难归纳到一个新的数据集,而这个数据集对于预测是必不可少的。

首先,让我们加载本练习所需的所有库:

import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import joblib

下面是数据集前几行的示例。我们可以通过熊猫图书馆轻松加载它:

# first, we import data from excel using the read_excel function
df = pd.read_excel('RhineLSTM.xlsx')
# then, we set the date of the observation as the index
df.set_index('date', inplace=True)
df.head()

加载后,我们可以使用 Matplotlib 库绘制数据集:

# specify columns to plot
columns = [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
i = 1
values = df.values# define figure object and size
plt.figure(figsize=(9,40))
# plot each column with a for loop
for variable in columns:
     plt.subplot(len(columns), 1, i)
     plt.plot(values[:, variable])
     plt.title(df.columns[variable], y=0.5, loc='right')
     i += 1
plt.show()

绘制变量直方图通常也是一个好主意:

# histograms of the variables
df.hist(figsize=(9,18))
plt.show()

使用 Seaborn 库,您可以创建一个 violin 图来了解每个变量的分布:

# calculate dataset mean and standard deviation
mean = df.mean()
std = df.std()
# normalise dataset with previously calculated values
df_std = (df - mean) / std
# create violin plot
df_std = df_std.melt(var_name='Column', value_name='Normalised')
plt.figure(figsize=(12, 6))
ax = sns.violinplot(x='Column', y='Normalised', data=df_std)
_ = ax.set_xticklabels(df.keys(), rotation=90)

评估不同的模型

我在莱茵数据集上训练了不同类型的模型,以确定哪一个最适合:

  • 基线模型,也称为持久性模型,返回 Kaub 水位的当前变化率作为预测(本质上预测“无变化”)。这是一个合理的基线,因为莱茵河水位通常会因更广泛的天气现象而在几天内发生变化(例如,阿尔卑斯山的缓慢融化逐渐向下游移动)。
  • 您可以训练的最简单模型假定输入变量和预测输出之间存在线性关系。与更复杂的模型相比,它的主要优点是易于解释,但是它的性能只比基线网络稍好。
  • 密集网络更强大,但看不到输入变量如何随时间变化。多步密集和卷积神经网络解决了这一缺点,它采用多个时间步作为每次预测的输入。
  • LSTM 模型名列前茅,在验证和测试集上具有较低的平均绝对错误率。

创建这个图表所需的 Python 代码对于这个博客来说太长了,但是你可以在这里访问它,应用于不同的数据集。

LSTM 回归模型

LSTM 网络是一种可以学习长数据序列的递归神经网络。它们不是神经元,而是由通过层相互连接的记忆块组成。内存块包含管理其状态和输出的门(输入、遗忘、输出),使其比典型的神经元更聪明。

它们在学术界被广泛用于预测河流高度,并被证明在某些情况下优于经典水文模型。

让我们从本文开头的代码重新开始加载 Rhine 数据库:

import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import joblib# first, we import data from excel using the read_excel function
df = pd.read_excel('RhineLSTM.xlsx', sheet_name='Detailed4_MAIN’)
# then, we set the date of the observation as the index
df.set_index('date', inplace=True)

数据准备

要构建正常运行的 LSTM 网络,第一步(也是最困难的一步)是准备数据。

我们将把问题框定为根据今天和前 6 天的天气和瑞士上游流量(backward_steps = 7)预测今天 Kaub 水位(t)的变化率。

使用 Scikit-Learn 库中的 StandardScaler()函数对数据集进行标准化。对于数据帧的每一列,该列中的每个值减去平均值,然后除以整列的标准偏差。对于大多数机器学习模型来说,这是一个非常普通的步骤,并允许整个网络更快地学习(下面将详细介绍)。

然后,数据帧通过一个转换函数。对于每一列,我们为前 7 天的值创建一个副本(15 * 7 = 120 列)。得到的数据帧的形状是 7506 行×120 列。

很多代码都是受这篇精彩的博客文章的启发。

# load dataset
values = df.values
# ensure all data is float
values = values.astype('float32')
# normalise each feature variable using Scikit-Learn
scaler = StandardScaler()
scaled = scaler.fit_transform(values)
# save scaler for later use
joblib.dump(scaler, 'scaler.gz')# specify the number of lagged steps and features
backward_steps = 7
n_features = df.shape[1]# convert series to supervised learning
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
     n_vars = 1 if type(data) is list else data.shape[1]
     df = pd.DataFrame(data)
     cols, names = list(), list()
     # input sequence (t-n, ... t-1)
     for i in range(n_in, 0, -1):
          cols.append(df.shift(i))
          names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
     # forecast sequence (t, t+1, ... t+n)
     for i in range(0, n_out):
          cols.append(df.shift(-i))
          if i == 0:
                names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
          else:
               names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
      # put it all together
      agg = pd.concat(cols, axis=1)
      agg.columns = names
      # drop rows with NaN values
      if dropnan:
          agg.dropna(inplace=True)
      return agg# frame as supervised learning
reframed = series_to_supervised(scaled, backward_steps, 1)

定义训练和测试数据集

我们必须将准备好的数据帧分成训练和测试数据集,以便对我们的结果进行公正的评估。训练数据集代表 80%的值,我们将使用剩余的 20%进行评估。当我们处理按时间顺序排列的数据时,打乱数据集的顺序是一个非常糟糕的主意,所以我们让它保持原样。接下来,我们将训练和测试数据集重塑为三维,以备后用。

# split into train and test sets
values = reframed.values
threshold = int(0.8 * len(reframed))
train = values[:threshold, :]
test = values[threshold:, :]
# split into input and outputs
n_obs = backward_steps * n_features
train_X, train_y = train[:, :n_obs], train[:, -n_features]
test_X, test_y = test[:, :n_obs], test[:, -n_features]
print(train_X.shape, len(train_X), train_y.shape)
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], backward_steps, n_features))
test_X = test_X.reshape((test_X.shape[0], backward_steps, n_features))
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)

拟合模型

最后,我们能够适应我们的 LSTM 网络。多亏了 TensorFlow/Keras 库,这只需要几行代码。我选择了 64 个内存块,批量大小为 72。我使用 Adam 优化算法,它比经典的梯度下降法更有效。

# design network
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.LSTM(64, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(tf.keras.layers.Dense(1))
model.compile(loss='mae', optimizer='adam')
# define early stopping parameter
callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)
# fit network
history = model.fit(train_X, train_y, epochs=25, callbacks=[callback], batch_size=72, validation_data=(test_X, test_y), verbose=2, shuffle=False)
# plot history
plt.figure(figsize=(12, 6))
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.ylabel('mean absolute error [Kaub, normalised]')
plt.legend()
plt.show()

模型结果

模型拟合后,我们可以启动预测并反转比例以获得最终结果。然后,我们可以计算模型的误差分数。在这里,该模型实现了 6.2 厘米的均方根误差(RMSE),这是很好的,但可能还可以改进。

# make a prediction
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], backward_steps*n_features))
# invert scaling for forecast
inv_yhat = np.concatenate((yhat, test_X[:, -(n_features - 1):]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
# invert scaling for actual
test_y = test_y.reshape((len(test_y), 1))
inv_y = np.concatenate((test_y, test_X[:, -(n_features - 1):]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
# calculate RMSE
rmse = np.sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)

我还检查了该模型在极端天气事件中的性能,例如 2016 年 5 月至 6 月在欧洲记录的大范围洪水,仅在巴伐利亚就造成了超过 10 亿欧元的损失。该模型通常能够跟踪水位的上升,但是在两个特定的高峰期间(一个在 4 月中旬,另一个在 6 月初),它给出的数字很低。

经过训练的 LSTM 网络在 2019 年 5 月的阿克塞尔风暴期间也表现良好,该风暴导致连续两天水位高度快速上升超过 1 米。然而,当洪水达到峰值时,它再次给出了比实际数字略低的估计。

在这两种情况下,我怀疑低的数字是由于一个主要的莱茵河支流,摩泽尔河,它被排除在分析之外,因为缺乏可靠的气象数据。摩泽尔河在考布以北的科布伦茨与莱茵河汇合。这是未来可能改进的领域。

模型性能

寻找最佳超参数是机器学习中最重要(也是最耗时)的任务之一。为了找到最佳设置,我运行了许多不同版本的网络:

  • 预处理:对于那些熟悉 Scikit-Learn 的人来说,我发现 StandardScaler()比 MinMaxScaler()更适合莱茵数据集,后者将所有值归一化到 0 到 1 之间。使用 MinMaxScaler()对值进行归一化会产生不稳定的性能。快速浏览一下本文开头绘制的直方图可以发现,大多数莱茵输入变量遵循高斯分布,因此这是合乎逻辑的。
  • 倒退:我发现 7 天是天气分析的合理时间框架,并给出了水从阿尔卑斯山流下所需的时间。增加超过这个数字的步骤对模型的性能没有有意义的改进。
  • 神经元:由 16 个记忆细胞组成的小型网络表现不佳,但由 32 个或更多神经元组成的大型网络则相当相似。我为我的模型选择了 64 个记忆单元。

  • Epochs: 一旦算法遍历数据集 25 次,我就选择停止训练。超过这个阈值,性能开始趋于平稳,模型开始过度适应训练集。

我已经在网上部署了这个模型的一个版本。其性能可以在这里进行监控。

我将在未来通过添加/删除变量和进一步调整超参数来尝试改进这个模型。如果你有任何想法,不要犹豫与我联系

使用机器学习解决俄罗斯贸易数据中的武器扩散问题

原文:https://towardsdatascience.com/using-machine-learning-to-tackle-arms-proliferation-in-russian-trade-data-e457f44002c0?source=collection_archive---------27-----------------------

理解大数据

我们如何构建和迭代一个机器学习模型来识别俄罗斯非法武器交易的实例。

作者:埃利奥特·冈恩

数据科学团队:Sean Antosiak、Elliot Gunn、Andrew Mikol、Jason Nova

项目负责人:Elan Riznis

简介
在本帖中,我们描述了我们如何构建一种新颖的方法,在大型贸易数据集上使用机器学习来识别向外国军队发货的俄罗斯方,或代表俄罗斯国有军事公司行事的各方。

俄罗斯是世界第二大武器出口国。现有的大量贸易数据给武器不扩散领域的研究人员和政府机构带来了挑战和机遇。最近,我们的数据科学家团队与 DC 华盛顿州的非营利组织 C4ADS 合作,研究我们是否可以将机器学习技术应用于他们拥有的非常大的贸易数据集,并找到潜在的俄罗斯武器扩散的实例。

我们的任务是双重的:ML 解决方案在这个用例中是否可行,如果可行,我们将如何构建我们的方法?在我们的研究中,我们发现只有一个其他项目试图将机器学习技术应用于类似的用例。伦敦大学国王学院的阿尔法项目(Project Alpha)正致力于为防扩散分析师创建一个大数据平台。

我们的第一次尝试是成功的:在 8 周内,我们能够识别高风险公司,这表明数据科学是一种可行的方法,对那些从事军备控制研究和/或出口控制的人来说非常有用。

在亚马逊网络服务(AWS)工作 这是我们团队第一次接触 AWS,所以学习曲线相当陡峭。我们学习了拼花文件以及模式信息是如何工作的。我们花了一周时间探索不同的选项,包括花一些时间在 AWS Glue 上,在那里我们没有成功地编写生命周期脚本。

我们最终选择了 EMR (Spark & Livy)和 SageMaker,因为它非常适合我们的用例。我们选择这两个是因为它们在协作工作环境中易于使用,并且能够使用多个集群来处理我们的大型数据集。设置过程是实质性的,需要一些所谓的系统管理员技能。简而言之,在设置 SageMaker 笔记本之前,我们在 EMR 服务中创建了集群。我们使用 SageMaker 笔记本的根目录中的一个笔记本作为中心位置,为 conda_python3 环境 pip 安装相关的库。

数据集的样本使用 Dask(拼花文件)进行探索,然后使用 Spark 进行清理。

数据探索&清理 “贸易”数据集大于 170 GB,包含 2016 年至 2018 年超过 6500 万行俄罗斯进出口数据。这包括 59 家被列为我们的目标变量的公司,它们是一家大型俄罗斯武器出口商和已知代表其转运货物的各方。

读取数据集有一些重大障碍,因为它们没有数据字典;有些柱子是神秘的。一个关键的专栏,Description Good,是用西里尔语写的——虽然我们很幸运有两个团队成员具有西里尔语能力,但是这个专栏应该被翻译成在未来的模型构建迭代中允许 NLP 派生的特性。这些公司在其税号(INN)中包含名称和唯一标识符。

我们只过滤出口商,并大量使用正则表达式来清理 PySpark 中的“交易”。我们必须将一些俄语(русскийязык语)转换成等价的 unicode,以便 regex 引擎进行处理。我们要么采用我们需要的精确单词(例如,пао、ооо、оо),要么必须采用单词的变化来匹配(例如,таможни、пост等),以确保这些色谱柱达到最基本的清洁标准。我们也考虑过将正则表达式应用于描述 Good,但是试图弄清楚俄语中动词/名词/形容词变化的复杂性被证明是相当困难的。

这将交易数据集从 170+ GB 减少到了 4GB 以下,即大约 900 万行。虽然我们担心减少的太多,但很难确保我们的正则表达式在清理数据时不会太严格或太宽松。这种减少确实让我们可以使用 Pandas 进行机器学习,而不是必须学习另一种新工具,Apache Spark 的 MLlib。导致两个数据帧的清理过程的示例代码片段:

功能工程 我们主要使用 UDF 来应用 lambda 函数,以仅允许指定的值(例如,对于浮点列,nones 和 nulls 被设置为 0.0)。在第一次传递数据时,我们确实手工输入了所有的方法和参数,但是在以后的迭代中,更加程序化的方法将会更加清晰。

每一列都有字符串或非序号值,所以它们都是编码的。此处是工作流程的一个示例:

我们试图将一些领域知识融入到特征工程中。我们的研究发现,军队和安全部队一直是俄罗斯非法武器转让的主要来源,要么通过直接参与地区冲突,要么通过腐败官员与犯罪组织合作进行销售。

我们创建了一个新的专栏,试图用一个虚拟变量“active-russian-military-ties”来捕捉这一过程。该标准被定义为具有当今或最近的历史(过去 10 年)联系,包括军事干预、维和以及签署的军事或技术合作协议。我们通过自己的研究手工编码,找到了 106 个符合标准的国家。

然而,稍后运行特征重要性分析将证实我们的直觉,即唯一重要的列是商品描述列。

原型和训练
我们同时追求监督和非监督学习方法。

受监督的
以 59 家出口商的列表作为我们的目标变量,我们在选定 scikit-learn 的GradientBoostingClassifier之前尝试了几个分类器。相对于深度决策树,GBC 让我们建立一个低偏差和低方差的预测函数。Boosting 在每一步按顺序拟合树,新的树根据先前树的残差进行训练。GBC 通过使用学习率(在 0,1 之间)来衡量新树每一步的贡献,解决了过度拟合问题。

因为它在最后组合它们之前按顺序训练了许多树,所以我们可以小心地控制方差并避免过度拟合,这与拟合单个深度决策树不同。决策树也是非参数化的,这对我们的数据集特别有用,因为我们的很多列都无法识别,因此无法与领域知识联系起来。

我们最初的迭代严重依赖于一个特性,所以我们删除了它并重新训练了模型。为了解决高度不平衡的类别,我们对数据集的子集进行了训练,其中我们分别对 25,000 和 250,000 个已知和非出口商进行了采样(这个 1:10 的比例最适合我们的模型)。我们的指标针对召回进行了优化,因为由于法律分歧,过度识别合法交易(误报)比错过非法交易(误报)要好。监督方法产生了 50 个预测的旅馆,它们不包含在 59 个原始列表中。我们有一种感觉,在俄罗斯注册网站上查找旅馆后,我们已经获得了一个很好的基线,其中许多旅馆都有与军事有关的描述。

无监督的 我们使用 scikit-learn 的最近邻的库,它实现了无监督的最近邻学习。

在最近邻算法中,参数是 n_neighbors 和使用的算法类型。BallTree 被用于该算法,因为它用于“快速广义 N 点问题”n_neighbors 用于对簇中的邻居进行分组。选择 10,000 这个数字来产生一个预测集,这个预测集足够宽,可以确定一个已知出口商的内部贸易交易的分组,并使用该交易来识别其他感兴趣的公司。在 37,467 个已知的 INN 预测器行中,前 1000 行用于构建 10,000,000 个邻域的预测数据框架。该数据帧然后过滤掉已知的 INN 邻居,并且对结果进行值计数以发现新的潜在 INN。在查看了最初的结果后,这种方法的成功被认为是不确定的。

方法 我们在方法中做了几个假设:

  • 非法和合法武器出口商在其贸易交易中表现出相似的特征(例如,在贸易伙伴、空间、货物、数量方面)。我们的目标变量是俄罗斯 59 家已知武器出口商的名单。使用这份合法出口商名单是了解非法武器出口商如何也出现在贸易数据中的一种方式。由于我们实际上不知道非法出口商和合法出口商有什么不同,这可能是一个巨大的飞跃。
  • 交易数据包含准确和真实的信息。更有可能的情况是,非法武器出口商混淆了他们的路线、出口目的地、货物数量,甚至货物实际上是什么。一个众所周知的例子是俄罗斯武器经纪人 Victor Bout,他利用许多幌子公司,通过飞机运输武器,并重新注册飞机,试图通过合法和非法手段逃避管制。其他出口商可能会采取迂回路线前往其真正的目的地,在空壳公司之间转移货物,这些过境国或公司不会被数据捕获。

吸取教训&展望未来

我们有 8 周的时间来学习新工具,并为 C4ADS 的项目利益相关者提供概念证明。听说我们的基线结果是进一步探索的有效 MVP,并得到了利益相关方的积极认可,我们非常兴奋。

  • 首先,文档是至关重要的:其他团队以前曾与他们合作应对这一挑战,但是没有任何文档(特别是关于架构设置),我们花了大量时间尝试不同的设置,然后选择了最适合项目独特需求的设置。为此,我们确保尽早并经常记录文档,并传递一份广泛详细的 22 页文档,以确保可重复性,并使下一个团队能够更快地发展。
  • 第二,项目管理让我们走上正轨:我们一直作为一个远程团队工作,并且能够从 0 到一个有希望的基线结果如此之快,因为我们有一个了不起的项目经理(感谢 Elan!)与我们紧密合作,跨越所有时区。我们每天站立,提交定期发布的画布,评估里程碑,并定期与涉众进行视频通话,以更好地理解业务问题和期望。
  • 结果,我们很早并且经常迭代:有了这个清晰的工作结构,并且我们的项目经理的焦点是在第 12 周交付 MVP,我们被鼓励尽早尝试,抛弃那些很快被证明没有结果的工具或工作流。我们无法在短时间内对关键的西里尔列进行超参数调整或 NLP,但我们很高兴我们的文档和基线结果将使下一个团队能够推动机器学习在这一领域的发展。

特别感谢 C4ADS 给我们这个难以置信的机会来解决这个新奇的数据科学问题。

使用 MALLET LDA 了解玩家讨厌神奇宝贝剑/盾的原因

原文:https://towardsdatascience.com/using-mallet-lda-to-learn-why-players-hate-pokémon-sword-shield-23b12e4fc395?source=collection_archive---------23-----------------------

简单介绍 MALLET LDA 如何用于主题模型,解释玩家不喜欢神奇宝贝剑/盾的原因。

Unsplash 上由 Kamil S 拍摄的照片

任天堂 Switch 最新的神奇宝贝游戏大张旗鼓地推出了,承诺用令人惊叹的视觉效果和开放世界的冒险让粉丝们惊叹不已。尽管受到了评论家的赞扬,但这场比赛还是让大多数玩家失望了。在这篇文章中,我将带你了解我如何使用槌潜狄利克雷分配(LDA) 来找出玩家不喜欢这个游戏的关键原因。

数据收集

我用 BeautifulSoup 从 Metacritic 上报废了 Pokémon 用户评论(我已经在这里详述了步骤)。Metacritic 评论有评级分数,表明用户对游戏的总体评价。这让我们的生活更轻松,因为我们不需要运行一个单独的情绪分析来寻找负面评论。

我总共收集了 2019 年 11 月 15 日至 28 日发布的 3,261 条用户评论。数据集包含以下特征:

  • 姓名:审核人的用户名
  • 日期:审核日期
  • 评分:从 0 到 10 的数字分数,给出游戏的总体评价。较高的分数意味着更积极的体验。
  • 评审:实际评审文本

包装

以下软件包用于数据清理和分析:numpy、pandas、regex、string、nltk、langdetect、sklearn、spaCy、gensim、pprint(可选)、matplotlib 和 collections。

你还需要安装 gensim 的 MALLET LDA 的包装器,如果你还没有的话。

导入日志记录也是可选的,但这是一个最佳实践,因为它使您能够了解模型运行时幕后发生的事情。

import numpy as np 
import pandas as pdimport re 
import stringimport nltkfrom langdetect import detectfrom sklearn.feature_extraction.text import CountVectorizer import spacy
from spacy.lang.en.stop_words import STOP_WORDSimport gensim
from gensim import corpora, models, matutils
from gensim.models import CoherenceModelimport os
from gensim.models.wrappers import LdaMalletmallet_path = '/Users/adelweiss/mallet-2.0.8/bin/mallet'from pprint import pprint #optionalimport matplotlib.pyplot as plt
%matplotlib inlinefrom collections import Counterimport logging #optional
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

特征工程

  1. 给评论贴上正面、负面或褒贬不一的标签。Metacritic 将低于 5 的分数视为负面,将 5 到 7 的分数视为混合分数,将高于 7 的分数视为正面。在下面的代码中,我定义了一个函数来根据这些规则标记评论。
def sentiment(x):
    if x > 7:
        return 'positive'
    if x < 5:
        return 'negative'
    else: return 'mixed'df['sentiment'] = df['rating'].apply(lambda x:sentiment(x))

数据清理

  1. 删除重复的用户评论。一些用户对《神奇宝贝之剑》和《盾牌》发表了相同的评论(这是意料之中的,因为这两款游戏在内容上仅略有不同)。
df.drop_duplicates(subset='name', keep = 'first', inplace = True)

2.过滤掉非英文评论。 为了做到这一点,我使用了 langdetect 包,它依赖于 Google 的语言检测库,支持 55 种语言。 detect 函数读取文本输入,计算文本使用支持语言的概率,并返回概率最高的语言。

def language_detection(x): 
 result = detect(x)
 if result == 'en':return x 
 else: return np.NaN 

df['review'] = df['review'].apply(lambda x:language_detection(x))

3.删除停用词。 我结合了 nltk 和 spaCy 的停用词,加入了‘游戏’、‘口袋妖怪’、‘神奇宝贝’等自定义停用词。

在检查每个单词的评论频率后添加自定义停用词(例如,频率为 5 表示该单词出现在 5 次评论中。在第 6 步中会有更多的介绍。).自定义停用词出现在大多数评论中。它们在评论中的高频率意味着它们对主题建模没有用。因此,我在后续运行中删除了它们,以提高模型性能。

该代码创建了一个停用词列表。这些词将在下一步从评论中删除。

nltk_stop_words = nltk.corpus.stopwords.words('english')stop_words = list(STOP_WORDS)
stop_words.extend(['game','pokemon','pokémon']) 
### these are common words that appear in almost all reviewsfor word in nltk_stop_words:
 if word in stop_words: continue
 else: stop_words.append(word)

4.文字清理。这是主要使用正则表达式的标准文本清理步骤列表:

  • 将所有单词转换成小写
  • 从列表中删除单词(我们将使用它来删除停用词,以及出现在少于 4 条评论中的罕见单词)
  • 删除数字
  • 删除标点符号
  • 替换单词(标准化我注意到的拼写不一致,如“游戏狂”和“游戏怪胎”)
  • 移除“\r”字符串文字
  • 从单词中去除多余的空格(即一行中有两个空格),这是由删除数字、标点符号和字符串文字的函数产生的
def make_lower(text):
 return text.lower()def remove_words(text,wordlist):
 for word in wordlist:
 if word in text.split():
     text = re.sub(r'\b{}\b'.format(word), '', text) 
 return textdef remove_digits(text):
 return re.sub('\d', ' ', text)def remove_punctuation(text):
 text = re.sub('[%s]' % re.escape(string.punctuation), ' ', text) 
 return re.sub(r'[^\w\s]', ' ', text)def strip_extraspace(text):
 return re.sub('\s\s+',' ', text)def replace_word(text,word,replacement):
 return text.replace(word,replacement)def remove_r(text):
 return text.replace('\r',' ')
#df['review'] = df['review'].apply(lambda x:remove_punctuation(x))def clean_text(text):
 text = make_lower(text)
 text = remove_punctuation(text)
 text = remove_digits(text)
 text = replace_word(text,'game freak','gamefreak') 
 text = replace_word(text, 'game play', 'gameplay')
 text = remove_words(text,stop_words)
 text = remove_r(text)
 text = strip_extraspace(text)
 return textdf['review'] = df['review'].apply(lambda x:clean_text(x))

5.词汇化。我使用 spaCy 的词类标注分类器来减少对名词和动词的评论。只使用名词和动词更容易辨别主题是关于什么的。

下面的代码遍历评论中的每个单词,并返回标记为“名词”或“动词”的单词。它会跳过已被词条化为“-PRON-”的单词(当单词是代词时,spaCy 会返回这个词),以及停用词列表中已被词条化的单词。

sp = spacy.load('en_core_web_sm')def lemmatize_words(text, allowed_postags=['NOUN', 'ADJ', 'VERB', ‘ADV’]):
 text = sp(text)
 lemmed_string =''
 for word in text:
     if word.pos_ in allowed_postags:
         if word.lemma_ == '-PRON-' or word.lemma_ in stop_words: 
 ### skip words that are not in allowed postags or becomes a    stopword when lemmatised 
             continue 
         else: lemmed_string = lemmed_string+' '+word.lemma_
     return lemmed_string.lstrip()df['review'] = df['review'].apply(lambda x:lemmatize_words(x, allowed_postags=['NOUN', 'VERB']))

6.去除生僻字。没有出现在大量评论中的罕见单词对于主题建模也是无用的。与停用词一样,我删除了它们以提高模型性能。

这段代码计算一个单词在评论中出现的次数,并将出现次数少于 4 次的评论添加到一个列表中。

word_frequency = Counter()for text in df.review:
 text = text.split()
 word_frequency.update(set(text))rare_words = []for key, value in word_frequency.items():
 if value < 4:
 rare_words.append(key)df['review'] = df['review'].apply(lambda x:remove_words(x,rare_words))

7.仅选择负面评论,并创建令牌。最后,由于我们只对负面评论感兴趣,我根据评论的情感标签过滤了评论,并应用 sklearn 的计数矢量器来创建单词标记。

CountVectorizer 的 token_pattern 参数指定只包含至少 3 个字符的单词。我的假设是 1 和 2 个字母的单词信息不多,对主题建模用处不大。另外, ngram_range 参数被设置为(1,2)以包含二元模型作为标记。

negative = df[df['sentiment']=='negative']vectorizer = CountVectorizer(stop_words=stop_words, ngram_range = (1,2), token_pattern="\\b[a-z][a-z][a-z]+\\b") 

为 MALLET LDA 奠定基础

要使用 MALLET LDA,我们需要使用矢量器拟合和转换数据,并创建模型需要的一些变量。首先,我们将计数矢量器与负面评论相匹配。然后,我们创建一个文档-单词矩阵,并将其从稀疏矩阵转换为 gensim 单词语料库。接下来,我们创建 word2idid2word 变量,将单词与其数字令牌 id 进行匹配,反之亦然,并将这些变量保存到字典中。

vectorizer.fit(negative.review)
doc_word = vectorizer.transform(negative.review).transpose()corpus = matutils.Sparse2Corpus(doc_word)word2id = dict((v, k) for v, k in vectorizer.vocabulary_.items())
id2word = dict((v, k) for k, v in vectorizer.vocabulary_.items())dictionary = corpora.Dictionary()
dictionary.id2token = id2word
dictionary.token2id = word2id

选择主题的数量

为了选择主题的数量,我们将计算每个指定数量的主题的一致性分数。

连贯性分数通过检查每个主题的热门单词之间的语义相似度来评估主题的质量。分数越高,模型越好。

为了计算每个模式的一致性分数,我使用了下面的代码,我在第 17 节的中找到了。代码循环遍历一系列数字,代表应用于模型的主题数量,计算每个模型的一致性分数,保存分数并绘制它们。

def compute_coherence_values(dictionary, corpus, texts, limit, start=2, step=3):
    """
    Compute c_v coherence for various number of topics

    Parameters:
    ----------
    dictionary : Gensim dictionary
    corpus : Gensim corpus
    texts : List of input texts
    limit : Max num of topics

    Returns:
    -------
    model_list : List of LDA topic models
    coherence_values : Coherence values corresponding to the LDA model with respective number of topics
    """
    coherence_values = []
    model_list = []
    for num_topics in range(start, limit, step):
        model = gensim.models.wrappers.LdaMallet(mallet_path, corpus=corpus, num_topics=num_topics, id2word=id2word)
        model_list.append(model)
        coherencemodel = CoherenceModel(model=model, texts=texts, dictionary=dictionary, coherence='c_v')
        coherence_values.append(coherencemodel.get_coherence())

    return model_list, coherence_values# Can take a long time to run.
model_list, coherence_values = compute_coherence_values(dictionary=id2word, corpus=corpus, texts=data_lemmatized, start=2, limit=40, step=6)# Show graph
limit=40; start=2; step=6;
x = range(start, limit, step)
plt.plot(x, coherence_values)
plt.xlabel("Num Topics")
plt.ylabel("Coherence score")
plt.legend(("coherence_values"), loc='best')
plt.show()

根据图表,2 或 5 是可供选择的好数字,因为它们的一致性得分最高。然而,选择 2 个主题可能会过于简单,所以我们选择 5 个。

描述 MALLET LDA 在多个主题上的一致性分数的图表

探索主题

为了查看与每个主题最相关的前 10 个单词,我们重新运行指定 5 个主题的模型,并使用 show_topics。你可以使用一个简单的 print 语句来代替,但是 pprint 让事情更容易阅读。

ldamallet = LdaMallet(mallet_path, corpus=corpus, num_topics=5, id2word=id2word, random_seed = 77)# Show Topics
pprint(ldamallet.show_topics(formatted=False))

下面是每个话题的热门词汇,以及我给它们的标签。大部分话题都和游戏性有关,除了最后一个。因为我们只看了负面评论,我们可以假设这些是问题领域。

表格描述了每个主题的前 10 个单词,以及建议的主题标签

检查完评论后,下面是每个主题的快速概述:

  • 时间:上场时间太短。许多玩家在 10 到 25 小时内完成了游戏
  • 体验:故事缺乏,整体游戏太简单。此外,大肆宣传的野生区域缺乏活力,而且大多是空的。
  • 视觉效果:动画、图形和模型缺乏质量和质感。对视觉效果的期望非常高,因为游戏狂认为这是从游戏中删除这么多神奇宝贝的原因。
  • 内容:玩家经常将 switch 游戏与 3DS 游戏相比较,后者有更多的功能、内容和可用的神奇宝贝。
  • 开发者:玩家们感觉被 Game Freak 背叛了,因为它并没有兑现其承诺的惊人的视觉效果。他们觉得《游戏怪胎》欺骗了他们,而且《神奇宝贝》的删减是不合理的。

结论

LDA 是一种在文本中寻找主题的好方法,尤其是当它用于探索性目的时。我认为 MALLET LDA 很好地概括了为什么玩家讨厌这个游戏。我快速浏览了一下正面评论,发现了两个广泛的话题:游戏性和正面感受。也许,这可能是在 switch 上开始玩神奇宝贝的新玩家(因此使用 Let's Go 作为基准)与更习惯复杂游戏的经验丰富的玩家之间的情况。

*代码可在此处找到 *

在 Python 中使用地图和过滤器

原文:https://towardsdatascience.com/using-map-and-filter-in-python-ffdfa8b97520?source=collection_archive---------11-----------------------

如何使用 python 中内置的地图和过滤功能

凯文·Ku 在 Unsplash 上的照片

在本教程中,我们将学习何时以及如何使用 Python 中内置的地图和过滤功能。

地图功能

假设您想使用已经有的列表创建一个列表。这意味着您希望使用现有的列表,对每个元素应用某种操作或函数,并使用这些输出来创建一个新的列表。

例如,如果我们有一个数字列表,我们想创建一个包含它们的方块的新列表。一种方法是使用 for 循环遍历数字列表,并应用一个函数返回每个数字或元素的平方。当我们遍历列表时,我们可以在新列表中添加或附加平方值。

让我们看看如何在代码中做到这一点:

我们有一个数字列表, num_list ,我们想要创建一个新列表, num_list_squared ,它包含了 num_list 的平方。我们使用 for 循环遍历 num_list ,并将每个数字的平方或 num 添加到我们的 num_list_squared 列表中。

另一种实现方法是使用名为 map 的内置 python 函数。

map 函数接受两个参数:我们想要应用的函数和我们想要应用它的 iterable 对象或序列(比如本例中的 list)。换句话说,map 函数将这个函数映射或应用到我们传入的序列的每个元素。

我们将用于地图功能的格式如下:

map(要应用的函数,要应用的元素序列)

这个 map 函数将返回一个 map 对象,它是一个迭代器。如果我们想从这个 map 对象创建一个列表,我们需要将 map 对象传递给内置的 list 函数,如下所示:

列表(映射(功能,顺序))

让我们看看如何使用内置的 map 函数来完成上面的代码:

请记住,我们可以将 map 函数应用于任何可迭代对象或序列中的每个元素,而不仅仅是列表。

那么让我们来分析一下这行代码中发生了什么:

num_list_squared = list(map(squared, num_list))

map 函数从 num_list 中获取第一个元素,即 1,并将其作为参数传递给平方函数(因为我们将该函数作为第一个参数传递给 map 函数)。然后, squared 函数返回 1 的平方,也就是 1,它被添加到我们的地图对象中。然后,map 函数从 num_list 中取出第二个元素,即 2,并将其作为参数传递给 squared 函数。 squared 函数返回 2 的平方,也就是 4,然后它被添加到我们的地图对象中。在它完成了对 num_list 元素的遍历,并且我们的其余平方数被添加到地图对象之后, list 函数将这个地图对象转换成一个列表,并且该列表被分配给变量 num_list_squared

使用λ表达式

我们可以通过传入一个 lambda 表达式作为我们的函数来进一步缩短代码:

如果你不熟悉 lambda 表达式,你可以看看这个教程:

[## Python 中的 Lambda 表达式

如何用 python 写匿名函数

towardsdatascience.com](/lambda-expressions-in-python-9ad476c75438)

:我们传入 map 的函数可以是 python 中的内置函数。例如,如果我们有一个字符串列表,我们想要创建一个包含这些字符串长度的新列表,我们可以像下面这样传递内置的 len 函数:

list(map(len, list_of_strings))

了解更多关于可迭代、迭代器和迭代的知识:

[## Python 中的迭代器和迭代器

Python 中的可迭代对象、迭代器和迭代

towardsdatascience.com](/iterables-and-iterators-in-python-849b1556ce27)

过滤功能

同样,假设我们想用一个已经有的列表来创建一个列表。但是这一次我们希望我们的新列表只包含满足给定条件的元素。例如,我们有一个数字列表,我们想创建一个只包含偶数的新列表。我们可以使用 for 循环来完成这项任务,如下所示:

我们有一个数字列表, list_of_nums ,其中包含数字 1、2、3、4、5 和 6。我们想要创建一个新的数字列表, list_of_even_nums ,它只包含来自 list_of_nums 的偶数。所以我们创建了一个函数, is_even ,它接受一个输入,如果输入是偶数,则返回 True,否则返回 False。然后,我们创建了一个 for 循环,该循环遍历 list_of_nums ,并通过将该元素传递给 is_even 函数来检查列表中的每个数字是否都是偶数。如果 is_even 函数返回 True,则该数字被追加到 list_of_even_nums 中。如果 is_even 返回 False,则该数字不会追加到 list_of_even_nums 中。

另一种实现方法是使用名为 filter 的内置 python 函数。 filter 函数接受两个参数:检查特定条件的函数和我们想要应用它的序列(比如本例中的列表)。filter 函数从我们的列表中取出每个元素,并将其传递给我们给它的函数。如果将该特定元素作为参数的函数返回 True,filter 函数将把该值添加到 filter 对象中(然后我们可以从该对象中创建一个列表,就像我们对上面的 map 对象所做的那样)。如果函数返回 False,那么该元素将不会被添加到我们的 filter 对象中。

换句话说,我们可以认为过滤函数是基于某种条件过滤我们的列表或序列。

我们用于过滤功能的格式如下:

过滤器(检查条件、我们想要应用的元素序列的函数)

这个过滤函数将返回一个过滤对象,它是一个迭代器。如果我们想从这个过滤器对象创建一个列表,我们需要将过滤器对象传递给内置的 list 函数(就像我们对 map 对象所做的那样),如下所示:

列表(过滤(功能,顺序))

现在让我们使用内置的过滤函数创建与上面相同的列表:

filter 函数从 list_of_nums 中获取第一个元素,即 1,并将其作为参数传递给 is_even 函数(因为我们将该函数作为第一个参数传递给 filter 函数)。然后 is_even 函数返回 False,因为 1 不是偶数,所以 1 没有被添加到我们的过滤器对象中。然后,filter 函数从 list_of_nums 中获取第二个元素,即 2,并将其作为参数传递给 is_even 函数。 is_even 函数返回 True,因为 2 是偶数,因此 2 被添加到我们的过滤器对象中。在遍历完 list_of_nums 中的其余元素并且将其余偶数添加到我们的 filter 对象中之后, list 函数将这个 filter 对象转换为一个列表,并且该列表被分配给变量 list_of_even_nums

使用λ表达式

我们可以通过传入一个 lambda 表达式作为我们的函数来进一步缩短代码:

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。

[## 通过我的推荐链接加入媒体——卢艾·马塔尔卡

阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…

lmatalka90.medium.com](https://lmatalka90.medium.com/membership)

结论

在本教程中,我们学习了 python 内置的地图和过滤函数是如何工作的。我们也看到了一些使用它们的例子。最后,我们看到了 lambda 表达式如何作为参数传递给这些函数。

利用数学操纵选举

原文:https://towardsdatascience.com/using-maths-to-rig-elections-e4e88b694c60?source=collection_archive---------51-----------------------

为什么你需要知道本福特定律

唐在 Unsplash 上的照片

声明:我不赞成用数学来操纵选举!但是为了争论的缘故…

想象一下,你不是一个道德高尚的人,你的任务是编造一组数字。你需要对这些数据进行加工,让它们代表一个有利的结果,同时不引起任何可能碰巧看到它们的人的怀疑。

你决定随机选择它们——或者更好——使用随机数发生器来消除你可能无意中带来的任何无意识的偏见。带着肆无忌惮的窃笑,你用更合适的结果取代了令人不快的结果。销毁证据后,那天晚上你上床睡觉,安全地知道你不可能被抓住。

不幸的是,我可能有些消息要告诉你。

令人不安的分布

我们上面的歪门邪道的同谋者认为他们随机选择的数字将是均匀分布的,这是正确的,因为每个结果都是同样可能的,因此是无模式的。然而,他们犯了一个错误:人口、街道地址、出现在英国《金融时报》上的数字、树上的叶子数量——任何自然的数字群体——也遵循一个不太直观的规则,这需要更多的工作来伪造。

为了说明这一点,让我们以世界各国的人口为例,这些国家的人口从几千到十多亿不等。现在,让我们把所有这些数字的第一个数字切掉。像这样:

数据来源: Worldometer

重要的问题来了:你觉得这些前导数字会怎么分布?

认为没有一个数字会比其他任何一个数字出现得更频繁似乎是合乎逻辑的。假设有 9 个可能的数字,每个数字出现的概率应该是九分之一,对吗?让我们看看这个预测是否会在实践中得到证实:

现在,这有点令人惊讶——等可能性的看似直观的结论实际上是完全错误的!这种模式不仅仅在人群中观察到——你会在房价、河流长度、建筑高度中发现它…

这种现象被称为本福特定律,在任何人群中都可以发现:

  • 不受人为约束;
  • 跨越足够数量的数量级

违反上述第一点,电话号码必须以某些数字(区号)开头。家庭中洗衣机的数量不倾向于取大范围的值,违反了第二条(如果我冒犯了任何收藏者,对不起)。结果是这些群体不一定会表现出这种行为。

究竟为什么

为了帮助你对为什么会发生这种情况有一点直觉,让我们举一个例子。考虑一个均匀分布的人口(从 1 到某个上限的数字),观察随机选择的数字以某个数字开头的概率如何随着人口规模的增加而变化。

在最左边,人口仅仅是{1},因此选择一个前导数字为 1 的数字的概率是 100%。随着人口规模的增长,获得前导数字 1 的概率会降低,直到达到九分之一,约为 0.11,此时人口规模为 9,每个数字都有一个。

但是当我们开始数到十的时候——获得前导 1 的可能性又开始变大了!当我们数到 99 时,概率像以前一样降低,其中每个前导数字都有同样的可能性再次出现——然后模式重复。

我们可以观察到,当人口规模为 9、99、999 等时,每个前导数字的概率确实是相等的。但是对于所有其他的人口规模,你得到 1 的可能性比其他任何数字都大。整洁!

回到我们的世界各国人口的例子,在数字上没有模式——一些国家很大,像印度,一些国家很小,像图瓦卢。一个典型国家的人口将介于 0 和最大国家(中国,有 14 亿居民)的人口之间——随机选取一个国家的人口基本上就像从 1 到 1,400,000,000 中随机选取一个数字。如果我们上面的图表延伸到 14 亿,我们会发现领先 1 的概率是 36.5%!在此范围内,前导 1 比 2 多,前导 2 比 3 多,依此类推。这就是为什么我们在本文的第一张图中发现了这种特殊的模式。

如果我们为所有不同的前导数字绘制一条类似于上图的曲线,我们将得到如下结果:

我们可以看到,随着我们越往上走,我们得到它的机会越来越小。可怜的 9 号最多有九分之一的几率,1 号最差也有九分之一的几率!

如何不操纵选举

那么这又和操纵选举有什么关系呢?所有这些在现实世界中很重要的原因是,除非他们非常仔细地准备,否则虚构的数字不会遵守本福特定律——它们不会在前导数字中显示这种频率模式。这给了我们一个工具来检测那些为了欺骗人们而伪造的数字..这里有几个例子:

  • 法务会计师和审计师利用本福德定律来发现社会经济数据中的欺诈行为。过去会计丑闻的数据被发现不符合这一规则。
  • 2004 年,希腊政府承认伪造经济数据以加入欧洲货币联盟。这种数据的欺诈性质可以从其违反本福特法则中看出。
  • 在 2009 年的伊朗选举中,一名候选人在伊朗不同选区的票数并不遵循本福特定律。事实上,以 7 开头的数字太多了。这种反常现象发生在六个最大的投票区中的三个。

值得一提的是,单凭本福特法则不足以证明一组数字是否被伪造——就像任何统计分析一样,它只能告诉我们这些数字不太可能是真实的。尽管有这样的警告,起初似乎是一个有趣而无害的数学规则,结果却成为识别和防止犯罪和欺诈行为的强大而有用的工具。

如果你正在监狱里读这篇文章,因为你被一个特别倾向于数字的侦探抓到在你的纳税申报单上说猪肉馅饼——祝你下次好运。

更多信息和积分

Andrew Hetherington 是英国伦敦的一名见习精算师和数据爱好者。

  • LinkedIn 上与我联系。
  • 看我在 GitHub 上摆弄什么。
  • 用来制作本文情节的笔记本可以在这里找到。

图片:大本钟照片由丹尼尔·h·唐Unsplash 上拍摄。

各国人口数据来自世界人口统计。2020 年 5 月 16 日访问。

IBM 人力资源流失案例研究

原文:https://towardsdatascience.com/using-ml-to-predict-if-an-employee-will-leave-829df149d4f8?source=collection_archive---------10-----------------------

Unsplash:马尔泰克·比约克

预测某个员工是否会离开组织?

在任何组织中,自然减员都是一个主要问题。在培训新员工上投入的时间、金钱和精力、工作依赖性和其他因素会导致公司在员工离职时遭受巨大的整体损失。此外,自然减员会导致现有员工之间的不信任,这本身会成为组织管理的一个主要困难。

IBM 人力资源流失案例研究是一个虚构的数据集,旨在确定可能影响决定哪些员工可能离开公司的重要因素。本文提供了深入的分析和预测模型,以了解重要的因素并做出准确的预测。

目录

  1. 数据准备和理解。
  2. 特征工程。
  3. 特征选择。
  4. 模型拟合
  5. 模型比较
  6. 建议和结论。

数据准备和理解

IBM 人力资源流失案例研究可以在 Kaggle 上找到。 Python 3.3 用于分析和模型拟合。使用的 IDE 是 Spyder 3.3.3

为了正确理解数据集,让我们看看它的一些基本特性。这包括数据集的形状和数据中存在的特征/变量的类型。此外,我们还将查看丢失的值(如果有的话)。

数据集的形状

损耗数据集有 1470 个观察值和 35 个变量。在 35 个变量中,存在一个目标变量损耗,可能的结果。其他 34 个变量是独立变量,只有一个变量是独立变量,即员工号,它表示员工号或标识号。

消耗

上图显示了目标变量的分布。在总共 1470 个观察结果中,1233 个是否定的,而 167 个是肯定的。我们将在将数据分成训练测试集后处理这种不平衡。

数据集没有缺失值。因此,不需要对缺失值进行进一步处理。让我们用箱线图来观察异常值。

箱形图(1)

箱形图(2)

年龄日工资率离家距离小时工资率月工资率百分比工资率倾向于没有任何异常值。

NumCompaniesWorkedTrainingTimesLastYearYearsWithCurrManageryears incurentrole有中等数量的异常值。

月收入总工作年数年就职年晋升有大量异常值。

解决这个问题的一种方法是缩放变量,以减少其对模型的影响。Python 的 Scikit-learn 库中的 StandardScaler() 可用于此目的。

继续之前的最后一步是检查多重共线性。为此,我们绘制了一个相关矩阵。

相关矩阵

上图显示了独立变量之间的相关性。我们这样做是为了检查多重共线性。多重共线性遵循的经验法则是,如果相关系数(r) 接近 0.80 。基于此,我们确定以下变量具有高度相关性:

月收入工作级别的相关性为 0.95。这是非常高的相关性。

总工作年限工作级别的相关性为 0.78,也非常接近 0.80。

所有其他变量似乎具有小于 0.80 的相关性。

了解了数据和初步分析后,我们现在可以更深入地了解特征工程。

特征工程

特征工程是指在使用机器学习或统计建模(如深度学习、决策树或回归)创建预测模型时选择和转换变量的过程。该过程包括数据分析、应用经验法则和判断的结合。

出于模型的目的,从现有的独立变量中创建了两个特征:

整体性:

环境满意度工作参与度工作满意度、人际关系满意度四个因素,其总和即为整体满意度。最高分“16”表示完全的整体满意度,而“4”被认为是最低的整体满意度。该变量的设计基于给定的图表:

流失比例与整体满意度

横轴表示整体满意度,纵轴表示对应于给定水平的比例损耗。可以清楚地得出结论,随着年级的增加,自然减员的比例减少,即从 5 年级的 0.56 减少到 16 年级的 0.00。

BelowAverageIncome

另一个特性,即 BelowAverageIncome 是根据员工所在的部门、该部门的平均收入以及员工的百分比工资收入创建的。如果员工的月收入低于该部门的平均收入,且加薪百分比小于 16,则该员工的等级为 1,否则为 0,表明该员工最有可能离职。

特征选择

基于探索性数据分析和描述性统计分析,我们选择和取消某些对我们的模型没有显著贡献的变量。根据以下情况取消选择变量:

  1. 可变类型
  2. 数据点的不变性
  3. 目标变量和自变量之间的独立性
  4. 多重共线性

变量类型:

分析中不使用标称变量,因为它不为模型构建过程提供任何输入。然而,它被保留,以便识别对其进行研究的员工。

数据点的不变性:

某些变量没有任何可变性。这些变量是:

  1. 员工计数:这只是对员工的计数,其取值始终为 1。
  2. 18 岁以上:该变量描述员工是否超过 18 岁。在所有情况下,它都采用值“Yes”。
  3. 标准小时数:员工一周工作的标准小时数。它的常数值是 80

目标变量和自变量之间的独立性:

检查目标变量和自变量之间的相关性是很重要的。如果两者之间的关系不显著,就不应该选择那个自变量进行建模。

为了确定自变量和目标变量之间的相关性,我们使用卡方检验。我们设定假设:

H0:这两个变量之间没有联系

H1:变量之间有关联。

如果 p 值小于显著性水平(a)或计算值大于表中值,我们拒绝零假设。出于本研究的目的,我们选择 LOS (a)为 5%。

基于卡方检验,取消选择以下变量:

从模型构建中移除的变量

多重共线性:

多重共线性是指输入变量之间的强关系或相关性。如果两个变量之间的相关系数大于 0.80,则称之为多重共线性。去除这些变量是很重要的,因为这会导致模型中的方差膨胀,从而增加模型中的误差。

基于我们的分析,我们去除了以下变量:

  1. 工作级别:与月收入的相关系数为 0.95,与总工作年限的相关系数为 0.78。
  2. 总工作年数 : 0.77 与月收入的相关性。

主成分分析

对于特征提取,我们也可以使用主成分分析(PCA)。特征提取是一个降维的过程,通过该过程,原始数据的初始集合被减少到更易管理的组以便处理。

主成分数量对比例变化的解释

创建了两个主要组件来解释所考虑的 26 个变量的可变性。运行一个循环,从 1 到 26 取成分,并测量所解释的可变性。只有一个主成分,解释的可变性约为 68%。这在两个主成分的情况下增加到大约 99%,在三个主成分的情况下接近 99.99%。任何进一步的增加都增加了解释不明显的百分比。

模型拟合

经过预处理后,我们将数据分为训练、验证和测试数据集。从总共 1470 个观察值中,我们选择:

  1. 训练数据集的 80%观察值。
  2. 验证数据集的 14%观察值。
  3. 测试数据集的 6%观察值。

选择的算法是线性核的支持向量机选择的参数如下:

在这种情况下,内核等重要参数选择为“线性”。 random_state 取 42 作为种子,模型拟合 1176 个观测值。

下一个过程是根据验证和测试集的拟合模型来预测值。为了计算准确度、精确度、召回率、真阳性和真阴性,我们创建了混淆矩阵。

验证集的混淆矩阵如下所示:

混淆矩阵(验证集)

测试集的混淆矩阵如下所示:

混淆矩阵(测试集)

我们现在计算 2 个数据集的准确性参数:

验证和测试集的评估指标

第一组信息属于验证集。我们得到的加权准确度为 1.00,精度为 1.00,召回率为 1.00。

第二组信息与测试集有关。我们得到的加权准确度为 1.00,精度为 1.00,召回率为 1.00。

我们现在来看看科恩的卡帕评分。Cohen 的 kappa 系数(κ)是一个统计量,用于测量评分者对定性(分类)项目的一致程度。一般认为这是一个比简单的百分比一致计算更稳健的方法,因为κ考虑了偶然发生一致的可能性。

科恩的卡帕评分

训练、验证和测试集的 Kappa 分数表明,绝对不存在偶然预测的可能性。因此,该模型的结果是完全可信的,也可用于进一步的分析。

:还使用了其他几个分类器,这里解释其中最好的一个。其他分类器包括支持向量机(RBF 核)随机森林(1000 棵树)逻辑回归

模型比较

如上所述,使用了几个分类器,并选择了最好的一个。然而,看看每个分类器的优点和缺点是值得的。 SVM(线性核)给出的准确率最高(100%),其次是随机森林 (87%)和 SVM (RBF 核)。过度拟合可能是最佳模型的一个问题,因为它可能屈服于新的数据点。此外,模型拟合需要时间。另一方面,随机森林有助于分类中的新数据点。

建议和结论

根据上面的图表,我们可以得出结论,股票期权级别在决定员工流失方面起着非常重要的作用。除此之外,月收入、工作满意度、工作投入度也位列前茅。另一方面,诸如绩效等级、性别、部门之类的因素往往影响不大(卡方检验也证明了这一点)。

基于整个项目,为了从模型中获得最佳输出,需要保持某些点。

人力资源部门可以关注那些在决定一个员工是否离开一个组织时起重要作用的重要变量。这些变量是:

StockOptionLevel

每月收入

工作满意度

工作投入

工作生活平衡

环境满意度

基于上述变量,人们可以清楚地注意到一种模式。员工更关心他们直接拿到手的物质物品。然后是决定员工是否会离开组织的心理变量。

因此,HR 可以关注这些方面,从员工的角度去理解。一旦遵循了这一点,被称为流失项目的项目就可以被用作保留项目。这可以极大地帮助组织。

其次,当收到新的数据集时,需要不时地调整模型。如果引入任何新的输入变量,重要的是检索参与初始研究的员工的信息。

我们因此结束了这个项目。

感谢您的阅读。我真诚地希望你觉得这篇文章很有见地,我一如既往地欢迎讨论和建设性的反馈。

给我发邮件:icy.algorithms@gmail.com

你可以在 LinkedIn 上找到我。

用 SHAP 的模型解释来理解泰坦尼克号上发生的事情

原文:https://towardsdatascience.com/using-model-interpretation-with-shap-to-understand-what-happened-in-the-titanic-1dd42ef41888?source=collection_archive---------37-----------------------

作者图片

注意:这是关于分析和理解泰坦尼克号数据集的两部分中的第二部分。请在这里找到第一部分。

介绍

在过去的帖子中,我对泰坦尼克号数据集进行了统计分析,以回答乘客的社会经济阶层是否对他们的生存概率有影响的问题。统计显著性检验表明,“Pclass”变量,即我用来表示社会经济地位的每个个体的类别,对人们的生存有显著影响,p 值为 2.24e-147。你可以在这里阅读的帖子。

在这篇文章中,我将建立一个机器学习模型,并用 SHAP 来解释这个模型。这将揭开泰坦尼克号上发生的一些隐藏的秘密,并帮助我们想象社会经济阶层对存活率的影响。

数据清理和预处理

让我们从查看数据集开始,看看是否需要任何数据预处理。

df = pd.read_csv('train_titanic.csv')
df.head(10)

作者图片

让我们看看是否有大量数据丢失:

cols = df.columns 
colours = ['darkblue', 'red'] 
sns.heatmap(df[cols].isnull(), cmap=sns.color_palette(colours))

作者图片

在上面的热图中,x 轴显示列名,y 轴显示行的索引。红色小条表示该行的特定列中缺少某个值。似乎“年龄”和“客舱”这两个特性缺少很多值。让我们看看百分比:

pct_list = []
for col in df.columns:
    pct_missing = np.mean(df[col].isnull())
    if round(pct_missing*100) >0:
        pct_list.append([col, round(pct_missing*100)])
    print('{} - {}%'.format(col, round(pct_missing*100)))

作者图片

特征“小屋”丢失了 77%的数据。所以我要去掉这个特征。然而,年龄丢失了 20%的数据。在这种应用中,年龄应该是一个重要的变量,因为它肯定影响了存活的概率(例如,老年人或儿童可能被给予优先权)。通常,我会用其他人的平均年龄来填充缺失的值。然而,在这个特定的数据集中,人们来自不同的阶层,因此将他们作为一个群体来对待并不是一个好主意。数据集具有特征“姓名”,该姓名具有人的头衔(例如“先生”、“小姐”…等)。这个标题应该是这个时代的一个重要标志。此外,我应该记住,在事件发生时(1912 年),社会经济地位影响人们的头衔,而不考虑年龄(例如,年轻的富人可以获得头衔,而同龄的普通穷人则不能)。因此,我将根据人们的头衔和级别对他们进行分组,然后将每组的平均年龄分配给每组中的缺失年龄。

# extracting the title from the name:
Title = []
for name in  df.Name:
    Title.append(name.split(",")[1].split(".")[0])

df["Title"] = Title#grouping people with pclass and title
df.groupby(["Pclass", 'Title'])['Age'].agg(['mean']).round(0)# adding the mean of the age of each group to the missing values
df["Age"] = df.groupby(["Title", "Pclass"])["Age"].transform(lambda x: x.fillna(x.mean()))

现在,我还可以删除不需要的特征,如姓名(从中提取标题之后)、机票 ID、乘客 ID。

df = df.drop(columns = ["Name"])
df = df.drop(columns = ["PassengerId"])
df = df.drop(columns = ["Ticket"])

最后一步,我将把分类特征编码成数字:

df.Sex = pd.Categorical(df.Sex)
df.Embarked = pd.Categorical(df.Embarked)df["Sex"] = df.Sex.cat.codes
df["Embarked"] = df.Embarked.cat.codes

我将从 x 数据集中删除“生存”结果变量

target = df.Survived.values
df = df.drop(columns =["Survived"])

构建线性模型

最后,我要建立模型。我将使用一个简单的逻辑回归模型,因为这里的目标是看特征如何影响结果,而不是在预测中获得高分。

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df, target, test_size=0.2, random_state=0)
from sklearn.linear_model import LogisticRegression
LR = LogisticRegression()
LR.fit(x_train, y_train)
LR.score(x_test, y_test)

该模型达到了 82%的准确率,这对于我们的目标来说是合理的。

现在,我们可以开始有趣的部分了。我将用 SHAP 来解释这个模型,看看这些特征是如何影响泰坦尼克号的发生的。

SHAP 的模型解释

SHAP 是一个伟大的模型解释工具。尽管这是一个复杂的模型,但理解起来很直观。SHAP 的目标是提供每个特征对结果变量影响的可视化。为了做到这一点,SHAP 将建立一个模型,使用所有的功能,除了一个感兴趣的功能,看看这个模型没有这个功能会怎么样。然后,它将再次构建模型,并使用该特征进行预测。那么特征的效果将是两个值之间的差。但是将特征传递给模型的顺序会影响输出(尤其是在基于树的模型中,其中模型遵循由特征排序的示意图方法)。因此,SHAP 计算了所有可能的排列,不同的特征以这些排列传递给模型。这似乎有巨大的计算成本,但 SHAP 优化了算法,使其对特定的机器学习模型更快。

让我们看看 SHAP 的地图:

import shap
explainer = shap.LinearExplainer(LR, x_train, feature_perturbation="interventional")
shap_values = explainer.shap_values(x_test)
shap.summary_plot(shap_values, x_test)shap.summary_plot(shap_values, x_train, plot_type="bar")

作者图片

作者图片

正如我进行的统计推断所预期的,Pclass 对乘客的存活率有显著的影响。这是仅次于“性”的第二大特征。我们从上面的图中看到,对应于 1 级(较富裕的人)的 Pclass 的低值(蓝色)对人的存活率有积极影响,而对应于第三级的较高值(红色)对存活率有消极影响。我们还可以看到,“性别”是最重要的特征,表明作为“女性”(蓝色)对存活率有积极的影响。特征“年龄”也显示较低的值(蓝色)对存活率有积极的影响。

让我们来看看可变票价,即每个人支付的票价。这个变量应该是对人们财富的连续描述:

shap.dependence_plot("Fare", shap_values, x_test)

作者图片

我们看到,人们付出的多少和他们生存的机会之间存在线性关系。他们越富有,生存的可能性就越大。

最后,让我们更仔细地看看几位乘客:

shap.force_plot(explainer.expected_value, shap_values[0,:], x_test.iloc[0,:], link="logit")

作者图片

那是一个没有活下来的乘客的情节。情节表明,他的“性别”(作为男性)和他的“阶级”(处于第三阶级)正在降低他的存活率。该图还显示,兄弟姐妹的数量(“SibSp”)为 0 会略微增加他的机会。似乎没有家人陪伴独自在船上的人能够不受干扰地跑得更快。

让我们来看看一个幸存的人:

shap.force_plot(explainer.expected_value, shap_values[3,:], x_test.iloc[3,:], link="logit")

作者图片

不出所料,她是一班付了高价车费的女生。这给了她更高的生存机会。此外,她有一个兄弟姐妹,而且年纪大了,这也减少了她的机会。

这些结果可以用下面的力图来总结:

作者图片

该图显示了积极(蓝色)或消极(红色)影响每位乘客存活率的特征。

结论

根据我进行的统计分析和模型解释,我可以对泰坦尼克号上发生的事情做出以下归纳。当船开始下沉时,富人有优先离开船的权利。那些兄弟姐妹较少的人速度更快,因为他们不必寻找他们的家人。当他们发现救生艇数量有限时,他们决定优先考虑儿童和妇女。所以优先顺序如下:有钱的女人和孩子,有钱的男人,然后是其他人。非常有趣的是,这种见解是如何从数据集中完全提取出来的。

请在这里找到我的笔记本。你必须下载并运行笔记本来载入 SHAP 的地图,因为它们依赖于 Javascript

参考文献

通过 Python 使用 MongoDB

原文:https://towardsdatascience.com/using-mongodb-with-python-bcb26bf25d5d?source=collection_archive---------13-----------------------

照片由 Unsplash 上的 h heyerlein 拍摄

在这篇文章中,我们将学习如何在 Python 中使用 MongoDB。MongoDB 是一个非常快速灵活的 NoSQL 数据库。它非常受欢迎,应用广泛。

在潜水之前,让我们先来学习三个我们会经常用到的术语。

  • 文档: MongoDB 是一个文档数据库,这意味着集合中的每条记录都是一个文档。
  • 集合:一个集合可以存储多个文档。
  • 数据库:数据库保存文档的集合。

我们来深入探讨一下。

目录

1.安装 MongoDB

我将使用 MongoDB Atlas 免费层,这样您就不需要在您的计算机上安装 MongoDB,这样我们将只关注 MongoDB 和 Python。你可以从这里得到。在那里创建一个集群并设置你的账户。

建立帐户后,不要忘记将您当前的 IP 地址添加到白名单中,并创建一个 MongoDB 用户,否则您将无法访问您的集群。

安装 PyMongo

我们需要安装pymongodnspython。我们需要dnspython来连接mongo+srv协议。要安装两者,可以使用下面这个命令。

pip install pymongo[srv]

2.连接到您的集群

设置好账户后,点击控制面板中的connect按钮,然后点击connect your application,再选择Python driver。您将在那里看到您的连接字符串,复制它。

不要忘记将admin<password>占位符替换为您在设置用户时创建的用户名和密码。请注意,此用户不是您注册时的用户,而是您在创建集群后创建的用户。

我将使用环境变量,因为我不想公开我的用户信息。您可以简单地将user变量分配给用户名,将password变量分配给密码。

3.创建数据库

连接到您的集群后,您可以作为client的属性访问您的数据库。所以当我们说client.test时,我们基本上是访问一个名为test的数据库。如果我们还没有test数据库,它将在插入一个文档到数据库后被创建。我们将在本节中看到如何做到这一点。

立正!如果您的数据库名称是test-db,您将不能使用类似client.test-db的属性样式。你应该使用字典风格。client[‘test-db’]会起作用。

让我们看看我们的test数据库是否存在。运行下面的代码后,您期望哪个输出?输出将是test database was not created.,因为我们还没有向test数据库插入任何文档。一旦我们将文档插入到数据库中,数据库就会被创建。

在数据库中创建集合

让我们创建一个名为col的集合,看看会发生什么。检查输出,我们没有名为test的数据库和名为col的集合。让我们插入一个文档,看看会发生什么。

插入文档

首先,我们需要创建一个user字典来将文档插入到集合中。user字典的关键字将是文档的字段,user字典的元素将是文档中该字段的值。

我们将把user插入到col集合中,我们可以用insert_one的方法来完成。插入这个文档后,您可以获得它的 id。insert_user.inserted_id是 bson 对象,不是常规字符串。

如果您没有自己指定_id字段,它会自动添加到您的文档中。

你可以很容易地在你的面板中创建一个数据库和集合,但我也想用这种方式来展示。

4.插入文档

到目前为止一切顺利。我们已经创建了我们的test数据库,我们可以使用client的属性访问风格来访问它。现在我们可以在这个数据库上创建一个名为blog的集合,并将文档插入到这个集合中。

我们首先选择test数据库,然后db指向该对象。然后我们创建一个blog集合,blog_collection指向那个对象。

我们可以在插入文档后检索它的 id。请参见我们文档第 13 行插入的 id。

在将第一个文档插入到blog集合之后,博客集合就在我们的数据库test上创建了。

我想自己指定文档的 id,可以吗?

是的,你可以!_id会自动确定,除非你亲自动手。我自己来,把我的_id设为1

要非常小心!如果您插入另一个带有1_id的文档,id 在文档中应该是唯一的。您将得到一个重复的密钥错误。身份证就像你的公民身份证,他们应该是唯一的,以被正确识别。

5.插入多个文档

我们可以插入多个文档,看看如何看到代码片段。我希望你在这个片段中关注 3 件事。

  • 首先,我们需要传递一个字典列表来插入多个文档,其中每个字典代表一个文档,检查第 10 行的posts变量。
  • 其次,看看我们的文档有多灵活,不需要模式!每个文档都不一样!你可以在 MongoDB 中做到,没有什么可以阻止你去做!
  • 第三,看插入多个文档和一个文档的区别,我们用insert_many代替insert_one,用inserted_ids代替inserted_id

我们将把posts变量作为第一个参数传递给insert_many方法。

6.更新一个文档

我们可以更新数据库中的文档。我们将使用update_one方法对我们的数据库执行一次更新。让我们回顾一下所有的步骤,并很好地理解如何去做。

  • 首先,我在第 9 行检索匹配我们的query过滤器的文档。我这样做是因为我想在更新之前/之后给你看。
  • 第二,我们准备一本字典,然后new_document指向它。重点看那本字典,‘**$set**’{'content': 'I love MongoDB and Python', 'title': 'Updated Post'}。我们这里基本讲的是设置 contentI love MongoDB and Python``titleUpdated Post
  • 最后,我们检索更新后的文档,看看它是否有效。看看updated_document的输出,我们的修改被应用了。Id 和其他我们没改的还是一样,只有contenttitle按照我们的本意改了。

update_one方法的第一个参数是你的过滤器,第二个参数是你要应用的修改和upsert。如果您将upsert设置为True,如果没有符合您的过滤器的文件,它将插入一个新文件。默认是False

如果有多个文档与您的过滤器匹配,将更新第一个匹配项。

7.更新多个文档

您博客中的一个用户已经更改了用户名,因此,您想要更新他的所有文档,以便将他的旧用户名更改为新用户名。我们将使用update_many方法更新多个文档。

8.获取一个文档

目前我们已经填充了数据库。现在我们想搜索我们的数据库,并检索与我们的查询相匹配的文档。

我们将使用find_one方法。我们将把{‘title’: ‘Second’}字典作为第一个参数传递给这个方法。通过传递这个字典,我们基本上告诉我们要从blog集合和test数据库中检索一个标题为Second的文档。我们只使用title进行过滤,但是我们可以向我们的{‘title’: ‘Second’}字典中添加更多像‘user’: ‘halilylm’这样的字段。

注意document是一个字典,所以您可以使用 get 函数来检索第 12 行的内容。

如果我们有不止一个带有Secondtitle的文档怎么办?向下滚动到下一个例子,看看会发生什么!

如果有多个文档匹配我们的查询,我们只检索第一个匹配。请参见下面的示例。有多个文档与我们的查询匹配,我们只得到第一个匹配项。

9.通过 id 获取文档

让我们假设,您创建了一个博客,并希望按 id 显示一篇文章。URL 是/blog/posts/5ed0254d97e5367d20c22e63,而5ed0254d97e5367d20c22e63是帖子的 id。因为这是一个普通的字符串,如果你用它作为_id,你将得到None。因为这是一个普通的字符串,而不是 bson 对象。您需要将这个字符串对象转换为 bson 对象。我假设这里的 id 是自动添加的,而不是你自己指定的。

您只需要导入ObjectId并将插入的 id 作为第一个参数传递给它。

我还想排除titlecontent,我可以传递一个我想排除的字段字典,并将字段设置为0作为find_one方法的第二个参数。看输出,titlecontent排除在外。

10.获取多个文档

您也可以一次获得多个文档。让我们尝试检索具有halilylmuser字段的所有文档

11.更复杂的查询

查询并不总是简单的。如果你在你的博客中建立一个搜索系统,你可能想要使用更复杂的查询。我的意思是,当一个用户搜索一个关键字时,你也希望显示与该关键字相关的帖子,而不仅仅是与用户完全匹配的帖子。

假设我们想要显示文档以及这些文档中以I love开头的content字段。你会怎么做?我们将使用正则表达式来实现这一点。我将传递一个字典来过滤我们想要的结果。这本字典将作为第一个参数传递给find方法。

我还想排除idpost_dateuser。我将把字典作为第二个参数传递给find方法。

看到输出,我们得到所有匹配我们的查询的文档。

12.删除一个文档

我们将使用delete_one方法从数据库中删除一个文档。

我们想要删除一个带有First posttitle的文档,因此我们需要将{'title': ‘First post'} dictionary 作为第一个参数传递给delete_one方法来定义我们到底想要删除哪个文档。

您可以使用deleted_count属性访问方式检查删除了多少个文档。第 9 行的输出是 1,因此一个与我们的查询匹配的文档如预期的那样被删除。

如果有多个文档匹配该查询,该怎么办?答案是只有第一个匹配会被删除。

13.删除多个文档

我们可以一次删除多个文档。假设halilylm用户已经删除了他的账户。我们想删除所有属于他的帖子。我们将使用delete_many()方法来执行此操作。

立正!如果您将空字典传递给delete_many()方法,它将从该集合中删除所有文档!非常小心!!!

14.限制性的

假设我们的查询与2 posts匹配,我们希望将结果限制为仅与1 post匹配。我们可以限制从数据库中获取的文档数量。为此,我们将使用limit方法。只需将您想要从我们的数据库中检索多少个文档传递给limit方法

看看输出,我们只得到一个文档。输出更长,因为这次我没有排除任何字段。

15.整理

我们将使用sort方法按照title字段对结果进行排序。

将字段名作为第一个参数传递给sort方法。将1–1作为第二个参数传递给sort方法。然而,你不必通过1来指定方向,因为默认是升序。

排序('标题',1)升序(默认)
排序('标题',-1)降序

我们先按升序排序,排除idpost_dateusercategory字段。

注意,一个空字典向find方法传递了第一个参数。这意味着你不想应用任何过滤器。第二个参数是要排除的字段的字典。

对于降序,只需将-1放在sort方法的第二个参数中。

结论

我希望您喜欢这篇文章,并且很好地理解了如何在 Python 中使用 MongoDB。敬请关注未来的作品。

利用地铁数据安置志愿者——深入研究数据科学

原文:https://towardsdatascience.com/using-mta-turnstile-data-to-place-optimize-placement-of-volunteers-and-start-a-career-in-data-1bece6a33f56?source=collection_archive---------41-----------------------

介绍

我最近开始了一个 Metis 数据科学训练营,加入了大约 55 名其他有抱负的数据科学家,为期 12 周,充满了编码、讲座、挑战和项目。我有生物学背景。我在一月份完成了旧金山州立大学海洋生物学的硕士学位。我进入 2020 年时,计划做一名实验室技术员或其他一些低级别的移液器猴子,同时我找到了一条具体的职业道路,但新冠肺炎全球疫情完全打乱了这个计划。作为一个住在新城市(俄勒冈州波特兰市)的 26 岁年轻人,我发现自己在寻找一些职业方向和日常目标。我对编码产生了浓厚的兴趣,并很快开始利用这些技能研究职业。我很快找到了 Metis,正式开始了我的数据科学之旅。

该项目

在这里,我描述了我在 Metis 课程中的第一个项目的经历——也是我的第一个数据科学项目(查看回购)。任务是分析纽约市 MTA 地铁数据,以便安排志愿者招募客人参加虚构公司 WomenTechWomenYes 举办的年度晚会。

许多人已经分析了这个数据集(google it),当我着手这个项目以及一个新的博客时,我想知道自己:我能贡献什么?作为一名数据科学新手,我怎么能希望提供其他更有经验的数据科学家错过的启示呢?这似乎不太可能。相反,我想通过我自己的视角与你分享这个项目:作为一个全新的、天真无邪的数据科学家,他希望深入到数据科学的本质、数学和 Python 驱动的世界中。

通过分享我在这个项目中的思考过程,我希望能阐明为什么我认为数据科学如此有用——如此彻头彻尾的。当我分享我的分析的代码片段和输出时,我希望其他新兴的数据科学家能够跟随并受到启发,去挖掘许多许多等待分析的可用数据集。因为归根结底,我的目标是让更多的人对数据科学感兴趣,数据科学是一套广泛适用的技能,以解决问题和帮助人们为中心。让我们开始吧。

该项目的第一步是找到并导入数据集,以便我们可以进行一些 EDA(首字母缩写词在数据科学中非常重要)。

从 MTA 网站加载了数周的十字转门数据:

df_1 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190323.txt')
df_2 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190330.txt')
df_3 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190406.txt').
.
.df_12 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190608.txt')
df_13 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190615.txt')
df_14 = pd.read_csv('http://web.mta.info/developers/data/nyct/turnstile/turnstile_190622.txt')Mta_2019_df_input_1 = pd.concat([df_1, df_2, df_3, df_4, df_5, df_6, df_7, df_8, df_9, df_10])mta_df = pd.concat([Mta_2019_df_input_1, df_10, df_11, df_12, df_13, df_14])

由此产生的数据帧非常壮观:

作者图片

我一点也不知道清理和使用这个数据集会有多么困难!但这是有趣的部分。

清理的第一步是添加一些列并清理列名:

mta_df['DATETIME'] = pd.to_datetime(mta_df['DATE'] + ' ' + mta_df[“TIME”], format=”%m/**%d**/%Y %H:%M:%S”)mta_df['TURNSTILE'] = mta_df['C/A'] + ' ' + mta_df['UNIT'] + ' ' + mta_df['SCP'] + ' ' + mta_df['STATION']

然后删除一些重复项:

mta_df.drop_duplicates(subset=['C/A', 'UNIT', 'SCP', 'STATION', 'DATETIME'], inplace=**True**)

然后我们制作了一个新的框架,它由四个属性组成,这四个属性共同代表了一个独特的十字转门:C/A、Unit、SCP 和 Station(每一个都是 MTA 十字转门的标识符)。

mta_daily = (mta_df.groupby(['C/A', 'UNIT', 'SCP', 'STATION', 'DATE'],as_index=**False**).ENTRIES.first())mta_daily.head()

作者图片

清理工作完成后,我们准备开始分析,这将有助于我们实现目标:通过瞄准最繁忙的车站和十字转门,最大限度地发挥志愿者的影响力。但是我们看到了另一个问题:十字转门的入口和出口是累积的,而不是代表自上次观察以来的乘客(每四小时观察一次)。

作者图片

我不知道如何解决这个问题,并得到我们的进入和退出总数在尼斯四个小时的增量。这对我来说是这个过程中的一个重要时刻。我甚至对这个简单的问题感到不知所措,觉得自己缺乏真正成为数据科学家的技能(有人告诉我,冒名顶替综合症是许多技术领域的常见疾病)。我感到很沮丧。

然而,我并不是独自进行这项分析:这毕竟是一个团队项目,而且最重要的是,我们有愿意提供帮助的导师。经过一天左右的努力,我们的一位导师向我们展示了一个真正优雅的解决方案,它允许我们将累积的进出变成每日的进出:

# Adding previous date and previous entries, so that we can look at the deltas:mta_daily[["PREV_DATE", "PREV_ENTRIES"]] = (mta_daily.groupby(["C/A", "UNIT", "SCP", "STATION"])["DATE","ENTRIES"].apply(lambda grp: grp.shift(1)))# Dropping top row of NaN's:
mta_daily.dropna(subset=["PREV_DATE"], axis=0, inplace=True)# Where is the counter going the wrong way?
mta_daily[mta_daily["ENTRIES"] < mta_daily["PREV_ENTRIES"]].head()# Fixing counter
def get_daily_counts(row, max_counter):
    counter = row["ENTRIES"] - row["PREV_ENTRIES"]
    if counter < 0:
        # Counter reversed?
        counter = -counter
    if counter > max_counter:
        # Counter reset to 0? 
        counter = min(row["ENTRIES"], row["PREV_ENTRIES"])
    if counter > max_counter:
        # Still too big?
        return 0
    return counter# finally adding our daily entries column!
mta_daily["DAILY_ENTRIES"] = mta_daily.apply(get_daily_counts, axis=1, max_counter=40000)mta_daily.head()

作者图片

耶!现在我们有了一个每日条目的专栏(我们只关注项目剩余部分的条目)。

除了有助于我们继续分析之外,这种合作干预揭示了一个极其重要的启示:在解决这个问题时,我不是一个人。这是一个团队项目,我们的教练已经表明他会在那里帮忙。

事后看来,这似乎是一个显而易见的观察。Metis 强调项目的协作性,我确实在这个项目上与一个小组一起工作。但我想强调这一系列事件,因为我真的觉得这是一个“水平上升”的时刻。我从来没有合作编写过代码,在进入一个新的程序时,我想向自己和他人证明我有能力去执行和竞争。然而,我觉得更多的人应该牢记一起工作和寻求帮助的教训。

带着一种“一切都会好起来”的新感觉,我们继续分析。我们现在有每个车站的每日入口和出口,我们想找到最繁忙的车站。我们首先按电台对数据帧进行分组,并按每日条目进行排序,以查看前 10 名:

top10_stations = \
    (stations_daily.groupby(['STATION'])['DAILY_ENTRIES'].sum()
                   .reset_index()
                   .sort_values(by='DAILY_ENTRIES',ascending=**False**) 
                   .STATION.head(10)) 

然后,我们提取了数据帧中属于前 10 名的部分:

station_daily_top10 = \
    stations_daily[stations_daily['STATION'].isin(top10_stations)].sort_values(by = 'DAILY_ENTRIES', ascending = **False**)

最后在 Seaborn 用标准的箱线图绘制出来:

sns.boxplot(x='DAILY_ENTRIES', y='STATION', data=station_daily_top10, order = top10_stations, \
            showfliers = **False**).set_title('10 Busiest Subway Stations', size = 15);

作者图片

厉害!通过几行代码,我们从一个庞大、杂乱的数据集中提取了对我们的客户有价值的第一条信息:纽约市 10 个最繁忙的地铁站!接下来,我们使用 geo_pandas 在地图上显示了最繁忙的车站:

top10_stations_geo = combined_df[combined_df['STATION'].isin(top10_stations)]fig, ax = plt.subplots(1, figsize=(10, 10))
districts_geo.plot(ax=ax)
top10_stations_geo.plot(ax=ax, column = top10_stations_geo['DAILY_ENTRIES'], \
                        legend=True, cmap = 'hot', markersize = 50)

作者图片

聚焦曼哈顿:

manhattan = districts_geo[districts_geo['boro_cd'].astype(int) <200]
fig, ax = plt.subplots(1, figsize=(20, 20))
manhattan.plot(ax=ax)
top10_stations_geo.plot(ax=ax, column = top10_stations_geo['DAILY_ENTRIES'], \
                        legend=True, cmap = 'hot', markersize = 80)

作者图片

漂亮!根据我们的分析,我们现在可以向客户展示最佳目标站的准确位置。

在这一点上,我们几乎完成了,我觉得完成了。该项目进展顺利,我们几乎准备好交付最终演示。

我们项目的最后一个方面是将人口统计数据整合到我们的分析和推荐中。首先,我们阅读从 data.cccnewyork.org 获得的人口统计数据,这是一个专注于追踪纽约市收入的网站。我们把注意力集中在纽约最富裕社区的名单上,理由是越富裕的人越有可能在晚会上捐款。

bougie_df = pd.read_csv('Income_Stations_by_Neighborhood.csv')
bougie_df.head()

然后,我们编写了一个函数,将人口统计数据框架中的电台名称与“电台 _ 每日”框架中的电台名称进行匹配。

def get_station_name(row, col_name, station_names, check_length = False):
    """
    Takes in a row of a dataframe and matches the value of the column labeled by 
    col_name with the values in station_names
    """
    row_name = row[col_name]
    row_name = re.sub('th', '', row_name)
    row_name = re.sub('rd', '', row_name)
    row_name = re.sub('nd', '', row_name)
    row_name = re.sub('-', '', row_name).upper()
    similarity = 0
    similar_name = ''
    for name in station_names:
        if check_length:
            if len(name) < 14:
                name += "".join(['-' for i in range(len(name), 14)])
        ratio = SequenceMatcher(None, row_name, name).ratio()
        if ratio > similarity:
            similarity = ratio
            similar_name = name.strip('-')
    return similar_name

我们将这些站名添加到我们的人口统计数据框架中:

bougie_df['MTA_name'] = bougie_df.apply(get_station_name, axis = 1, col_name = "MAP NAME", \
                                        station_names = stations_daily['STATION'].unique())bougie_df.head()

然后我们交叉参考这些车站,找出富裕社区中最繁忙的车站:

bougie_list = bougie_df['MTA_name']bougie_stations = \
    stations_daily[stations_daily['STATION'].isin(bougie_list)].sort_values(by = 'DAILY_ENTRIES', ascending = False)
bougie_top10 = \
    (bougie_stations.groupby(['STATION'])['DAILY_ENTRIES'].sum()
                   .reset_index()
                   .sort_values(by='DAILY_ENTRIES',ascending=False) 
                   .STATION.head(10))

最后,我们绘制了这些社区中最繁忙的 10 个车站:

sns.boxplot(x='DAILY_ENTRIES', y='STATION', \
            data=bougie_stations[bougie_stations['STATION'].isin(bougie_top10)], \
            order = bougie_top10, showfliers = False);

作者图片

原来如此!最后一个交付给我们客户的东西:纽约最富裕社区的十大最繁忙地铁站。

最后的想法

在完成了我的第一个数据科学项目之后,我感觉自己已经跨越了一个里程碑。不是戏剧性的,但在 Metis 的筹备和第一周期间,我有无数次合理地不确定自己是否能做到,我的数学、编码和分析技能是否达到 Metis 为学生设定的高标准。完成这个项目后(回购!),我感觉我可以在数据科学方面取得成功,我真的很兴奋能继续这个训练营。

我最近开始着手我的第二个项目(我很快会写博客,别担心),从第一个项目中获得的信心支撑着我。如果您没有从这篇博客文章中获得任何其他东西,我希望您可以将我的数据科学之旅的开始视为灵感,以真正涉足您正在做的任何事情,并知道您周围总有人可以帮助和支持您!下次见。

使用朴素贝叶斯从零开始创建国际象棋人工智能

原文:https://towardsdatascience.com/using-naive-bayes-to-create-a-chess-ai-from-scratch-e35bc64ef109?source=collection_archive---------53-----------------------

皮奥特·马科夫斯基在 Unsplash 上的照片

国际象棋一直被认为是智力和智力的测试。1996 年,在国际象棋大师加里·卡斯帕罗夫和 IBM 的深蓝之战后,它也成为了人工智能的一块基石。因为它的重要性,我认为尝试创造一个有能力的象棋人工智能会很有趣。

为了实现创建一个有能力的国际象棋人工智能的目标,我开发了一个朴素贝叶斯分类器,使用概率来寻找最佳可能的移动。

我使用的数据集有超过 20,000 个游戏。可惜我再也找不到源头了。如果你正在寻找数据集,你可以联系我在 victorwtsim@gmail.com。

朴素贝叶斯分类器:

朴素贝叶斯分类器依赖于使用从数据集收集的概率来做出最佳决策。对于国际象棋,这是如何实现的?以下是一个逐步指南:

第一步:统计每一步棋在输赢游戏中出现的次数。

第二步:将每一个计数值,除以输局的总棋数和赢局的总棋数,变成一个概率。

第三步:利用贝叶斯定理计算出获胜概率最高的招式。

代码:

第一步:在一场输和赢的游戏中,每出现一个招式,就数一次。

from pandas import read_csv
import chess
file = '/Users/XXXXXXX/Desktop/Files/Data/chess.csv'
csv = read_csv(file)outcomes = list(csv['winner'])
moves = list(csv['moves'])
for i in range(len(moves)):
    moves[i] = moves[i].split()
white_moves = 0
black_moves = 0

在进入实际程序之前,这些是我用来访问我的数据的先决条件。如果您可以访问数据集,您可以复制它。

def initialize_dictionaries():
    white = {}
    black = {}
    all_moves = []
    for game in moves:
        for move in game:
            if not(move in all_moves):
                all_moves.append(move)
    for move in all_moves:
        white[move] = 1
        black[move] = 1
    return white,blackdef initialize_data(white,black):
    white_moves = 0
    black_moves = 0
    for outcome in outcomes:
        index = outcomes.index(outcome)
        if outcome == 'white':
            work_dict = white
            white_moves += len(moves[index])
            ip2 = 0
        elif outcome == 'black':
            work_dict = black
            black_moves += len(moves[index])
            ip2 = 1
        for move in moves[index]:
            if moves[index].index(move) % 2 == ip2:
                if move in work_dict:
                    work_dict[move] += 1
    return white,black,white_moves,black_moveswhite,black = initialize_dictionaries()
white,black,white_moves,black_moves = initialize_data(white,black)

这两个函数查看数据,将游戏的结果与移动匹配,并相应地添加每个移动在输赢游戏中出现的次数。我以 Python 字典的形式存储数据,以支持基于文本的索引。

第二步:将每一个计数值除以输局的总棋数和赢局的总棋数,变成一个概率。

def to_probability(dictionary,length):
    for term in dictionary:
        dictionary[term] = dictionary[term]/length
    return dictionary
white = to_probability(white,white_moves)
black = to_probability(black,black_moves)

这个函数非常简单,几乎不需要解释:用一个移动出现的次数除以移动的总数,得到一个概率。

第三步:利用贝叶斯定理计算出获胜概率最高的招式。

贝叶斯定理指出,假设 B 发生了,A 发生的概率是 B 发生的概率,当 A 发生时,乘以 A 发生的概率,除以 B 的概率。

让我们用赢来代替 A,用移动 X 来代替 B。赢的概率,给定移动 X 被使用,可以计算为:移动 X 在赢的配置中出现多少次,乘以总的赢的概率,再乘以移动 X 被使用的概率。

PA = outcomes.count('white')/len(outcomes)
PB = 1
PBgiveA = white[move]/outcomes.count('white')
whitePAgiveB = (PBgiveA * PA) / PB

这里的代码是这个概念的应用,给出了白棋赢的概率。请注意,移动 X 的概率是 1,如果我们想移动,移动有 100%的机会发生。

完整的功能是:

def calculate_probability(side,board,white,black,white_moves,black_moves):
    len_white = white_moves
    len_black = black_moves
    if side == True:
        color = 'white'
    else:
        color = 'black'
    legal_moves = str(board.legal_moves)[36:-2].replace(',','').split()
    probs = []
    for move in legal_moves:
        if not(move in white):
            len_white += 1
            white[move] = 1/len_white
        PA = outcomes.count('white')/len(outcomes)
        PB = 1
        PBgiveA = white[move]/outcomes.count('white')
        whitePAgiveB = (PBgiveA * PA) / PB
        if not(move in black):
            len_black += 1
            black[move] = 1/len_black
        PA = outcomes.count('black')/len(outcomes)
        PB = 1
        PBgiveA = black[move]/outcomes.count('black')
        blackPAgiveB = (PBgiveA * PA) / PB
        if color == 'white':
            probs.append(whitePAgiveB-blackPAgiveB)
        elif color == 'black':
            probs.append(blackPAgiveB-whitePAgiveB)
    final_move = legal_moves[legal_moves.index(max(legal_moves))]
    return white_moves,black_moves,final_move

结果:

我试着用这个算法和它自己玩。它对开局和理想位置有很好的感觉(比如确保中路),但在捕捉棋子时有问题。

我认为这是因为朴素贝叶斯分类器只知道合法的移动,而不掌握棋盘上的整体状态。

使用自然语言处理来比较病毒

原文:https://towardsdatascience.com/using-natural-language-processing-to-compare-viruses-6a281311d9a?source=collection_archive---------75-----------------------

可能加速病毒疫苗开发的研究

疾控中心Unsplash 拍摄的照片

N 编者按: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情电晕病毒的信息,可以点击 这里

2019 年 12 月,世界开始了解一种新病毒,俗称冠状病毒,科学上称为新冠肺炎。

当冠状病毒被宣布为全球疫情时,开发疫苗的竞赛就开始了。

假设:这篇文章提供了我对自己参与的一项研究的见解。提供的图片是我个人电脑的截图。我要感谢 Nikhil Malhotra 和 Akshit Kothari 给我这个机会参与这个研究项目。

本文旨在提供两种病毒与其蛋白质结构之间的简单可能的比较,并为加速病毒的分析提供可能的基础。

那么我是怎么做到的呢?

弗兰基·查马基Unsplash 上拍摄的照片

数据是 AI 中最重要的实体!

但是病毒的数据大部分是一系列的化学化合物,这将超出我这个计算机专家的理解范围;).所以我花了一些我的互联网数据来建立一个病毒学的基础。

我的病毒学竞赛…

  1. 什么化合物导致病毒攻击人体?
  2. 它们形成什么类型的联系?(因为我知道一些…但不多)
  3. 病毒遗传对疫苗开发有影响吗?(我知道这似乎是显而易见的,但我想确定一下)
  4. 我能得到蛋白质结构的 3D 图吗?
  5. 我如何利用蛋白质结构来比较两种病毒?

所以,是的…这些是我的一些主要问题。我必须从某个地方开始,所以我从向量空间映射开始。

好的……那么什么是向量空间映射,我该如何使用它呢?

埃文·丹尼斯Unsplash 上拍摄

简单来说,自然语言处理中的向量空间映射就是将文本处理成‘向量’(耶…字面意思)。更简单的说法是字嵌入。在单词嵌入中,通过给单词分配标记,将单词转换成数字。在我的例子中,我有像 C,O,N(碳,氧,氮)这样的原子,所以我标记了每个原子。

到了完成矢量化的部分…

我需要文本格式或任何可读格式的病毒蛋白质结构。蛋白质数据库(PDB)通过建立不同病毒的各种蛋白质结构的巨大数据库来解决这个问题。“pdb”格式。

[## RCSB·PDB

作为 wwPDB 的成员,RCSB PDB 根据公认的标准管理和注释 PDB 数据。RCSB PDB…

www.rcsb.org](https://www.rcsb.org/search)

这是. pdb 文件的样子…

我的电脑截图

  1. CA '-α碳,' CB '-β碳
  2. 赖氨酸、ILE 等是氨基酸(一些化学术语)

我感兴趣的是三列中的浮点数。这些数字代表作为第一个肽链“1”一部分的相应原子的“x”、“y”、“z”坐标,依此类推。

为了获得这些坐标(如上所述),我需要一些能够读取每一列的包或任何阅读器。我偶然发现了 Biopython,一个处理 PDB 文件的 python 模块。

[## Biopython 教程和食谱

Biopython 项目是免费 python 开发者的国际协会(https://www.python.org

biopython.org](http://biopython.org/DIST/docs/tutorial/Tutorial.html#htoc162)

我开始从他们的 PDB 文件中获取蛋白质结构中每个原子的坐标,并通过将数据添加到数据框中来绘制坐标图。我有这样的东西…

“plotly”获得的麻疹病毒蛋白质结构截图

现在我们有了一个结构…一个 3D 结构,但是下一步呢?

现在最重要的部分——向量空间映射…

PDB 文件包含含有在 3D 空间中不同位置的原子的残基序列。我把每个原子组合成一个残数,然后把它转换成一个 NumPy 数组。

if(extension=='pdb'):
  data = parser.get_structure("pdb",main_virus_needed+'\\'+filename)
  initial_models = data.get_models()
  models = list(initial_models)
  for model in models:
    chains = list(models[0].get_chains())

    for chain in chains:
      residues = list(chains[0].get_residues())

      for residue in residues:
       atoms = list(residue.get_atoms())

       for atom in atoms:
         store_residue(atom)

store_residue()是一个函数,定义为通过以下方式获得矢量形式的原子坐标

<Atom object>.get_vector()

[## 生物。PDB,亚当,亚当

object — + | Atom 定义 Atom 类。atom 对象存储 Atom 名称(有和没有空格)、坐标、B…

biopython.org](https://biopython.org/DIST/docs/api/Bio.PDB.Atom.Atom-class.html#get_vector)

看起来我已经准备好了,但是我考虑了一个问题,如果一些蛋白质结构比另一些更小或更大,那么需要多一个步骤来解决这个问题……填充!

所以我决定添加零填充并匹配向量的形状,并使用np.ravel()得到一个连续的扁平 1D 数字数组。

为了比较,我决定用余弦相似度…

为什么余弦相似?

作为一个有计算机背景的人,我没有任何病毒学背景,也不知道病毒学家用来比较两种病毒的方法。所以为了建立基础,我用余弦相似度。我知道这不是最理想的方法,但它让我可以根据一些计算得出一些不准确的结论。

余弦相似度的基本公式

[## 余弦相似性

余弦相似性是内积空间的两个非零向量之间的相似性的度量。它被定义为…

en.wikipedia.org](https://en.wikipedia.org/wiki/Cosine_similarity)

一些结果…

以下是使用 Matplotlib… 与冠状病毒的蛋白质结构比较的不同病毒 I 的一些蛋白质结构的统计

  1. 麻疹
  2. 登革热
  3. 埃博拉病毒
  4. 丙型肝炎
  5. 艾滋病病毒
  6. 甲型流感
  7. 中东呼吸综合征
  8. 肺炎
  9. 严重急性呼吸综合征
  10. 轮状病毒

使用 Matplotlib 绘制

通过观察上面的统计,我对 SARS 和新冠肺炎的比较结果惊讶为 57.95%,因为他们来自同一个家族,相似度应该很高。但是伊波拉和轮状病毒也像新冠肺炎一样引起呼吸困难、咳嗽、胸痛等呼吸系统疾病,分别有 91.03%和84.71%的相似链。因此,只比较蛋白质链可能是加速疫苗开发的起点。

D 是声明者:结果需要实验室和从业者的验证

下面是解决问题的不同方法的链接—

[## 新冠肺炎蛋白质比较分析

计算分析:微生物的世界我们生活在一个世界,这个世界在一月份受到了一场…

www.linkedin.com](https://www.linkedin.com/pulse/covid-19-protein-comparison-analysis-nikhil-malhotra/)

“开头是作品最重要的部分!”

谢谢你通读这篇文章,我将非常乐意回应你的反馈

在数据块上使用 Neo4j 和 PySpark

原文:https://towardsdatascience.com/using-neo4j-with-pyspark-on-databricks-eb3d127f2245?source=collection_archive---------16-----------------------

理解大数据

释放 Spark 和图形数据库携手工作的全部潜力

Fernand De CanneUnsplash 上拍摄的照片

随着最近发布的利用 Spark 数据源 API 的 Apache Spark 官方 Neo4j 连接器,在 Apache Spark 环境中查询 Neo4j 数据的方式发生了根本性的变化。除了这一变化,以前的 Neo4j Spark 连接器被标记为不推荐使用。在本文中,我想分享一个更新的端到端工作流,该工作流设置 Neo4j 和 Spark 的完全互连配对,该配对利用了新连接器的功能。

在这个过程中,我们将首先使用 Azure 虚拟机设置一个 Neo4j 云实例。之后,我们将设置一个运行 Spark 的 Azure Databricks 实例,然后使用 Apache Spark 的新 Neo4j 连接器在两个资源之间建立连接。如果您已经有了一个运行良好的 Neo4j 或 Databricks 实例,那么您当然可以跳过相应的步骤。但是,请注意每个步骤顶部的兼容性信息。

步骤 1:设置 Neo4j 云实例

首先,简单说一下兼容性:我们将使用的连接器支持 Neo4j 及更高版本。不支持 3.5 之前的版本。但是,它支持 Neo4j 企业和社区以及单实例虚拟机、因果集群和 Neo4j Aura。本文将关注单实例虚拟机的工作流。

作为我们的 Neo4j 实例,我们将使用正式的 Neo4j 企业虚拟机映像。最新版本在 Azure marketplace 中被列为 Neo4j Enterprise VM 版本 4.1 。如果你没有企业许可证,有 Websoft9 提供的社区版本的图片以及 Bitnami 提供的容器图片:

Azure marketplace 上 N eo4j 的搜索结果(作者截图)

虚拟机部署后,导航到它的 Networking 选项卡,确保它的端口设置是正确的:为了从 Spark 查询它,一个 螺栓端口必须允许入站流量。默认情况下,这是端口 7687。此外,我们将使用 Neo4j Web 接口来填充数据库,为此我们需要一个开放的 HTTPHTTPS 端口。默认情况下,它们映射到端口号 7474 和 7473。如果缺少这些端口规则中的任何一个,点击添加入站端口规则按钮添加它们。

Neo4j 实例的入站端口设置(作者截图)

用数据填充 Neo4j 实例

验证端口设置后,从其概述选项卡中获取虚拟机的公共 IP,并通过导航到 http://YOUR。VM.IP.ADDRESS:7474 。对于新启动的虚拟机,Neo4j 可能需要几分钟才能启动并接受入站连接。如果您使用 VPN 或代理,请确保对它们进行了相应的配置,否则您可能会收到“此站点无法访问”错误。

如果所有设置都正确,将弹出 Neo4j 浏览器 Web UI:

Neo4j Web UI(浏览器)(作者截图)

系统将提示您输入登录凭据。输入登录数据库的默认用户名和密码:

  • 用户名: neo4j
  • 密码: neo4j

第一次访问时,出于安全原因,您必须立即更改默认密码。选择一个可靠的密码并登录后,就可以填充数据库了。在本指南中,我们将使用 Cypher 查询语言通过在命令行中键入以下内容来创建一个简单的虚拟数据集:

UNWIND range(1,100) as id
CREATE (p:Person {id:id}) WITH collect(p) as people
UNWIND people as p1
UNWIND range(1,10) as friend
WITH p1, people[(p1.id + friend) % size(people)] as p2
CREATE (p1)-[:KNOWS {years: abs(p2.id - p2.id)}]->(p2)

因此,将创建一个类似于下面的数据结构,其中每个 Person 节点都用一个 ID 标记,每个 KNOWS 关系都有一个附加的(任意的)属性 years ,它描述了双方认识多久了:

结果数据结构,限于 5 个节点(作者截图)

就是这样!我们的 Neo4j 图形数据库已经启动并运行,迫不及待地想从 Spark 中查询。我们现在将继续设置一个合适的 Spark 环境。

步骤 2: 配置火花环境

再次,关于兼容性的重要说明:在撰写本文时, Neo4j 不支持 Spark 3.0 的连接器。因此,我们将不得不退回到 Spark 2.4 环境,以便与 Neo4j 通信。

对于我们的设置,我们将使用 Azure Databricks 实例。在 Azure marketplace 上搜索数据块并创建一个新资源。除了通常的设置(资源组、名称、位置和定价层),不需要任何特殊的配置设置。

部署完成后,通过导航到 Azure 资源的概述选项卡并单击启动工作区按钮,打开 Databricks 工作区。

(作者截图)

首先,我们需要一个集群。导航到集群选项卡并单击创建集群按钮以显示集群配置工具:

Databricks 集群创建屏幕(作者截图)

选择集群名称和可用性模式,并根据您的喜好配置工作节点的大小和数量。现在,请记住,我们被迫使用 Spark 2 设置——幸运的是,Databricks 仍然提供各种 Spark 2.4.5 发行版。确保在 Databricks 运行时版本字段中选择其中之一,例如运行时版本 6.6 运行 Spark 2.4.5Scala 2.11

启动您的集群,一切就绪!

步骤 3: 在 Neo4j 和 Spark 之间建立连接

既然我们现在已经启动并运行了 Neo4j 和 Databricks,那么是时候关注它们之间的联系了。为此,我们需要将 Apache Spark 的 Neo4j 连接器添加到我们的 Databricks 集群中。

在数据块中,导航到集群选项卡。选择先前创建的集群并访问其库选项:

Databricks 集群视图(作者截图)

现在,通过点击安装新的按钮,选择 Maven 并点击搜索包,为 Apache Spark 添加 Neo4j 连接器。键入“neo4j”查看所有可用选项。在撰写本文时,搜索中出现了三个包:

(作者截图)

  • neo4j-spark-connector :这是弃用的连接器版本(2.4) 。它不再受到开发者的积极支持。
  • neo4j-connector-Apache-spark _ 2.11:这是当前的连接器版本(4.0),也是我们将要使用的。
  • neo4j-connector-Apache-spark _ 2.12:这也是当前的连接器版本,唯一的区别是它是为 Scala 版本 2.12 编写的。因为我们的 Databricks 环境运行 Scala 2.11,所以这个不适合我们的目的。

根据开发人员的说法,由于 API 的不同,将两个连接器分开是必要的:

由于 API 的差异,不同的 scala 版本需要不同的 JAR 文件。确保您有适合您的环境的 JAR 文件。

阅读 Neo4j

连接器安装完成后,在工作区选项卡中创建一个新的 Jupyter 笔记本(右击→ 新建)。在插入 Neo4j 虚拟机的 IP、用户名密码后,通过运行以下命令尝试读取 Neo4j 数据库的节点数据:

df = spark.read.format("org.neo4j.spark.DataSource")\
 .option("url", "bolt://***XXX.XXX.XXX.XXX***:7687")\
 .option("authentication.type", "basic")\
 .option("authentication.basic.username", "***neo4j"***)\
 .option("authentication.basic.password", "***password"***)\
 .option("labels", "Person")\
 .load()
display(df)

如果您收到无法连接错误,请确保您的 Neo4j 虚拟机仍在运行,并且其 Bolt 端口接受入站流量。如果查询返回

java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V

您很可能为错误的 Scala 版本安装了连接器——确保您选择的是 Scala 2.11 版本(见上文)。

如果一切都解决了,查询将返回如下所示的数据帧:

演示查询以数据帧格式返回的结果(作者截图)

作为 Python 的替代,您也可以通过在笔记本单元格顶部添加 %scala 魔法命令来使用 Scala 进行查询:

%scalaimport org.apache.spark.sql.{SaveMode, SparkSession}val spark = SparkSession.builder().getOrCreate()val df = spark.read.format("org.neo4j.spark.DataSource")
 .option("url", "bolt://***XXX.XXX.XXX.XXX***:7687")
 .option("authentication.type", "basic")
 .option("authentication.basic.username", "***neo4j"***)
 .option("authentication.basic.password", "***password"***)
 .option("labels", "Person")
 .load()display(df)

写入 Neo4j

写入图形数据库的工作方式类似。在下面的查询中,我们将更新原始数据,以包括前两个节点的名称:

df = spark.createDataFrame(
 [(1, "John"),(2, "Thomas")],
 ["id", "name"]
)df.write.format("org.neo4j.spark.DataSource")\
 .option("url”, "bolt://***XXX.XXX.XXX.XXX***:7687")\
 .option("authentication.type", "basic")\
 .option("authentication.basic.username", "***neo4j***")\
 .option("authentication.basic.password", "***password***")\
 .option("labels", ":Person")\
 .option("node.keys", "id")\
 .mode("Overwrite")\
 .save()

请注意,我们使用了“Overwrite”模式,并将 node.keys 选项设置为 DataFrame 的 id 列,以便将新值附加到现有节点。事实上,再次运行 read 查询将返回我们之前的结果,该结果用前两个节点的 name 属性进行了更新:

写入数据库后读取查询的结果(作者截图)

就是这样!🎉您已经成功地建立了一对完全基于云的互联 Apache Spark 和 Neo4j,使您能够充分利用“传统”大数据和图形数据库的潜力。

要获得更多的查询示例和语法概述,请深入阅读官方的 Neo4j 连接器 for Apache Spark 文档或查看快速指南阅读编写来自/到 Neo4j 的。其他示例可以在 Zeppelin 笔记本示例库中找到。连接器还支持 Cypher 查询,允许您重用 Neo4j 桌面/ Web 应用程序中的现有查询。密码查询示例可在官方密码页面上找到。

利用网络科学探索 Instagram 上的标签文化

原文:https://towardsdatascience.com/using-network-science-to-explore-hashtag-culture-on-instagram-1f7917078e0?source=collection_archive---------12-----------------------

如何从 Instagram 中抓取标签,并使用 NetworkX 建立标签之间的关系模型

来源:https://www . digital ambience . com/insta gram-light-forest-by-hey-hush/

摘要

该项目提出,网络科学和图论可以用来有效地分析 Instagram 上的标签文化。Instagram 标签的关系特征可以通过这些技术实现,这允许更深入地理解跨标签的对话和主题。

方法

为了演示这个想法,我写了一个实用的技术演练,演示如何使用 Python 和 NetworkX 来实现这一点。

大约有 37000 条 Instagram 帖子至少提到了一次#happiness,这些帖子都是从 Instagram 上删除的。语料库中所有唯一的标签(除了# happy)代表图中的节点如果标签在同一篇文章中被提到,它们之间就会形成边缘

这产生了一个无方向的“幸福”图,然后使用社区检测和各种中心性测量进行分析。

调查的结果

社区检测确定了三个集群,解释为:

  • 快乐的方面似乎是关于人们所做的事情和经历(例如:’#摄影#夏天,**#旅行#家庭)
  • 幸福的方方面面似乎大体上是关于人们如何思考和感受的(例如:生活、动机、T21、灵感、名言)
  • 第三个非常独特的集群完全是关于#婚礼、#庆典#派对

结论和应用

这种方法表明,图论可以成为探索社交媒体元数据(如标签)的一种非常直观和有用的方法。它可以应用于两个直接领域:

1.我们可能希望绘制主题实体的图形,例如,发布可持续发展、英国退出欧盟、冠状病毒的人

2.我们可以使用图表来模拟和理解围绕特定品牌、事件和地点的对话

(我们需要记住 Instagram 上内容的性质,因为这将指导什么是合适的应用)

可以在我的 GitHub 上找到复制该任务的所有功能代码:

[## kitsamho/insta gram _ Scraper _ Graph

这里有两个 Python 文件,每个文件都包含一个自定义的 Instagram 类。您需要有一个 Instagram 帐户…

github.com](https://github.com/kitsamho/Instagram_Scraper_Graph)

内容

乔安娜·科辛斯卡在 Unsplash 上的照片

1)背景和背景

  • Instagram 是巨大的
  • 标签文化:让数据科学家的生活更轻松!
  • 一张图片胜过千言万语…还是一堆标签?
  • Instagram 上的‘快乐’对人意味着什么?

2)网络科学和图论

  • 什么是图?
  • 我们可以在分析中使用图论的哪些方面?
  • 关于图表的进一步阅读

3)在 Python 中构建功能

  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉独特的 Instagram URL
  • 使用多线程处理从 html 解析标签和其他内容
  • 特征生成和探索性分析
  • 数据选择
  • 图形构建
  • 可视化/分析和解释

1)背景和背景

照片由 NoisepornUnsplash 上拍摄

Instagram 是巨大的

Instagram 成立于 2006 年,是一个创意表达的小众场所。早期用户蜂拥而至,热衷于分享他们的城市涂鸦、豆奶拿铁和拉面的照片。iPhone 1 + Instagram 是酷的标志——数码潮人的黄金时代。

唉,这并没有持续多久。Instagram 的中产阶级化是不可避免的,当脸书在 2012 年收购 Instagram 时,他们迅速通过广告将其货币化,并将 Instagram 变成了一棵巨大的摇钱树。

2020 年,Instagram 不再酷,它是一个商业巨兽。

来源:https://www . statista . com/statistics/253577/number-of-monthly-active-insta gram-users/

据估计 有 10 亿活跃月用户5 亿 每日故事,Instagram 的崛起和影响力令人印象深刻。

随后,Instagram 在许多方面已经成为主流消费文化的可靠晴雨表,这意味着它本身是一个潜在的有用工具,可以用来了解人们的情况。

虽然有理由假设 Instagram 的图片和视频将是主要的“首选”数据来源,但我发现分析 Instagram 的标签文化是一种更有效的方式来了解 Instagram 上出现的主题和话题。再加上从网络科学中借用的分析技术,意味着主题和话题之间的关系可以更容易理解。

标签文化:让数据科学家的生活更轻松!

照片由布丽娜·布鲁姆Unsplash 上拍摄

数据科学家工作的一个更重要的部分是能够投入时间来确保我们的数据符合目的,这包括彻底清理我们的数据。这可能会占用大量时间,尤其是社交媒体数据往往非常非结构化并且经常夹杂着“噪音”。

如果没有足够的预处理,我们对社交媒体数据使用的自然语言处理技术可能会导致难以解释和洞察的输出。

我们可以使用各种方法来清理和组织我们在社交媒体上发现的原始语言,但当用户为我们做这件事时,它变得更加容易。Instagram 上的标签文化——本质上是有组织的、正常的和不那么喧闹的——是一种文化现象,为我们做了所有这些艰苦的工作。

一张图片胜过千言万语。还是一堆标签?

乔治·帕甘三世在 Unsplash 上的照片

由 octothorpe 符号(#)引入的标签是一种元数据标签,用于 Twitter 和其他微博服务等社交网络。它允许用户应用用户生成的动态标签,帮助其他用户轻松找到具有特定主题或内容的邮件"

人们经常用一系列标签来结束 Instagram 帖子。标签可以提供那个人当时在想什么、感觉什么、观察什么的简明摘要。标签提供了图片本身无法提供的上下文和含义。

让我们找一个例子来说明这一点。

Instagram 上的‘快乐’对人意味着什么?

蒂姆·莫斯霍尔德在 Unsplash 上的照片

社交媒体越来越有可能被抨击为让人痛苦的东西。可悲的是,这是事实。

让我们逆势而为,翻转这个真相,在 Instagram 上探索的快乐

以下 Instagram 帖子在帖子正文中包含了#幸福。从这篇文章中,我们可以单从图片中解读出什么?

https://www.instagram.com/thatdesivegangal/—包含#幸福的公众号

不多。也许和音乐有关?

除了显而易见的以外,从这张照片上几乎看不出什么。当他们发布这个的时候,他们还在想什么/感觉什么?

现在,当我们查看这个人在他们的帖子旁边制作的所有标签时,我们对这个人以及他们在那个时刻与#快乐的联系有了更多的了解。

https://www.instagram.com/thatdesivegangal/—包含#幸福的公众号

对这个人来说,快乐与积极、动力、改变和减肥联系在一起。

虽然 Instagram 图片能告诉我们一些东西,但人们使用的标签能告诉我们更多的东西。

扩大标签分析

现在,这只是一个 Instagram 帖子,包含了#幸福。当我们看到周围其他人的帖子的标签提到#幸福时会发生什么?

如果我们扩大分析范围,查看成千上万条包含快乐的帖子,会发生什么?

Instagram 上所有包含# happy 的帖子

很快我们就会有大量的标签需要导航。什么样的可扩展方法可以让我们理解所有这些数据?

这就是我们可以使用网络科学和图论来帮助我们的地方。

2)网络科学和图论

照片由安娜斯塔西娅·杜尔吉尔Unsplash 上拍摄

网络科学是一个蓬勃发展且日益重要的跨学科领域,其重点是将复杂的社会、生物和技术系统表示、分析和建模为网络或图形

来源:http://tuvalu.santafe.edu/~aaronc/courses/5352/

我喜欢把网络科学和图论看作是让我们理解事物如何联系的方法。我们可以借用网络科学和图论的一些基本原理来理解insta gram 上的 hashtags 是如何连接的。

与网络科学和图论相关的主题非常密集,需要很长时间才能涵盖。这将分散项目的实际重点。然而,有一些简单但基本的概念需要解释。

什么是图?

一个 (G) 是一个网络的抽象表示。图是由顶点 (V)边(E) 其中【𝐺=(𝑉,𝐸】

来源:https://www.computerhope.com/jargon/n/node.htm

  • 图中的节点 (V) 代表我们数据中存在的唯一数据点。在 hashtag 分析的情况下,hash tag 表示我们网络中的节点是有意义的。因此,如果我们的 #happiness 数据集只有两个标签,即 #A#B ,我们将有两个节点

代表两个 hashtags 的两个节点

  • (E) 是表示节点间某种关系的连接。在分析标签的情况下,将这种关系表示为某种事件可能是有意义的,即,如果标签 #A 在与# B 相同的帖子中被提及,我们将假设在 #A#B 之间存在关系,因此我们将在这两个节点之间创建一条边

如果两个标签在同一篇文章中被同时提及,那么它们之间就会形成一条边

当我们将其他帖子的标签添加到图表中,并模拟它们与之前所有帖子的关系时,我们开始看到结构形式。

随着更多的标签被添加到图表中,我们可以看到哪里有联系,我们可以开始看到结构形式

我们可以在分析中使用图论的哪些方面?

  • 社区检测。我们可以使用算法来识别和标记与#快乐相关的主题群在这个例子中,我们有 14 个标签,它们都以不同的方式连接在一起,但是形成了不同的簇。

识别图中的标签社区

  • 度中心性/中介中心性。我们可以计算出网络中哪些标签在连接整个网络时特别重要。就像希思罗机场连接了很多世界一样,什么标签连接了#幸福“风景”?

确定哪些标签在图表中起着核心作用

  • 可视化。如果我们用散点图来绘制网络,这是一种非常引人注目的方式来可视化大量关于幸福的信息

关于图表的进一步阅读

迈尔·杨奇煜 写了一整个系列,很好地涵盖了图论的内容。如果你觉得这个话题有趣,我强烈建议你在某个时候读一读这篇文章。

[## 图表介绍(第一部分)

Python 中的主要概念、属性和应用

towardsdatascience.com](/introduction-to-graphs-part-1-2de6cda8c5a5)

现在,让我们看看构建网络需要采取的实际步骤。

3)在 Python 中构建功能

凯文·Ku 在 Unsplash 上拍摄的照片

我构建了两个 Python 类,处理从捕捉数据到构建、可视化和分析幸福图的所有过程。每个类别中的关键管道概述如下。

**class InstagramScraper()**
  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉唯一的 Instagram URL 的
  • 使用多线程处理从 post html 解析标签和其他内容
**class InstagramGraph()**
  • 特征生成&探索性分析
  • 数据选择
  • 图形构建
  • 可视化

您可以在这里访问这些类的完整代码及其相关的自述文档:

https://github.com/kitsamho/Instagram_Scraper_Graph

InstagramScraper 类()

来源:https://hacker noon . com/how-to-scrape-a-website-not-get-black-list-271 a605 a0d 94

**class InstagramScraper()**
  • 使用 Selenium WebDriver 在 Instagram 上搜索标签
  • 捕捉独特的 Instagram 网址
  • 使用多线程处理从 html 解析标签和其他内容

这一阶段结合使用自动网页浏览和 html 解析从 Instagram 帖子中抓取内容。我构建了一个定制的 Python 类 InstagramScraper t ,它包含了一系列抓取和数据提取方法,可以返回抓取的 Instagram 数据的 Pandas DataFrame。

我将用几个示例代码块向您展示下面的主要过程,以突出所涉及的主要步骤。

使用 Selenium WebDriver 在 Instagram 上搜索标签

Instagram 上有一个功能,用户可以搜索特定的标签,Instagram 会返回包含该标签的所有帖子。这是我们获取所需数据的第一步;使用 Selenium webdriver 让 Instagram 为我们完成这项工作。

在 Instagram 上搜索包含# happy 标签的 Instagram 帖子

捕捉独特的 Instagram URL

使用 Selenium WebDriver 捕获独特的 Instagram URLs

Instagram 上的每个帖子都有自己的唯一网址。在我们搜索了所有包含特定标签的文章之后,第一步是获取所有包含该标签的文章的唯一 URL。

我发现最好的方法是结合使用 Selenium WebDriver 和动态解析 html,当我们滚动浏览所有内容时,提取每个 posts href 属性到一个数据结构。

由于 Instagram 网站的这一部分是高度动态的,需要一致的滚动来加载新图像,使用某种基于浏览器的自动化,如 Selenium WebDriver,是捕捉数据的唯一可行的方法。这意味着获取所有链接可能需要一点时间。

捕捉唯一的 Instagram URL 示例代码

使用 Selenium web driver & Beautiful Soup 捕捉独特的 Instagram URL。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

解析标签和其他内容

一旦你有了所有链接的列表,你就可以结合使用 urlopen 和 html 解析(我用的是 Beautiful Soup )来获取每个 Instagram 帖子的数据。

一旦你有了每个帖子的 html 并解析了它的内容,你会发现 Instagram 帖子的数据有一个类似字典/ JSON 风格格式的一致结构

解析标签和其他内容—示例代码

每个 Instagram 帖子都包含 dictionary / JSON 类型结构的数据。我们使用单独的函数迭代每个帖子,提取我们想要的数据。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

InstagramScraper 输出

InstagramScraper 类的主要输出是一个 Pandas 数据帧,其中包含大量 post 变量。

来自 InstagramScraper 类的熊猫数据帧输出

这些变量中的大部分都是自解释的,并且提供了各种各样的选项来进行 hashtags 之外的探索性分析。

现在我们有了收集的数据和一个漂亮整洁的熊猫数据框架,我们可以开始考虑处理我们的数据,为建立我们的网络和可视化我们的图表做准备,这是事情变得真正有趣的地方。

InstagramGraph()类

来源:https://TechCrunch . com/WP-content/uploads/2017/12/insta gram-hash tags . png?w=730 & crop=1

我构建了第二个 python 类,名为 InstagramGraph ,它也包含一系列方法,允许您分析 Instagram 数据集,并最终使用 Plotly 可视化将 hashtag 数据建模为 NetworkX graph 对象的实例。

**class InstagramGraph()**

该类别中的关键流程包括:

  • 特征生成&探索性分析
  • 数据选择
  • 图形构建
  • 可视化/分析&解读

特征创建&探索性分析

瓦迪姆·谢尔巴科夫Unsplash 上拍摄的照片

下面的代码块概述了来自 InstagramGraph 类的管道,该类包含一套在我们的数据中创建新特征的方法。

一系列方法在我们收集的 Instagram 数据中生成新特征。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

这里有几种方法,一些是语言学的(例如确定帖子的语言),尽管这种方法中的大多数过程产生额外的描述性指标,例如按用户的平均帖子计数、按帖子的总标签计数和按用户的平均标签使用。

虽然这些新特性/指标中的一些与图形构建没有直接关系,但总是值得生成更多的数据点,因为它提供了对主题进行一些探索性分析的机会,这可能会提供更多关于我们的图形模型所揭示的内容的上下文。它也可能激发一些你最初没有考虑过的其他分析想法。

让我们来探索其中的一些。

语言

Python 中的 langdetect 库允许我们通过 API 访问谷歌翻译库,并赋予我们在文本中识别语言的能力。

注意——由于谷歌云 API 请求的限制,在处理大量数据时,运行速度会非常慢。出于举例的目的,这很好;如果你想生产这个,你可能会想一个更快的选择,并考虑支付。

包含# happy 的帖子的语言分布

正如我们所看到的,Instagram 上大多数关于 #happiness 的帖子都是英文的——超过我们数据集的 80%。

用户发布频率

对一些分布的观察表明,关于用户发布频率的数据是非常正偏的;有少数异常帐户发布非常频繁(多达 140 次),而大多数发帖者只发布一次。

在我们的数据中,用户发帖的频率——正常化为 1

提取标签

为了提取我们的标签,我创建了两个方法来完成这项工作。一种方法接受一个字符串输入并返回一个 hashtags 列表。如果人们没有在他们的标签串之间加入空格,代码会考虑到这一点,并将它们“解包”到单独的标签中。

从字符串中提取标签。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

分析

对帖子的标签频率的分析表明,虽然帖子的频率倾向于遵循均匀分布,但是有一些异常的帖子包含异常高数量的标签。进一步的研究表明,这部分似乎是由在数据集上多次发帖的人驱动的(高频发帖者)。所以从广义上来说,那些经常发帖的人也倾向于在他们的帖子中使用更多的标签。

虽然标签使用的分布基本一致,但高频发帖者在帖子中使用更多的标签

词条匹配标签

然后,第二种方法获取这个 hashtags 列表,并在适当的时候查看输入的 lemmatise 。对于那些不熟悉词条满足的人来说,这是我们将一个单词简化为其词根形式的过程。例如,如果我们有标签#最快乐的#更快乐的——这些都是动词#快乐的变形。因此,lemmatisation 将这些变化还原为 #happy。我已经使用了一个空间模型的实例来执行引理满足。

lemmating hash tags—示例代码

使用空间匹配标签。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

数据选择

维多利亚诺·伊斯基耶多Unsplash 上拍摄的照片

好了,下一步是选择我们想要建模的数据。代码概述了来自 InstagramGraph 类的管道,该类包含一套允许我们选择我们想要的数据的方法。

在我们构建图表之前,选择数据的子集。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

选择语言

虽然 Instagram 是一个国际平台,但为了简单起见,我们可能会考虑过滤掉非英语帖子。正如我们之前看到的,超过 80%的关于#幸福的数据是英文的,所以关注这些数据的子集是有意义的。

删除已验证的用户

经过验证的用户往往是品牌、名人或网上商店,即不代表 Instagram 普通用户的“人”。根据您的分析目标,您可能需要考虑从数据中删除已验证的用户。

移除高频海报

对高频海报的粗略检查显示,这些有时是未经验证的账户,在未经验证的状态下使用 Instagram 出售东西。我们觉得这代表了我们想了解的观众吗?如果是,我们可以把它们留在里面,如果不是,我们可以在构建图表之前把它们过滤掉。

使用 lemmatised 标签?

我认为在可能的情况下选择 lemmatise 抓取的标签是有意义的,因为它减少了构建图表所需的数据量——我们不一定需要所有额外的数据。

图形构建

兰迪·法特在 Unsplash 上的照片

既然我们已经选择了要建模的数据,我们就可以构建图表了。我们为此使用的管道如下:

InstagramGraph 类中的方法,包含允许创建包含 hashtag 数据集的边和节点数据的 Graph 对象的进程管道。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

这条管道中有三个主要流程:

编译一个列表列表

这只是将 hashtags 从我们的数据帧提取到一个列表列表中——每个子列表代表我们的 #Happiness 数据集中的一篇文章。通过将输入作为列表的简单列表,我们可以很容易地将这种图形构建方法重新用于将来的其他数据源,在那里我们可以以类似的结构/列表格式的列表来争论原始数据

编译列表列表—示例代码

编辑一个由 n 个列表组成的列表,其中每个列表包含一个人的标签,n 是 Instagram 帖子的总数。默认的停用词被删除,并可选择添加更多停用词。资料来源:https://github.com/kitsamho/Instagram_Scraper_Graph

编译列表列表—示例输出

从一系列标签中提取一个列表列表

识别边和节点

下一步是获取我们的列表列表,并使用它来生成存在于整个数据集的节点和边。请记住,节点是数据集中唯一的标签,如果任意两个标签在同一篇文章中同时出现,就会创建一条边。

识别边和节点—示例代码

计算 hashtag 数据中存在的所有节点和边——保留几个数据帧作为属性。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

识别边和节点-示例输出

该方法将两个生成的数据帧保存为属性—一个包含所有节点的数据帧和一个包含所有边的数据帧。

节点数据框架

节点数据帧——每一行代表#Happiness 数据集中存在的一个唯一的标签

边缘数据帧

边缘数据框-每行代表#Happiness 数据集中存在的唯一边缘。边缘频率是指该配对在#Happiness 数据集中出现的次数

构建图表

下一步是将这些节点和边添加到 NetworkX 图形对象(G)的实例中。

构建图表—示例代码

获取边和节点数据并将其添加到 NetworkX graph 对象的实例中。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

构建图表—示例输出

一旦我们创建了 NetworkX 图形对象,我们就可以使用各种计算出的图形属性来进一步增强我们的节点数据。

  • 中间中心性
  • 邻接频率
  • 聚类系数
  • 社区

用图形属性增强的节点数据框架

使用来自 NetworkX 的新指标增强了节点数据框架

可视化

照片由 russn_fckrUnsplash 上拍摄

现在我们有了一个 NetworkX graph 对象的实例,它是用从我们的 #Happiness 数据集中提取的节点和边数据构建的。

曲线图

我们现在可以使用 Plotly 将 hashtag 图可视化为一个相连的散点图。

在 Plotly 中将 hashtag 图绘制成散点图,带有各种参数,允许定制显示的数据。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

我提出了几个论点,允许对视觉化进行一定程度的定制。

**def plotGraph**(node_size=’adjacency_frequency’,colorscale=’Viridis’)

我认为使用节点大小来传达一些其他变量是有意义的——默认为“邻接频率”,即一个节点与多少个其他节点有边连接。在这种情况下,较小的节点将代表具有较少边的 hashtag,相反,较大的节点将具有较多边。我们可以通过应用与节点大小相关的色标来进一步强调这一点。

**def plotGraph**(layout=nx.kamada_kawai_layout)

NetworkX 有一些不同的图形布局。kamada kawai 布局以尽可能少的重叠边的方式排列节点。因此,这有一种趋势,节点的排列方式传达集群…但最重要的是,往往是最容易解释的布局。还有其他选择,如圆形布局、随机布局,但根据我的经验,这些更难理解。

**def plotGraph**(light_theme=True)

有两种颜色主题,浅色和深色。

调用绘图函数…

幸福图的网络图可视化——节点的大小与它们到其他节点的边的频率正相关。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

我们可以很容易地看到在#幸福图中有几个连接良好的标签(有更多节点连接的标签)。微笑生活家庭动机、聚会……这些都是网络上经常被提及的标签。

正如我们所看到的,force directed 布局倾向于将连接良好的标签组推到一起。如果我们再次绘制图表,但使用社区标签对节点进行着色,我们可以更好地可视化社区是如何脱离的。

**def plotGraph(**community_plot=True)

幸福图的网络图可视化——节点的颜色代表使用 NetworkX 计算的社区,节点大小代表邻接频率。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

旭日图

#幸福网络中似乎有三个容易识别的社区。我们可以使用 sunburst 可视化来探索哪些特定的标签有助于每个社区。单击滚轮与集群互动。

在#幸福图中检测到的社区的交互式旭日可视化。来源:https://github.com/kitsamho/Instagram_Scraper_Graph

社区检测确定了三个群集,解释为:

  • 快乐的方面似乎是关于人们做什么和经历什么(例如,#摄影#夏天,**#旅行#家庭)(片段 0)
  • 快乐的各个方面似乎大体上是关于人们如何思考和感受的(例如:生活、动机、灵感、引语)
  • 第三个非常独特的集群完全是关于#婚礼、#庆典#派对(第 3 部分)

结束语

Artem Beliaikin 在 Unsplash 上拍摄的照片

我希望你喜欢这个关于如何从 Instagram 中提取标签数据,并使用从网络科学中借鉴的原理进行分析的实践练习。

我这个项目的主要目标是:

  1. 试图证明社交媒体数据中蕴藏着丰富的可访问性和潜在的深刻内容。
  2. 向你展示在你可能意想不到的地方几乎总是有有趣的模式

我选择了#幸福作为案例研究,因为希望这是我们都能涉及到的事情。然而,代码适用于任何标签。只要你能收集足够多的数据来建模,任何事情都有可观察到的关系。你可以考虑品牌、地点、事件甚至人。

你甚至不需要使用 Instagram 这些代码可以很容易地被重新利用,以使用 Instagram 之外的各种元数据。Twitter、LinkedIn 甚至抖音都有可能。

正如我之前提到的,代码在我的 GitHub 上可用,所以请随意用、进化改进。我很想看看人们用这个做什么。

下次见!🚀

山姆干杯

参考和链接

使用神经网络推荐网飞的节目

原文:https://towardsdatascience.com/using-neural-nets-to-recommend-shows-on-netflix-d4fbecffe0b0?source=collection_archive---------55-----------------------

机器学习应用

训练模型来预测上瘾的概率

没有什么比在网飞开始做一些事情,然后意识到 20 分钟内你不感兴趣更令人痛苦的了。在这个项目中,我试图通过计算在给定特定表演特征的情况下,我将完成一个表演的概率,将事情掌握在自己手中。

隔离 5 个月后,我非常确定我已经看完了我想在网飞看的所有东西。从积极的角度来看,能够在凌晨 2 点睡觉,9 点起床去工作对我的工作效率并不是很好。曾几何时,我真的很喜欢每个工作日步行 30 分钟到加州火车站,然后再回来。对我慢慢陷入彻底的无精打采的唯一安慰是,我一直在努力阅读网飞的原著。没什么可看的了,再加上决定让自己的时间变得更有效率,这两个因素促使我尝试使用神经网络来预测我接下来应该看什么。

来自 Unsplash自由股票的照片

步骤 1:获取数据

这个项目的第一步是获得我的观察活动。我喜欢网飞使这很容易访问,甚至下载为. csv 文件。

这是我在过去 3 年里看过的关于网飞的完整列表(只看超过 1 集的节目,我看过 100-200 个关于网飞的节目)。这让我有可能开发一个更加个性化的推荐系统。如果你是网飞的新用户,该平台通过使用与你相似的人的观看模式,避开了缺乏你观看活动数据的问题。

然后,我从 IMDb.com 搜集了一些数据,这些数据包含了很多电视剧的特征,比如类型、演员、创作者、剧集长度和标签。这里有一些快速汇总表,显示了我在网飞观看的节目中最受欢迎的流派、演员和标签。

观看的热门类型(由 IMDb.com 定义的类型),按作者分类的图片

热门观看标签(IMDb.com 定义的标签),作者图片

我决定不使用 actors,只是因为数据中可能有太多的变化来提供任何有用的信息——这最终会创建太多没有任何真正预测能力的特征(也称为过度拟合)。

顶级演员观看(演员在 IMDb.com 上搜索),图片由作者提供

有了一大堆用来预测我想看什么节目的特征(或自变量),接下来我需要创建一个因变量。因变量只是一个 1 或 0 的指示器,它描述了在我开始的情况下,我是否完成了一个节目。

步骤 2:预处理和特征选择

在这一步中,我们试图将我们的数据转换成一种可以用来运行神经网络的格式。我的数据集中的每个观察都是电视节目和季节的组合。比如《十三个为什么》第一季。我添加到数据集的特征包括作为分类变量的流派、标签和季节号,以及作为数值变量的剧集长度。

为了创建有用的东西,我必须将数据集转换成一个包含各种虚拟变量的大数据集。例如,对于“戏剧”类型,像“13 个原因”,“非正统”这样的节目以及该类型的其他 18 个节目的值为 1,而所有其他节目的值为 0。

因为我有大量的标签,所以我最终得到了一个包含 2000 多个特征的大数据集,但只有 200 多个观察值!这导致我不得不削减功能,以防止严重的过度拟合。为了做到这一点,我创建了一个相关性矩阵,查看所有特征之间的相关性。这使我能够删除那些不会提供任何新信息的高度相关的特征。例如,类别“犯罪”将与标签“谋杀”高度相关,因此“谋杀”将被移除。下面是实现这一点的一些示例代码:

X_corr = X.corr() columns = np.full((X_corr.shape[0],), True, dtype=bool) #Keep only columns with correlation less than 0.9 for i in range(X_corr.shape[0]): 
    for j in range(i+1, X_corr.shape[0]): 
        if X_corr.iloc[i,j] >= 0.9: 
            if columns[j]: columns[j] = False selected_X = X[X.columns[columns]]

使用上述相关矩阵作为特征选择方法的一个限制是,它不会拾取相关单词的相关性。例如,像“高中”和“高中生”这样的标签可能不会被上面的算法选中。如果有的话,这两者之间可能存在非常小的相关性(因此,在我项目的下一个实现中,我希望尝试一些单词关联算法来改进特征选择过程)。

第三步:型号选择

我使用 Keras API 运行 TensorFlow。有了我可以指定的一大堆参数,我决定尝试几个不同的参数,看看在将训练好的模型应用于测试集时,什么会产生最高的准确性。我决定关注以下杠杆:节点数、正则化系数和压差值。总之,这些杠杆可以提供一个最不可能欠拟合或过拟合的模型。

下面是一个代码示例,它创建了一个简单的参数矩阵,我希望执行网格搜索来确定最佳模型:

#Generate list of nodes to search over
nodes = np.arange(12, 61, 24)#Generate list of dropout values to search over
dp = np.arange(0.2, 0.81, 0.3)#Generate list of regularization coefficients to search over
reg = np.array([1e-4, 1e-5, 1e-6])params = list(product(nodes, dp, reg))

关于网格搜索的完整实现,请点击这里的链接!

以下是我在这个网格搜索过程中获得的一些经验:

  • 在开始前尝试几个型号;观察损失函数:当我第一次开始运行几个测试模型时,我观察到我的测试集的损失函数在增加,这对于一个旨在最小化损失函数的模型来说是非常奇怪的。这个奇怪的现象是通过指定一个较小的步长来解决的,这样梯度下降就不会“跨过”最小值。下面是一些如何实现较小步长的示例代码:
from keras.callbacks import LearningRateScheduler#define when you want to reduce your step size by setting n
n = 50def lr_scheduler(epoch, lr):
    if (epoch > n):
        #define your step size here
        lr = 1e-5 
    return lr...model.fit(X_train, Y_train, epochs = 200, batch_size = 32,\                    callbacks = [LearningRateScheduler(lr_scheduler, verbose=1)])
  • 使用“提前停止”功能:这似乎是常识,但 Keras 中默认的“拟合”命令不会提前停止你的神经网络,即使下一次迭代没有在最小化你的损失函数方面提供显著的改善。此外,它不使用导致最低损失的权重,除非您使用keras.callbacks.EarlyStopping功能。下面是一个实现示例:
from keras import callbacksearlystopping = callbacks.EarlyStopping(monitor =”val_loss”,\ 
mode =”min”, patience = 30, restore_best_weights = True)

尽管你可能受到诱惑,但你不希望指定一个“val_accuracy”作为停止标准,因为这可能会导致过度拟合。

  • k 倍交叉验证的重要性:由于我的训练和测试集非常小,仅在一种类型的训练和测试集上尝试许多模型很可能会导致过度拟合。因此,对于每个模型,我在 10 个不同的训练测试集组合上进行尝试,并取平均准确度来选择一个模型。有许多包可以实现这一点,但是我实现了一个简单的 for 循环,并为训练测试分割的每次迭代使用了不同的种子。
for i in range (0, 10):     X_train, X_test, Y_train, Y_test = train_test_split(X, Y,\ test_size = 0.2, random_state = 1234 + i, stratify = Y)...#Fit your model here
  • 添加层的危险:一开始,我根据自己对神经网络功能的直觉,给我的模型添加了许多层。由于神经网络基本上试图创建新的特征,在给定现有特征的情况下最好地预测结果,所以我假设有更多的层会更好。然而,这导致了严重的过度拟合。因为我已经面临着适应太多特性的危险,所以创建一个更复杂的模型会加剧这个问题。
  • 并行处理的重要性:当我第一次开始网格搜索来寻找参数的最佳组合时,我计算了一次运行的时间,然后意识到,对于我想要尝试的大约 729 个不同的模型,依次运行每个模型需要大约 14 个小时。使用multiprocessing包,我可以同时运行大约 12 个模型,将我的总处理时间减少到 2 个多小时。
#Always time your code first before running all your different models! Assume here that train_model is a function I created to train my model.start = time.time()
train_model(params)
end = time.time()
print(f"Runtime of the program is {end - start}")#Here is some simple code to perform parallel-processing for a grid searchimport multiprocessing as mp
pool = mp.Pool(processes = 12)
test = pool.map(train_model, params)

第四步:预测在网飞看什么

在想出一个模型来训练我的数据后,我导出了这组参数和权重,开始生成一个概率,即我将完成网飞上的每一场演出。

为了得到网飞所有的节目,我从 Reelgood.com 搜集了一些数据。事实证明,大约有 1700 个来自网飞所有版本(本地和外国)的电视节目。然后,我再次搜集了 IMDb.com 的数据,寻找所有这些节目的特征。

Reelgood.com的网飞演出的完整列表(作者截图)

最后,我将我的模型和权重应用到我的网飞头衔的完整列表中,我最终得到了我将获得每个网飞头衔的预测概率。

我的首选是一些我从未听说过,但听起来确实有点有趣的节目。以下是我的模型极力推荐的一些节目(括号中的概率指的是在我开始的情况下,我完成一个节目的概率):

  • 她一定要得到它(97.3%的可能性),根据斯派克·李的热门电影改编,讲述了一个年轻的黑人女性如何驾驭自己的爱情生活。听起来像是我喜欢的“亲爱的白人”!
  • 我通常不喜欢韩剧,但“之声”(96.1%的概率)——这是一个关于侦探解决谋杀案件的故事——听起来很有趣!
  • 《丽塔》(94.5%的可能性)——这是一个关于直言不讳、叛逆的老师的故事,她需要掌控自己的个人生活?这听起来有点像威廉姆·H·梅西在《无耻之徒》中扮演的角色,我非常崇拜他。此外,它来自丹麦,这使得它更加迷人!
  • 多莉·帕顿的心弦”(93.9%概率)——关于家庭的节目设置为多莉·帕顿音乐?听起来像是“这是我们”遇到了“小美国”,我就是为此而来的。

最后,我实际上看到了一些我最喜欢的节目与推荐节目的相似之处,这是一个很好的健全性检查,表明该模型是有效的。

总之,如果再来一次,我会做下面这些事情:

  • 模型中查看的构建日期:人是会变的,所以模型需要跟上!我会喜欢 3 年前看的东西吗?从这个意义上来说,我最近看的电视节目的重量可能会增加,或者如果我厌倦了某个类型,重量可能会减少?不过,总的来说,我确实有轻微的强迫症,比如,只有去年感恩节去伦敦的时候,我才会看英剧。因此,在这种情况下,加权显示我刚刚完成的可能是有意义的。
  • 获取更多数据:没有什么比更多数据更能帮助模型了。尤其是因为我总是处于过度适应我所拥有的功能的危险之中。也是看更多网飞的借口(尽管我刚刚订阅了 Apple TV+,相信我,当我告诉你你需要它的时候)。
  • 收集更多有见地的特征:我是否忽略了其他比类型和标签更具描述性的特征?例如,烂番茄分数?(我认为这通常能很好地预测我的观看活动,但也有一些明显的例外,比如《13 个为什么》第三季——我对此没有借口,也不知道为什么我会如此上瘾)。
  • 执行文本关联:如果我有一些 NLP 经验,我会使用比相关矩阵更复杂的算法来选择我的特征。但是,嘿,这是另一个项目的想法,不是吗?
  • 选择基于高召回率的模型:我稍微想过这个问题,但是考虑到任务的性质,选择召回率更高的模型不是更好吗?虽然这可能会在推荐的节目中产生很多噪音,但有更多的假阳性几乎没有什么代价。

然而,这些都是以后的想法。今晚,请原谅我回去完成“13 个为什么”的原因,这样我才能继续我的生活。

在我的 Github 上可以看到这个项目的完整代码:

[## Bryan chaws/网飞 _ml_project

机器学习项目-使用神经网络来预测显示,我会喜欢基于过去的观看活动。01 …

github.com](https://github.com/bryanchiaws/netflix_ml_project)

使用神经网络改善废物管理,一次一个传播。

原文:https://towardsdatascience.com/using-neural-networks-to-improve-waste-management-one-propagation-at-a-time-b2b7414d329d?source=collection_archive---------38-----------------------

图一。马累 Thilafushi 的废物处理场。哈特斯托克-穆罕默德阿卜杜勒拉希姆

问题范围

这绝对不好看,也不是在餐桌上提出来的东西,尽管每顿饭结束后,这个过程又开始了,我说的是浪费!

废物管理是收集和处理废物的过程,是每个社会不可分割的一部分,通常反映了一个社会的整体发展水平。废物管理的低端是一个非常遥远的过程,通常只涉及一个露天垃圾场。另一方面,这些过程涉及多阶段的努力,有针对性的资助,以便收集、分类和重新利用废物以备将来使用,或完全分解废物以产生能源。世界上存在的大多数废物管理系统都介于这两个极端之间。然而,不幸的是,上图中的 Thilafushi 城市垃圾填埋场更常见。

目前,世界每年产生 20 亿吨城市固体废物,预计到 2050 年将增加到 34 亿吨。高收入国家占世界人口的 16%,却产生了 34%的垃圾。因此,预计随着人口更多的发展中国家继续进步,产生的废物量也将增加。

这是个大问题。

幸运的是,我们有机会从错误中学习并改变!

数据如何产生影响

数据帮助我们量化和结构化问题。数据科学家使用他们所掌握的数据来提供见解,从而为解决此类问题的决策提供信息。一个典型的过程可以总结为 3 个一般步骤:

  1. 确定需求
  2. 测量问题
  3. 构建定制解决方案

就废物管理而言,这一过程极其复杂,需要在地方政府和社区的支持下采取多学科方法才能取得成功。在这篇文章的剩余部分,我将特别关注这个过程的一个方面,通过图像分类进行垃圾分类。

由于不同类型的废物需要特定的解决方案,确保废物得到适当分类对于建立有效和高效的废物管理系统至关重要。请看下面(图 2 )的示例,了解典型的分类设施可能是什么样子,以及废物是如何进行初步分类的。那么大的问题应该是显而易见的,我们如何用可以结合技术的系统来取代现状,以提高这一过程的有效性和效率?

图 2。左图:这只是垃圾分类设施众多可能例子中的一个。右图:最常见的分类方法是手工分类,这非常耗时,而且成本极高。来源

例如,某些塑料可以回收并再次用作生产原料,然而,一种塑料所需的再加工通常与另一种塑料大不相同。这些差异的细节超出了本文的范围,但是如果你有兴趣了解更多,请查看这个网站

那么,什么是图像分类,它是如何工作的?下面我简单解释一下。

利用神经网络进行垃圾分类

神经网络(也称为 多层感知器【MLP】)是一种预测建模技术。像许多其他建模技术一样,神经网络涉及通过迭代过程最小化成本函数。然而,与其他一些模型不同,神经网络的体系结构非常健壮,能够相对容易地处理回归和分类问题。

图 3。一种基本的神经网络架构,也称为多层感知器。资料来源:走向数据

神经网络由多层节点或神经元组成,因此得名(见图 3 )。第一个是输入层,它接收预处理的数字数据。该层中的节点数量取决于输入数据本身。最后一层被称为输出层。这一层的节点数量完全取决于问题的性质。例如,回归(或二元分类)问题将涉及一个输出节点,而多类分类问题将涉及两个或更多。

输入和输出之间的层被称为隐藏层,这就是神奇的地方!可以有任意数量的这些层,并且每层中的每个节点都接收前一层的所有输入值。然后,它为每个值分配一个随机权重乘数,将它们的总和相加,然后添加一个随机偏差值以确保区分( y = sum(wixi) + b* )。然后,该值通过所谓的激活函数 ( ,其中有许多),然后返回一个值,由后续层中的每个节点处理。决定使用哪种激活函数取决于所处理的数据以及问题的类型。这些决定背后有一些复杂的数学原理,我不会在这里深入探讨,更多信息请查看 Sagar Sharma 的这篇伟大的 Medium 文章

数据通过这些层从左到右被处理的过程被称为正向传播。这些步骤随后被再次重复,重复次数由研究人员确定,称为批量。达到这个阈值后,网络反向工作,通过使用许多潜在的优化器方法之一调整权重和偏差,采取实际上最小化损失函数的步骤(又名学习,这是机器学习的学习方面!关于如何选择正确的优化器的更多细节,请查看 Sanket Doshi 的这篇伟大的文章。每次模型到达定型数据的末尾时,它都从开始处返回并继续。数据的每个完成的‘圈’被称为一个时期。

最后,当模型已经完成了所有这些步骤,并且运行了预定数量的时期时,那么可以说模型已经被'训练过,并且它准备好进行预测。哦,最后一点是关于我们如何比较模型的。当人们想要评估一个模型与另一个模型时,比方说比较某个任务的性能,用来表示模型大小和复杂性的一个重要术语是它拥有的参数的数量,因为计算单个层有时会很麻烦。考虑参数的一种方法是,想象如果我从一个输入节点开始,对每个节点遍历整个模型,会有多少条可能的路径。看看你能不能算出上图中有多少个参数。退一步说,这些数字可能会变得惊人!

干得好!你通过了神经网络的本质!我希望您现在已经理解了这种建模技术的基本过程。下面我将简要解释我用于图像处理的特定类型的神经网络,因为它有一些关键的差异,使它非常擅长具体解释图像数据。

卷积神经网络

如果你已经做到这一步,那么理解卷积神经网络( CNN )的细微差别应该相对简单。看看下图( Pic。看看你自己能不能理解 CNN 在做什么!

图 4。一个基本的卷积神经网络结构。来源:走向数据科学。

你很有希望马上注意到的是,CNN 和基本的 MLP 架构之间的主要区别是在过程的前端。我们在这里看到的是该过程的卷积部分,这是将图像作为数据(由每个像素的 RGB 值表示)并将所谓的滤镜 应用于每个卷积层的行为。这些过滤器然后重新格式化输入数据,只保留看似重要的信息。当使用 CNN 时,这些信息通常被称为每幅图像的边缘特征。每个输入数据经过卷积步骤后,将作为列向量传递到最终的 MLP 结构,并在输出层进行分类,类似于上一节中讨论的过程。这个过程是专门为处理图像数据而设计的,因此它是当今图像处理的前沿方法之一。如需进一步阅读 CNN,请查看 Sumit Saha 的这篇文章

张量流、数据和模型性能

一种用于设计和执行神经网络的非常流行的技术被称为 张量流 。Tensorflow 是一个开源软件库, Google Brain 的第二代深度神经网络学习系统于 2017 年发布。Keras也是一个开源软件库,旨在实现深度神经网络的快速实验,运行在 Tensorflow 之上。

我用来训练我的模型的数据是由 Kaggle *提供的垃圾分类数据集。*该数据集包含超过 22,000 张标记图像,代表两个类别,或者是有机或者是回收利用。大约有 12,000 个有机图像和 10,000 个回收图像,这意味着模型训练的类别平衡良好。检查以确保类之间存在健康的平衡是数据科学家为确保模型稳健而采取的许多重要预处理步骤之一。

图 5。我的最佳表现模型的培训与验证损失和准确性。

所以我们在这里,经过大量的时间和努力微调我的 CNN,我终于能够建立和训练一个我满意的模型。

以下是我的 CNN 规格:

参数:670 万

测试损耗:~0.283

测试准确率:~91%

这些值显示在左侧(图 5 ),顶部的图形代表随时间的损失,底部代表精确度。如果你还记得,我们之前简单地讨论过纪元,正如你所看到的,这就是时间在 x 轴上的表现。

幸运的是,模型的训练如预期的那样进行,我们可以看到随着时间的推移,损失减少了,准确性也提高了。机器学习中存在许多计算限制,此链接详细介绍了这一主题。不过现在,请相信我的话,我说 50 个时期是在我的个人笔记本电脑上用这种规模的数据集调优和训练模型的好时间。总的来说,我对我的模型的表现很满意,因为我能够进入 90 年代,并且测试损失相对较低。

基准

数据科学家采用的典型基准测试实践是将您自己定制的模型与预先训练的模型进行比较。在 CNN 的世界中,有许多预先训练好的模型,Keras 已经内置了这些模型,允许轻松导入。

我决定将我的 CNN 与目前市场上表现最好的三个模型进行比较,即: VGG16VGG19除了。这三款都脱胎于 ImageNet 挑战。** ImageNet 的数据库中有超过 1500 万张图像,这些模型已经在其中的很大一部分上进行了预训练(我的数据集是 22,000 张图像,请记住关于计算能力的那部分… ),因此非常健壮或“可概括”。这些模型是开源的,可以作为其他数据科学家在测试时调整/微调和比较他们自己的模型的基础。**

行业精度基准:

VGG16: ~86%,基础参数:1470 万

VGG19: ~86%,基数参数:2000 万

异常:~90%,基础参数:2080 万

这里要注意的第一件事是这些预训练模型中的每一个与我的相比有多少参数。这代表了他们接受培训的大型数据集,以及创建这些模型的人投入的大量时间和精力来微调每一层。我们看到的 VGG16 和 19 的较低精度分数并不意味着我的模型在任何方面都‘更好’,对于一个不是为我使用的特定数据集构建和训练的模型来说,这是一个非常好的分数。与我创建的模型相比,你将有更好的机会在新数据上测试这些预训练模型中的一个,并获得正确的预测,因为他们只是接触了更多的图像,尽管我支持我的模型,并欢迎挑战!最后,我们有例外模型,它的得分和我自己的模型一样好。关于这种特殊模型的一个注意事项是,它由非常非常多的层组成,但我们看到 VGG19 的参数数量相似…花一分钟时间,看看您是否能想到这可能是为什么(提示:记住要同时考虑层和节点!)。

应用和未来工作

当谈到解决我们今天在全球面临的非常大和非常真实的废物管理挑战时,将所有这些付诸实践是真正的挑战。在家坐在电脑屏幕前就能获得这类结果,这很有趣,也几乎令人难以置信,但这些技术的实际应用才是问题的核心。幸运的是,已经有很多人致力于这些艰难的挑战。我的一个灵感来自于 Bobulski,Janusz & Kubanek,Mariusz的这篇论文。(2019).下图(图 6 )显示了他们提出的系统。当废物沿着传送带运送时,首先由固定的摄像机捕获图像,然后将数据传递到预先训练的 CNN 模型中,最后给出输出,然后通知空气喷射器将废物引导到哪个垃圾箱中。

图 6。拟议的废物分类和分类系统。资料来源:Bobulski,Janusz & Kubanek,Mariusz。(2019).

这整个过程只需要几秒钟,而且可以连续运行几个小时。因此,从理论上讲,该系统可以提高整个垃圾分类过程的有效性(正确分类垃圾)和效率(能够更快和更长时间地分类)。

这个系统是一个真正的可能性,像这样的分类技术将依赖于在废物管理周期的所有阶段负责任地收集和解释数据,越来越接近循环经济的未来。

具体到我未来的工作,我很乐意沿着我已经开始的路线继续下去,访问越来越多的数据集,我可以在其上训练我的模型。然而,获得良好、全面的数据是一项挑战,也是传播此类技术的最大障碍之一。开发解决方案和实施这样的总体系统需要地方做出巨大的努力。在未来,我希望能够亲自为这些努力做出贡献,并在 2020 年 10 月从熨斗学校毕业后从事这一行业。

如果你能做到这一步,那么非常感谢你的时间!我希望这是翔实的,请随时联系我,如果你有任何问题或意见在 Tcastanley@gmail.com!

来源

“使用神经网络预测股票价格”——不要被愚弄了

原文:https://towardsdatascience.com/using-neural-networks-to-predict-stock-prices-dont-be-fooled-c43a4e26ae4e?source=collection_archive---------15-----------------------

许多人声称他们可以使用机器学习来预测股票和加密货币的价格;但没人能证明靠实时数据盈利。有什么条件?

公共使用信贷:pixabay.com

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

如果你正在阅读这篇文章,你可能已经在网上看到过使用股票/加密货币数据和机器学习算法来“预测”未来价格的博客帖子/文章。在这里,我展示了一个类似的项目,其中我使用一些指标,如 Tweet 量、Google Trends 量、市值和交易量来“预测”明天的开盘价。在这个例子中,我们将关注比特币价格,因为这是一个热门话题,但只要数据可用,这可以外推至任何资产。有人可能会天真地认为,当有一天人们在推特上谈论比特币,在谷歌上搜索比特币,并比平时更多地交易比特币时,我们可能会看到比特币的价格明天会比今天更高。此外,也许我们可以根据这些数据以足够高的精度预测明天的开盘价,最终产生一个有利可图的交易算法。为了检验这一假设,我从经济学、谷歌趋势和其他免费在线资源中收集了一些数据,得出了以下数据框架。

从机器学习的角度来看,我们可以将这四个列视为我们的功能集,将“价格”列视为我们的目标。更具体地说,明天的价格是今天特性的目标。通过一点功能工程,我们还可以包括过去一天、过去两天、过去三天等指标的变化,其中大于 1 的值表示增加,小于 1 的值表示减少。

在单个实例中对最近的变化进行编码的工程特性

线性回归

现在我们有了数据集,让我们使用一些机器学习技术来看看我们是否可以准确预测明天的开盘价。

上面的代码使用 Scikit-Learn 执行简单的线性回归,并产生以下输出:

预测价格与实际价格。R = 0.99008。RMSE=358.12

如果我们沿着时间轴绘制我们的预测价格和实际价格,我们观察到:

一段时间内的预测价格和实际价格

哇!这太不可思议了!真的这么简单吗?你所要做的就是从网上获得一些搜索和交易量数据,并使用简单的线性回归,你就可以准确预测比特币的价格?不幸的是,事实并非如此。更不幸的是,网上有很多人展示这样的结果,并声称拥有神奇的快速致富交易算法。我每天在各种提要上看到的无数文章都使用这样的技巧,以及一些流行词汇,如“机器学习”、“人工智能”和“神经网络”,来抓住你的注意力,让你认为他们在做什么。你抓到窍门了吗?

这里的技巧是,昨天的价格被输入到具有特征集的模型中(或者通常类似于 avg last 5 days)。该模型很快了解到,将预测值固定在昨天的价格上,然后根据其他特征将其略微调高或调低,以尝试朝着正确的方向进行预测,这是最好的方法。一般来说,这个可能是一个不错的策略。例如,如果其他特征完美地指示了价格变动,这将非常有用。本质上,你的模型会使用昨天的价格作为基线,然后预测上涨或下跌。如果它能像许多人声称的那样准确预测涨跌,你就能快速致富!这里主要的、欺骗性的问题是在实际价格和预测价格之间使用像 R 或 RMSE 这样的指标来向人们展示“看我的模型预测得多好!”。任何有统计学背景的人都会将 0.99 的 R 值视为一个值得崇拜的模型。尽管在这种情况下,在训练数据中包含以前的价格保证了由残差评估的这种性能。我也见过文章/博客文章的标题照片是一个类似于上面显示的 plotly 输出的情节;此外,没有代码向读者显示以前的价格实际上包括在回归算法中。如果我运行同样的算法,但是用随机生成的值填充量要素,并且只包括今天的价格数据作为信息要素,那么从远处看输出将是相同的,R 也将非常高。实现这种把戏的一种更偷偷摸摸的方法是建立一个递归神经网络或 LSTM 模型,用 n 个以前的价格为网络的每个实例排序,然后不打乱训练数据。你告诉网络最小化损失,网络很快学会:“嘿,看,如果我用昨天的价格来预测,损失函数最小化!”。

回溯测试

为了进一步证明这种交易策略的盈利能力,我做了一些回溯测试,看看如果你真的用这些预测进行日常比特币交易会发生什么。下面是一个相似的岭回归模型的结果,该模型的 R 值也大于 0.99。如果您想查看回溯测试的完整细节和代码,我在本文末尾附上了完整笔记本的链接。

正如你所看到的(令我失望),使用 Tweet 量、Google Trends、市值和交易量来预测明天的价格实际上比简单地在回溯测试间隔开始时买入和结束时卖出表现得更差,尽管模型评估指标非常出色。请记住,这还忽略了所有的交易费用,这显然会大大降低策略的性能。

话虽如此,我刚刚读了一篇由一所大学发表的学术博士撰写的文章,没有提及我概述的问题,但最终得出的结论是:“通过利用一个以推文和谷歌趋势数据为输入的线性模型,我们能够准确预测价格变化的方向。”[1]在论文的后面,他们提到“研究发现,逻辑回归在对这些推文进行分类方面表现最佳,它们能够正确预测 43.9%的价格上涨。”[1]嗯?我没看错吧?因此,作者在这里归类为“准确预测”的是,在不考虑费用的情况下,56.1%的时间亏损。哎哟。此外,在图 9 中,作者包括了一个类似于我上面展示的图(我在大约 20 分钟内生成的),其中预测价格精确地随着实际价格波动,以至于它们几乎不是不同的线。当然,没有提到昨天的价格在特性集中。

我想说明的是,将预测锁定在以前的价格,然后使用其他功能将预测向上或向下发送实际上可能是一种有用的交易策略(尽管在我看来,直接预测进场和出场比试图预测价格本身更好)。我写这篇文章的主要原因是,我认为许多人被这样的文章所欺骗,这些文章没有概述他们的训练数据的确切组成,而是试图通过在点击标题和封面照片的保护伞下只包括漂亮的情节和令人印象深刻的评估指标来打动读者。

结论

关于这个话题,我的想法是,即使这些在线文章看起来很吸引人,简单的机器学习算法并不是开发交易模型的最佳方法。我觉得这里有深度学习的地方;也许在强化学习风格的交易环境中,大型 LSTM、康文网络或其他具有技术分析类型特征的创新神经网络架构会产生多产的、有利可图的结果。最后,在研究算法交易之类的东西时,要记住几件事:

  1. 如果这看起来好得不像是真的,那很可能就是真的。再深入一点。寻找一个回溯测试,或者自己进行回溯测试。利润/损失是一个比 R 或 RMSE 更好的评估指标(参见我开发的简单回溯测试平台这里)。
  2. 当人们发现有利可图的算法时,他们一般不会发布到网上让全世界免费看到。每当你做交易时,总有人在交易的另一边。如果你赢了,他们就输了。如果你输了,他们赢了。那些成功者往往不会教育他们所资助的失败者。当然,除非你付钱让他们这么做。

这个项目的所有代码可以在这里找到复制。

引用:

[1]:使用推文量和情绪分析进行加密货币价格预测【https://scholar.smu.edu/cgi/viewcontent.cgi?article=1039 &context = datascience review

使用具有嵌入层的神经网络来编码高基数分类变量

原文:https://towardsdatascience.com/using-neural-networks-with-embedding-layers-to-encode-high-cardinality-categorical-variables-c1b872033ba2?source=collection_archive---------18-----------------------

我们如何使用具有数千个不同值的分类特征?

有多种方式对分类特征进行编码。如果类别之间不存在有序关系,one-hot-encoding 是一个受欢迎的候选(即,为每个类别添加一个二进制特征),还有和许多其他的。但是一次性编码有一些缺点——可以通过使用嵌入来解决。

稍微有帮助的插图,图片由作者使用 draw.io 完成

一个缺点是,它不适合高基数类别:它将产生非常大/宽和稀疏的数据集,并且由于大量的要素,将需要大量的内存和正则化。此外,一次性编码没有利用类别之间的关系。假设你有动物种类作为特征,值类似于家猫老虎大象。与家猫大象相比,家猫老虎可能有更多的相似之处。这是一个更聪明的编码可以考虑的事情。在非常实际的场景中,这些类别可以是诸如客户、产品或位置之类的东西——非常高的基数类别,在各个观察之间具有相关的关系。

解决这些问题的一种方法是使用嵌入,一个流行的例子是 NLP 问题中的单词嵌入。我们使用更小的密集空间,而不是使用巨大的二进制空间对类别进行编码。我们不是手动编码它们,而是定义嵌入空间的大小,然后尝试让模型学习有用的表示。对于我们的动物物种示例,我们可以检索类似于代表家猫[1, 0.4, 0.1]代表老虎[0, 0.1, 0.6]代表大象[-0.5, 0.5, 0.4]代表鲨鱼等等的表示。

在接下来的文章中,我们将使用嵌入来构建一个神经网络来编码分类特征,此外,我们将针对一个没有分类变量的非常简单的线性模型和一个具有一次性编码特征的更复杂的正则化线性模型来对该模型进行基准测试。

玩具的例子

让我们看一个产生的玩具问题。假设我们反复从不同的供应商那里购买不同的产品,并且我们想要预测它们的大小。现在让我们假设每个产品都标有一个supplier_id和一个product_id,这是供应商和产品本身的标识符。我们还假设物品具有一些明显的尺寸/特征x1x2,如priceweight,以及一些秘密的、不可测量的特征s1s2s3,理论上可以这样计算出size(S3 对尺寸没有影响):

y = f(price, weight, s1, s2, s3) 
  = price + s1 + weight * s2

问题是我们不知道秘密特征s1s2s3,我们无法直接测量它们,这实际上是机器学习中一个相当常见的问题。但是我们在这里还有一点余地,因为我们有产品和供应商 id——但是数量太多了,无法一次性编码并直接使用它们。让我们从经验中假设我们知道来自不同卖家的产品具有不同的尺寸,因此有理由假设来自卖家的物品具有非常相似的秘密属性。

y = g(supplier_id, product_id, size, weight)

问题是,即使我们有几十万个不同的 id,我们的模型能从产品和供应商 id 中学习到上面的关系g吗?答案是肯定的,如果我们有足够的观测数据。

让我们看一个小数据集,以获得更好的图片。我们生成 300 个样本,在s1中有 4 个不同的值,在s2中有 3 个不同的值(记住s3没有影响),并可视化秘密属性对价格、重量和尺寸之间关系的明显影响。

import seaborn as sns
import matplotlib.pyplot as pltdata = generate_secret_data(n=300, s1_bins=3, s2_bins=6, s3_bins=2)
data.head(10)
##   s1   s2  s3  price  weight       y
## 0  1  0.0   2  1.269   2.089   4.055
## 1  3  2.0   1  2.412   1.283   9.764
## 2  2  1.0   2  3.434   1.010   8.230
## 3  1  3.0   1  4.493   1.837  12.791
## 4  3 -2.0   2  4.094   2.562   3.756
## 5  1  2.0   2  1.324   1.802   7.714
## 6  1  2.0   1  2.506   1.910   9.113
## 7  3 -2.0   1  3.626   1.864   4.685
## 8  2  1.0   1  2.830   2.064   8.681
## 9  1  2.0   1  4.332   1.100   9.319
g = sns.FacetGrid(data, col='s2', hue='s1', col_wrap=3, height=3.5);
g = g.map_dataframe(plt.scatter, 'weight', 'y');
plt.show()

每个 s2 属性的不同重量与尺寸(y)关系

g = sns.FacetGrid(data, col='s2', hue='s1', col_wrap=3, height=3.5); g = g.map_dataframe(plt.scatter, 'price', 'y'); 
plt.show()

每个 s2 物业的不同价格与规模(y)的关系

一个玩具的例子,它将如何出现在(一种)现实

但现在的问题是:我们不知道特性s1s2s3,只知道产品 id&供应商 id。为了模拟这个数据集在野外是如何出现的,让我们引入一个散列函数,我们将使用它来模糊我们不可测量的特性,并生成一些产品和供应商 id。

import hashlibdef generate_hash(*args):
    s = '_'.join([str(x) for x in args])
    return hashlib.md5(s.encode()).hexdigest()[-4:]generate_hash('a', 2)
## '5724'
generate_hash(123)
## '4b70'
generate_hash('a', 2)
## '5724'

我们现在可以用模糊属性生成数据,用产品 id 代替:

data = generate_data(n=300, s1_bins=4, s2_bins=1, s3_bins=2)
data.head(10)
##   product_id supplier_id  price  weight      y
## 0       7235        a154  2.228   2.287  4.470
## 1       9cb6        a154  3.629   2.516  8.986
## 2       3c7e        0aad  3.968   1.149  8.641
## 3       4184        0aad  3.671   2.044  7.791
## 4       4184        0aad  3.637   1.585  7.528
## 5       38f9        a154  1.780   1.661  4.709
## 6       7235        a154  3.841   2.201  6.040
## 7       efa0        0aad  2.773   2.055  4.899
## 8       4184        0aad  3.094   1.822  7.104
## 9       4184        0aad  4.080   2.826  8.591

我们仍然看到不同的产品倾向于具有不同的值,但是我们不再能够容易地从产品 id 计算出size值:

sns.relplot(
    x='price',
    y='y',
    hue='product_id',
    sizes=(40, 400),
    alpha=1,
    height=4,
    data=data
);plt.show()

每个产品 id 的价格与尺寸(y)的关系—图片由作者制作

现在让我们生成一个更大的数据集。为了能够公平地将我们的嵌入模型与更简单的基线进行比较,并且为了能够验证我们的方法,我们将为我们的分类基数S1_BINSS2_BINSS3_BINS假设相当小的值。

如果S1_BINS, S2_BINS, S3_BINS >> 10000基准模型会遇到内存问题,性能会很差。

from sklearn.model_selection import train_test_splitN = 100000
S1_BINS = 30
S2_BINS = 3
S3_BINS = 50
data = generate_data(n=N, s1_bins=S1_BINS, s2_bins=S2_BINS, s3_bins=S3_BINS)data.describe()# cardinality of c1 is approx S1_BINS * S3_BINS,
# c2 is approx. S2_BINS * S3_BINS
##             price      weight           y
## count  100000.000  100000.000  100000.000
## mean        3.005       2.002      17.404
## std         0.997       0.502       8.883
## min        -1.052      -0.232      -2.123
## 25%         2.332       1.664       9.924
## 50%         3.004       2.002      17.413
## 75%         3.676       2.341      24.887
## max         7.571       4.114      38.641
data.describe(include='object')
##        product_id supplier_id
## count      100000      100000
## unique       1479         149
## top          8851        0d98
## freq          151        1376

我们现在将数据分为特征和响应,以及训练和测试。

x = data[['product_id', 'supplier_id', 'price', 'weight']]
y = data[['y']]
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=456)

构建基准模型:天真和基线

让我们首先组装一个非常简单的线性模型,它的性能非常差,并且只试图从priceweight中估计size:

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_errornaive_model = LinearRegression()
naive_model.fit(x_train[['price', 'weight']], y_train);y_pred_naive = naive_model.predict(x_test[['price', 'weight']])mean_squared_error(y_test, y_pred_naive)
## 77.63320758421973
mean_absolute_error(y_test, y_pred_naive)
## 7.586725358761727

如果我们观察priceweight与响应size之间的相关性,忽略 id,接近响应总体方差的不良性能是显而易见的:

sns.pairplot(data[['price', 'weight', 'y']].sample(1000));
plt.show()

价格、重量和尺寸(y)之间的相互关系—到处都是—图片由作者制作

为了获得更好的基准,我们可以对分类特性进行一次性编码,并标准化数字数据,使用sklearns ColumnTransformer将这些转换应用于不同的列。由于要素的数量,我们将使用岭回归而不是正常的线性回归来保持系数较小(但非零,不像 Lasso,这将导致丢失特定类的信息)。

from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformerdef create_one_hot_preprocessor():
    return ColumnTransformer([
        ('one_hot_encoder', OneHotEncoder(sparse=False, handle_unknown='ignore'), ['product_id', 'supplier_id']),
        ('standard_scaler', StandardScaler(), ['price', 'weight'])]
    )

one hot 预处理器将特征扩展到列并使数据变宽,此外,数字特征标准化为零均值和单位方差:

建立具有嵌入层的神经网络,包括预处理和未知类别处理

现在让我们为我们的类别建立一个具有嵌入层的神经网络模型。为了将它们提供给嵌入层,我们需要首先将分类变量映射到数字序列,即分别来自区间[0, #supplier ids]的整数。[0, #product ids]

由于sklearns OrdinalEncoder目前还不能处理未知值,我们需要随机应变。当随机分割测试数据或在预测期间看到新数据时,可能会出现未知类别。因此,我们必须使用编码器的简单实现(使用数据帧而不是数组,没有针对速度进行优化),它可以处理未知值:我们本质上使用有序字典作为散列表来将值映射到正整数范围,其中未知值将被映射到 0(我们需要映射到非负值以符合稍后的嵌入层)。

编码器现在可以编码和解码我们的数据:

ce = ColumnEncoder()ce.fit_transform(x_train)
##        product_id  supplier_id  price  weight
## 17414         941          104  2.536   1.885
## 54089         330          131  3.700   1.847
## 84350         960          122  3.517   2.341
## 68797         423           77  4.942   1.461
## 50994         617          138  4.276   1.272
## ...           ...          ...    ...     ...
## 55338         218          118  2.427   2.180
## 92761         528           10  1.705   1.368
## 48811         399           67  3.579   1.938
## 66149         531          126  2.216   2.997
## 30619        1141           67  1.479   1.888
## 
## [75000 rows x 4 columns]
ce.inverse_transform(ce.transform(x_train))
##       product_id supplier_id  price  weight
## 17414       a61d        b498  2.536   1.885
## 54089       36e6        e41f  3.700   1.847
## 84350       a868        d574  3.517   2.341
## 68797       4868        80cf  4.942   1.461
## 50994       69f3        eb54  4.276   1.272
## ...          ...         ...    ...     ...
## 55338       2429        cc4a  2.427   2.180
## 92761       5c02        0ec5  1.705   1.368
## 48811       426c        7a7d  3.579   1.938
## 66149       5d45        dc6f  2.216   2.997
## 30619       c73d        7a7d  1.479   1.888
## 
## [75000 rows x 4 columns]
x_train.equals(ce.inverse_transform(ce.transform(x_train)))
## True

它还可以通过将未知数据映射到零类别来处理未知数据:

unknown_data = pd.DataFrame({
    'product_id': ['!§$%&/()'],
    'supplier_id': ['abcdefg'],
    'price': [10],
    'weight': [20],
  })ce.transform(unknown_data)
##    product_id  supplier_id  price  weight
## 0           0            0     10      20
ce.inverse_transform(ce.transform(unknown_data))
##   product_id supplier_id  price  weight
## 0       None        None     10      20

为了将数据输入到模型中,我们需要分割输入,将其传递到不同的层,本质上是传递到X = [X_embedding1, X_embedding2, X_other]。我们可以再次使用转换器来实现这一点,这次使用的是np.arrays,因为StandardScaler返回数组:

emb = EmbeddingTransformer(cols=[0, 1])
emb.fit_transform(x_train.head(5))
## [array([['a61d'],
##        ['36e6'],
##        ['a868'],
##        ['4868'],
##        ['69f3']], dtype=object), array([['b498'],
##        ['e41f'],
##        ['d574'],
##        ['80cf'],
##        ['eb54']], dtype=object), array([[2.5360678952988436, 1.8849677601403312],
##        [3.699501628053666, 1.8469279753798342],
##        [3.5168780519630527, 2.340554963373134],
##        [4.941651644756232, 1.4606898248596456],
##        [4.27624682317603, 1.2715509823965785]], dtype=object)]

现在让我们将这两者结合起来,用训练数据装配预处理器,对类别进行编码,执行缩放并将其转换为正确的格式:

def create_embedding_preprocessor():
  encoding_preprocessor = ColumnTransformer([
      ('column_encoder', ColumnEncoder(), ['product_id', 'supplier_id']),
      ('standard_scaler', StandardScaler(), ['price', 'weight'])
  ])embedding_preprocessor = Pipeline(steps=[
      ('encoding_preprocessor', encoding_preprocessor),
      # careful here, column order matters:
      ('embedding_transformer', EmbeddingTransformer(cols=[0, 1])),
  ])return embedding_preprocessorembedding_preprocessor = create_embedding_preprocessor()
embedding_preprocessor.fit(x_train);

如果我们现在将这些数据提供给模型,它将无法学习未知类别的任何合理内容,即当我们进行fit时,在训练数据x_train中不存在的类别。因此,一旦我们试图对这些进行预测,我们可能会得到不合理的估计。

解决这个词汇外问题的一种方法是将一些随机的训练观察值设置到未知的类别中。因此,在模型的训练期间,转换将使用 0 对这些进行编码,这是未知类别的标记,并且它将允许模型学习接近未知类别的平均值的东西。有了更多的领域知识,我们还可以选择任何其他类别作为默认类别,而不是随机抽样。

# vocab sizes
C1_SIZE = x_train['product_id'].nunique()
C2_SIZE = x_train['supplier_id'].nunique()x_train = x_train.copy()
n = x_train.shape[0]# set a fair share to unknown
idx1 = np_random_state.randint(0, n, int(n / C1_SIZE))
x_train.iloc[idx1,0] = '(unknown)'idx2 = np_random_state.randint(0, n, int(n / C2_SIZE))
x_train.iloc[idx2,1] = '(unknown)'x_train.sample(10, random_state=1234)
##       product_id supplier_id  price  weight
## 17547       7340        6d30  1.478   1.128
## 67802       4849        f7d5  3.699   1.840
## 17802       de88        55a0  3.011   2.306
## 36366       0912        1d0d  2.453   2.529
## 27847       f254        56a6  2.303   2.762
## 19006       2296   (unknown)  2.384   1.790
## 34628       798f        5da6  4.362   1.775
## 11069       2499        803f  1.455   1.521
## 69851       cb7e        bfac  3.611   2.039
## 13835       8497        33ab  4.133   1.773

我们现在可以转换数据

x_train_emb = embedding_preprocessor.transform(x_train)
x_test_emb = embedding_preprocessor.transform(x_test)x_train_emb[0]
## array([[ 941.],
##        [ 330.],
##        [ 960.],
##        ...,
##        [ 399.],
##        [ 531.],
##        [1141.]])
x_train_emb[1]
## array([[104.],
##        [131.],
##        [122.],
##        ...,
##        [ 67.],
##        [126.],
##        [ 67.]])
x_train_emb[2]
## array([[-0.472, -0.234],
##        [ 0.693, -0.309],
##        [ 0.51 ,  0.672],
##        ...,
##        [ 0.572, -0.128],
##        [-0.792,  1.976],
##        [-1.53 , -0.229]])

是时候建立神经网络了!我们有 3 个输入(2 个嵌入,1 个正常)。嵌入输入都被传递到嵌入层,被展平并与正常输入连接。下面的隐藏层由 Dense & Dropout 组成,最后线性激活。

import tensorflow as tfmodel = create_model(embedding1_vocab_size=C1_SIZE+1, 
                     embedding2_vocab_size=C2_SIZE+1)tf.keras.utils.plot_model(
    model,
    to_file='../../static/img/keras_embeddings_model.png',
    show_shapes=True,
    show_layer_names=True,
)

具有嵌入层的神经网络,图像由作者使用 tensor flow . keras . utils . plot _ model 完成

num_epochs = 50model.fit(
    x_train_emb,
    y_train,
    validation_data=(x_test_emb, y_test),
    epochs=num_epochs,
    batch_size=64,
    verbose=0,
);

如果您看到类似InvalidArgumentError: indices[37,0] = 30 is not in [0, 30)的错误,则您选择了错误的词汇表大小,根据文档,它应该是嵌入值的最大索引加 1。

正如我们所看到的,对于我们的线性问题,该模型的表现与基线线性模型相当好,甚至更好,尽管它的真正优势只有在我们扩大类别空间或向响应添加非线性时才会发挥作用:

y_pred_emb = model.predict(x_test_emb)mean_squared_error(y_pred_baseline, y_test)
## 0.34772562548572583
mean_squared_error(y_pred_emb, y_test)
## 0.2192712407711225
mean_absolute_error(y_pred_baseline, y_test)
## 0.3877675893786461
mean_absolute_error(y_pred_emb, y_test)
## 0.3444112401247173

我们还可以提取嵌入层的权重(第一行包含零标签的权重):

weights = model.get_layer('embedding1').get_weights()
pd.DataFrame(weights[0]).head(11)
##         0      1      2
## 0  -0.019 -0.057  0.069
## 1   0.062  0.059  0.014
## 2   0.051  0.094 -0.043
## 3  -0.245 -0.330  0.410
## 4  -0.224 -0.339  0.576
## 5  -0.087 -0.114  0.324
## 6   0.003  0.048  0.093
## 7   0.349  0.340 -0.281
## 8   0.266  0.301 -0.275
## 9   0.145  0.179 -0.153
## 10  0.060  0.050 -0.049

这些权重有可能被保存在某个地方,并在其他模型中用作特征(预训练嵌入)。查看前 10 个类别,我们可以看到来自supplier_id的具有相似响应的值y具有相似的权重:

column_encoder = (embedding_preprocessor
  .named_steps['encoding_preprocessor']
  .named_transformers_['column_encoder'])data_enc = column_encoder.transform(data)(data_enc
  .sort_values('supplier_id')
  .groupby('supplier_id')
  .agg({'y': np.mean})
  .head(10))
##                   y
## supplier_id        
## 1            19.036
## 2            19.212
## 3            17.017
## 4            15.318
## 5            17.554
## 6            19.198
## 7            17.580
## 8            17.638
## 9            16.358
## 10           14.625

此外,该模型对于未知数据表现合理,因为其表现类似于我们非常简单的基线模型,也类似于假设简单的条件均值:

unknown_data = pd.DataFrame({
    'product_id': ['!%&/§(645h'],
    'supplier_id': ['foo/bar'],
    'price': [5],
    'weight': [1]
})np.mean(y_test['y'])
## 17.403696362314747# conditional mean
idx = x_test['price'].between(4.5, 5.5) & x_test['weight'].between(0.5, 1.5)
np.mean(y_test['y'][idx])
## 18.716701011038868# very naive baseline
naive_model.predict(unknown_data[['price', 'weight']])
## array([[18.905]])# ridge baseline
baseline_pipeline.predict(unknown_data)
## array([[18.864]])# embedding model
model.predict(embedding_preprocessor.transform(unknown_data))
## array([[19.045]], dtype=float32)

包裹

我们已经看到了如何利用嵌入层来编码高基数分类变量,并且根据基数,我们还可以调整密集特征空间的维度以获得更好的性能。这样做的代价是一个更加复杂的模型,而不是运行一个带有一次性编码的经典 ML 方法。
如果经典模型是优选的,则类别权重可以从嵌入层中提取,并在更简单的模型中用作特征,因此取代了一次性编码步骤。

最初发表于

使用 NLP 探索领导力和灵感

原文:https://towardsdatascience.com/using-nlp-to-explore-leadership-inspiration-f2e0b805d01c?source=collection_archive---------37-----------------------

对 NPR 前 350 名毕业典礼演讲进行 EDA、主题建模和情感分析

MD 杜兰Unsplash 上的照片

每年,全国各地的领导人都会站出来,通过充满情感、政治和鼓舞人心的毕业典礼演讲来激励下一代。随着今年演讲的临近,我决定探究这些领导人如何激励年轻人,以及性别是否在他们的方法中发挥了作用。

设计

为了建立我的语料库,我开始用 Beautiful Soup 搜集 NPR 排名前 350 的毕业典礼演讲的名字、年份和学校。演讲本身的获取有点棘手,因为它们存在于许多不同的格式中,包括 pdf 文档、各种新闻文章和大学网站。因此,我没有直接从 NPR 提供的链接中抓取演讲稿,而是用 Selenium 构建了一个刮刀,从 YouTube 视频中抓取文稿。由于缺少了一些演讲,我最终得到了一个超过 300 个文档的完整语料库。

利用收集的数据,我使用自然语言处理(NLP)来了解男性和女性领导者如何使用毕业典礼演讲平台。首先,我手动创建了一个男性或女性的二元列,然后使用 ed a、主题建模和情感分析来识别演讲和性别的模式。在这里找到的完整代码

调查的结果

探索性数据分析

和埃达单独在一起时,我能够开始看到毕业典礼演讲中的模式。把这些演讲分成一个单词列表,我就可以比较每个演讲的长度。虽然大多数男性和女性的演讲长度在 2000-3000 字之间,但男性的演讲比女性的演讲更长。男性演讲的平均长度为 3068 个单词,而女性演讲的平均长度为 2537 个单词,这相当于 1-2 个段落的差异。

由 Molly Liebeskind 创建的图像

我还创建了仅包含动词、仅包含名词和仅包含形容词的栏目,并发现词性的平均分布在男性和女性的演讲中是一致的。

主题建模

在探索了各种主题建模技术和矢量器之后,我确定这个问题最强的方法是使用 TF-IDF 矢量器引理化非负矩阵分解。男性和女性演讲者的演讲可以分为五个主题:教育、文化、职业、希望、政治

主题符合我对毕业典礼演讲的期望。然而,有趣的是,我发现男性和女性演讲者的主题分布有所不同。为了形成下面的图表,我们根据演讲中最常见的主题对每篇演讲进行了分类。例如,如果一个演讲主要是关于政治,但也有一点关于职业,那么它就被归类为政治。如下图所示,男性演讲者更重视职业和政治,而女性演讲者更重视教育和文化。

由 Molly Liebeskind 创建的图像

男性和女性领导人都谈到了希望、未来和抱负。

情感分析

我的下一个目标是确定毕业典礼演讲是否有一个一致的故事情节或公式。为了做到这一点,我首先将每篇演讲分成 10 个更小的部分,这将允许我观察整个演讲过程中情绪的变化。然后,使用维德情绪分析,我确定了每个部分的积极程度。我发现,在激励毕业生时,无论男女,演讲者都遵循一种一致的模式。他们以强烈的正面陈述开始,引发了兴趣和兴奋。他们利用演讲的主体来谈论他们的激情点,包括政治、教育改革、气候变化等。最后,他们以对下一代未来的鼓舞和兴奋而结束。

由 Molly Liebeskind 创建的图像

结论

大学毕业典礼是全美领导人与年轻一代分享智慧的时刻。根据演讲者的不同,学生将会接触到关于政治、教育、职业或文化的建议。然而,不管演讲者是谁,学生们都被充满希望、改变和进化的话语所鼓舞和激励。

我个人最喜欢的几个:

  • 比尔·盖茨在哈佛大学
  • 杜兰大学的艾伦·德杰尼勒斯
  • 巴拉克·欧巴马在霍华德大学
  • 肯扬学院的戴维·福斯特·华莱士

请在下面留下任何问题和评论!

Python 中的自然语言处理和观点挖掘

原文:https://towardsdatascience.com/using-nlp-to-figure-out-what-people-really-think-e1d10d98e491?source=collection_archive---------20-----------------------

Rayshard Brooks 枪击案的情感分析

来源

在本文中,我将向您展示如何使用自然语言处理(NLP)和更具体的情感分析来了解人们对某个主题的真实感受。

背景

我决定分析 Youtube 上与以下视频相关的评论:【https://www.youtube.com/watch?v=kuhhT_cBtFU】T2&t = 5s。

美国有线电视新闻网(CNN)发布的视频显示了 2020 年 6 月 12 日亚特兰大一名警察开枪打死 Rayshard Brooks 的瞬间。Rayshard Brooks 因涉嫌酒后驾车被捕,并试图逃离警察。他设法偷了一把泰瑟枪,并试图在逃跑时用它射杀一名警察。然而,美国有线电视新闻网没有显示争吵的最后一部分(在另一台摄像机上捕捉到的),但他们很快提到了它。

我很想知道人们会对整个事件以及 CNN 遗漏部分事件做出什么样的反应。非常清楚,我不是在这里给出我的观点,我只是在分析关于这个事件的说法。

获取数据

我用 selenium 搜集了 Youtube 上的评论。我总共收集了 1460 条评论。下面这篇文章展示了我是如何做到的:https://towards data science . com/how-to-scrape-YouTube-comments-with-python-61ff 197115d 4

数据如下所示:

df.head()

清理数据

这是任何 NLP 项目的重要组成部分。以下是用于清理文本的函数:

**#Import packages** import pandas as pd
import re, string
import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer 
from textblob import TextBlobsw = stopwords.words('english')**#The function** def clean_text(text):text = text.lower()
    text = re.sub('@', '', text)
    text = re.sub('\[.*?\]', '', text)
    text = re.sub('https?://\S+|www\.\S+', '', text)
    text = re.sub('<.*?>+', '', text)
    text = re.sub('[%s]' % re.escape(string.punctuation), '', text)
    text = re.sub('\n', '', text)
    text = re.sub('\w*\d\w*', '', text)
    text = re.sub(r"[^a-zA-Z ]+", "", text)

 **#Tokenize the data**    text = nltk.word_tokenize(text)
 **#Remove stopwords**    text = [w for w in text if w not in sw]return text

该函数的作用如下:

  • 删除所有大写字母、标点符号、表情符号、链接等。基本上,去掉所有不是单词或数字的东西。
  • 将数据符号化成单词,这意味着将每个注释分解成一组单独的单词。
  • 删除所有停用词,这些词不会给评论增加价值,比如“the”、“a”、“and”等。

现在让我们将函数应用于数据:

df['comment'] = df['comment'].apply(lambda x: clean_text(x))

词汇化

我决定对文本进行词汇化,这是将一个单词的屈折形式组合在一起的过程,这样它们就可以作为一个单独的项目进行分析,因为它们有相似的意思(walking 变成 walk,officers 变成 officer,等等)。).

nltk。WordNetLemmatizer()函数就是这样做的。代码如下:

**#Lemmatizer** lemmatizer = WordNetLemmatizer()def lem(text):
    text = [lemmatizer.lemmatize(t) for t in text]
    text = [lemmatizer.lemmatize(t, 'v') for t in text]
    return textdf['comment'] = df['comment'].apply(lambda x: lem(x))

最后我把数据里所有的空评论都去掉了(有些人只是评论表情符号,标点符号之类的东西)。

**#Remove all empty comments** empty_comment = df['comment'][1459]for i in range(len(df)):
    if df['comment'][i]==empty_comment:
        df=df.drop(i)df=df.reset_index(drop=True)

这留给我们 1441 条评论来分析。

分析数据

字频率

让我们使用 nltk 的 FreqDist 函数,从查看词频开始,也就是说,哪些词在评论中出现的频率最高。

**#From lists of comments to a single list containing all words** all_words=[]        
for i in range(len(df)):
    all_words = all_words + df['comment'][i]**#Get word frequency** nlp_words = nltk.FreqDist(all_words)
plot1 = nlp_words.plot(20, color='salmon', title='Word Frequency')

这并不能让我们了解人们对这个视频的感受。不过,这里还是有一些值得一提的地方:

  • 种族主义这个词被说了 17 次,种族主义这个词被说了 21 次,而残暴这个词只被说了 6 次。
  • 罪犯这个词被写了 40 次。

让我们寻找最频繁的二元组,这意味着在评论中最频繁的相邻词对。

#**Bigrams**
bigrm = list(nltk.bigrams(all_words))
words_2 = nltk.FreqDist(bigrm)
words_2.plot(20, color='salmon', title='Bigram Frequency')

好吧,好吧,现在我们开始看到一些有趣的东西。“拒捕”这个词出现得最多。这里还大量提到了泰瑟枪(射泰瑟枪、拿泰瑟枪、警泰瑟枪等),即使片段中没有显示显示雷夏德·布鲁克斯(Rayshard Brooks)发射泰瑟枪的视频。著名的“假新闻”也出现了差不多 30 次。

以下是最受欢迎的三元模型图:

我认为这个图表真正突出了人们的想法。很多假新闻和很多关于使用被盗泰瑟枪的报道。还提到了使用致命武力。

极性

使用 Textblob 的情感函数,我们可以查看评论的极性,这是一个介于-1 和 1 之间的浮点数,用于表示评论是正面(1)还是负面(-1)。例如,句子“Textblob 很棒”的极性为 0.4。

以下是如何获得每个评论的极性以及所述极性的分布:

**#Get sentiment from comments** df['comment'] = [str(thing) for thing in df['comment']]sentiment = []
for i in range(len(df)):
    blob = TextBlob(df['comment'][i])
    for sentence in blob.sentences:
        sentiment.append(sentence.sentiment.polarity)df['sentiment']=sentiment**#Plot** df['sentiment'].plot.hist(color='salmon', title='Comments Polarity')

绝大多数评论被认为是相当中性的,平均评论的极性为-0.005。大约 75%的评论极性小于 0.06,意味着正面评论很少(我当然希望如此,毕竟是枪击案!).事实上,1441 条评论中只有 45 条的极性超过 0.5。

尽管如此,我认为可以从这一分析中得出以下几点结论:

  • 人们不喜欢 CNN 遗漏了 Rayshard Brooks 开枪的片段,称该视频和 CNN 经常是“假新闻”。
  • Rayshard Brooks 的行为似乎在这里被负面地看待,许多人提到他酒后驾车,拒捕,试图逃跑并开枪。
  • 视频观众似乎认为枪击发生的条件更“可接受”,这可以解释中性的极性。
  • 令人惊讶的是很少提及警察暴行或种族主义。

这就是了。我们设法了解了人们对 CNN 发布的视频和发生的事件的感受。

评论分类

在这种情况下,很难对评论进行分类(例如正面和负面),因为我们不知道评论的“真实”类别。我们没有已知的标签来构建和评估一个简单的机器学习分类器。

然而,有标记文本数据的方法(手动、使用极性、使用正对负单词的计数等。),我将在下一篇文章中介绍。但是请记住,如果我们不知道注释的真实类别,就永远不可能知道模型的真实性能。这也是 NLP 如此具有挑战性的原因之一!

非常感谢你的阅读!

完整的代码可以在这里找到。

利用公司年报中的自然语言处理预测濒临破产

原文:https://towardsdatascience.com/using-nlp-to-predict-almost-bankruptcy-b2613ed62a3?source=collection_archive---------20-----------------------

公司年报的语言能预测股价暴跌吗?

在交易固定收益和货币多年后,我正在寻找一个项目,作为自然语言处理(NLP)的介绍。在目睹了金融和整个企业界如此多的丑闻后,我被这样一种观点吸引住了,即公司年报的措辞可能带有欺诈或不负责任的法庭特征。这种情况下,要看我说的话,而不是我报告的数字。

最大提款是给定时期内投资组合或证券的最大市值损失,是一种广泛使用的风险管理指标。该项目的目标是看看是否可以从公司年度报告的文本中预测急剧和极端的股本减少,作为破产、信贷风险和治理的一个代理,并将其与使用财务指标(FIN)和市场指标(MKT)的更传统的衡量标准进行比较。

正式调教

该问题被设置为一个二元分类问题,目标是在发布年度报告后的一年中股票价格的最大滚动 20 天下降。当提款大于或等于 80%时,目标登记“几乎破产”(正面)事件。该特征集是通过预处理公司年度报告的文本并在术语-频率-逆-文档-频率(tf-idf)矩阵中表示这些文本而得到的。

数据库

Sharadar 提供价格合理的订阅,涵盖 14,000 多家美国公司的 20 多年历史,可通过 Quandl API 访问。对于我们的应用程序来说,重要的是 Sharadar 股票数据库包括破产公司和其他退市公司。上市公司需要向 SEC 提交年度报告(10K ),这些报告可从 SEC 网站上的 EDGAR 数据库中获得。一个现有的 Python 包被用来抓取这些数据。股票价格数据库提供了 160,926 个潜在目标事件,其中 38,807 个可以与下载的年度报告数据库相匹配。

数据库示意图(图片由作者提供)

文本预处理

鉴于数据跨度长达数十年,年度报告采用多种格式,包括文本、html 和 XBRL。因此,使用 Regex 来解析报告并删除特殊字符:

def remove_html_tags_char(text):
    '''Takes in string and removes special characters '''

    #Define special Chars
    clean1 = re.compile('\n')               
    clean2 = re.compile('\r')               
    clean3 = re.compile('&nbsp;')           
    clean4 = re.compile('&#160;')
    clean5 = re.compile('  ')
    #Define html tags
    clean6 = re.compile('<.*?>')
    #remove special characters and html tags
    text = re.sub(clean1,' ', text)
    text = re.sub(clean2,' ',text)  
    text = re.sub(clean3,' ',text) 
    text = re.sub(clean4,' ',text) 
    text = re.sub(clean5,' ',text) 
    text = re.sub(clean6,' ',text) 
    # check spacing
    final_text = ' '.join(text.split())return final_text

为了与现代方法保持一致,文本经过了最低限度的处理,没有词汇化、词干化或停用词删除。这遵循了这样的想法,即有了足够大的数据集,应该让模型来确定这些细微差别(例如时态)对于手头的问题是否重要。

数据探索

数据高度不平衡:

跨管道少数民族阶层比例的演变(图片由作者提供)

毫不奇怪,“几乎破产”超过了美国经济衰退(与美国实际 GDP 的关系更加微妙——详见 GitHub):

作者图片

像许多其他市场价格变量一样,自相关性或持续性在股本缩减中很明显:

作者图片

这最后一个观察导致了市场(MKT)模型的引入,用于基线比较的目的。MKT 只是将一家公司之前的年度支出四分位数排名作为唯一特征。

交叉验证

方法

从 2015 年开始的数据被用作保留集,而训练数据从 1997 年运行到 2014 年。使用扩展窗口方法,将 CV 集分成 5 等份:

CV 集从 1997 年运行到 2015 年,并使用扩展窗口方法分成 4 次迭代(图片由作者提供)

tf-idf 矢量器在 CV 训练数据上形成,然后使用变换将测试文档转换成其 tf-idf 矩阵。宏(谐波)召回被用作 CV 分数,并且与训练集中的数据量成比例地加权(即,CV1 具有 10%的权重,CV2 具有 20%的权重,等等)。在需要加试的情况下,使用 CV 集得分的标准差。

结构

使用欠采样、过采样和类别权重来解决数据不平衡问题。考虑的另一个样本偏差是财务报表随时间的不平等分布可能会损害模型的通用性。事实上,我们可能会认为,过去 23 年中不断变化的监管、会计、法律和经济格局将在年度报告的语言和结构中得到反映。为了对此进行测试,对(I)多数类数据的随机选择和(ii)保持每年多数类事件比率等于少数类事件比率的预处理样本进行了验证(时间均衡)。最后,测试了各种概率阈值和集合。

机器学习实现示意图(图片由作者提供)

主要结果

最佳微调下各种模型的 CV 性能指标(图片由作者提供)

欠采样比过采样表现更好

时间均衡将欠采样分数从 62%提高到 66%

梯度增强的性能优于随机森林和对数回归

欠采样有利于肯定回忆(灵敏度),而过采样有利于否定回忆(特异性)

最佳模型是比率为 25% / 75%的过采样/欠采样的集合,两种模型都使用梯度增强

拒绝测试

结果

最优模型(NLP)应用于基于年度扩展窗口的维持集。然后将综合结果与基线财务比率(FIN)和市场(MKT)模型进行比较:

与 FIN 和 MKT 相比,NLP 的聚合维持结果(图片由作者提供)

NLP 混淆矩阵是:

作者图片

分析

在二元目标的基础上使用连续变量的好处之一是,我们可以通过计算混淆矩阵中每个类别的下降统计来计算模型误差的成本:

假阳性捕捉关于连续目标空间的有用信息(图片由作者提供)

尽管假阳性的比例相对较高并且相关联的精度较低,但是模型误差揭示了关于分类空间的有趣信息

这可以更实际地在领域空间中通过形成预测真实、预测负面和所有的平均加权的投资组合来充实:

模型实用程序的领域视角(图片由作者提供)

解读

对这些模型的解释有些挑战,但是我们可以看看在独立的基础上,维持集中的哪些词具有最高的概率。这是通过形成文档矩阵来实现的,其中每个文档只有一个唯一的单词,然后通过模型预测来运行该矩阵。需要注意的是,一个单词可能有很高的概率,但它只适用于一个或很少几个文档。在这方面,并不是所有的话都会被笼统地预测到。

从元数据开始,最大单个单词概率低于 50%,99.9%的单词在 20%到 30%之间。这表明是在文档中找到的单词的组合而不是单个隐含单词产生了高概率的文档。

独立单词概率表示单词组合驱动模型(图片由作者提供)

下面列出的热门词汇被手动分类。在这些类别中:会计/信贷、顾问发言/业务重组和负面情绪词汇直观上令人愉悦。

会计/信贷和顾问演讲/业务重组是直观的类别(图片由作者提供)

进一步的思考

看到一个相对简单的模型如 tf-idf 与基线财务比率模型相比表现如此之好,多少有些令人惊讶。信贷和业务重组的热门词汇类别表明,该模型擅长提取与财务比率中包含的信息类似的信息。该项目的下一阶段是探索与财务比率强劲的几乎破产事件相关的语言。这种对“额外性”的关注可能需要更复杂的算法(如 BERT)来更好地解析上下文,但可能能够揭示财务报表中不存在的会计欺诈或其他不诚实行为。

识别弱势公司是一回事,但知道这种弱势何时会在积极事件中表现出来要难得多,而且还取决于复杂的外部环境。这是对高假阳性率的合理解释。看看报告文本是否包含比已经摘录的更多的关于时间的信息将是令人着迷的。一种尝试的方法是余弦相似性,但这可能会受到维数灾难的影响。另一种方法是将单个公司的 tf-idf 矩阵本身的年度变化包括在特征集中。在总体水平上,人们可以试着检验年度 tf-idf 矩阵是否能预测下一年几乎破产数量的变化。

Github

该模型连同其他项目文件可在 GitHub 上获得。玩得开心,请随时分享反馈!

用非负矩阵分解对公司进行分类。

原文:https://towardsdatascience.com/using-nmf-to-classify-companies-a77e176f276f?source=collection_archive---------9-----------------------

从非财务标准中提取有价值的信息。

乔希·里默尔在 Unsplash 上的照片

介绍

公司是随着时间发展的复杂实体。作为一名参与投资的数据科学家,我长期以来一直在问自己这样一个问题:评估企业数据建模的最合适维度:这些东西存在于什么空间?没有比这个更好的答案了:“太多了!”。这个问题的另一种表述是询问有多少个独立标准足以描述一家公司。作为标准的例子,我们可以想到市场资本总额、工业部门、雇员人数、当前收入水平、碳足迹、金融分析师的意见等等:可用标准的数量很容易超过 100 个。这些标准不是独立的,相反,它们被一个隐含相关性的整体网络联系在一起。这使得选择相关的变量子集成为一项非常困难的任务。

分组与预测

投资从业者通过两种主要方式将机器学习应用于这些数据:

  • 通过或多或少复杂的回归,根据不同标准的历史数据预测公司的未来行为,通常是其股票市场价值的演变。这就是监督学习

照片由石页康让Unsplash 上拍摄

  • 自动将公司分组为同质集合,从而能够将这些公司与其邻居进行比较,而无需适当的标签:这种练习被称为无监督学习。金融业经常使用的一个集合的例子是工业部门的概念。

塔伊夫·拉赫曼Unsplash 上拍摄的照片

这两个练习——分组和预测——追求不同的目标,但随着更多标准的使用,这两个练习在实施时都更加困难和不稳定。这是统计学习中众所周知的现象的表现之一,被称为“维数灾难”。

因此,自然需要通过减少所用标准的数量来缩小问题的规模。说起来容易做起来难。如果我们添加一个可解释性约束,在实践中就更难实现了。

在本文中,我们将看到非负因式分解算法(NMF) 如何让这两个目标更接近。

现在让我们把这个问题形式化一点。

非负矩阵分解

假设可用数据由(n,f)类型的 X 矩阵表示,即 n 行和 f 列。我们假设这些数据是正的或零的,并且是有界的——这种假设可以放宽,但这就是精神所在。

X 的非负因式分解是 X 的一种近似分解:

其中 W 为(n,c)型,H 为(f,c)型,满足约束 W ≥ 0,H ≥ 0。
需要记住的一些要点:

  • 这种分解是近似的,而不是相等的。解 w 和 h 矩阵最小化了真实数据 x 和它们的近似 X^.之间的二次误差
  • 这种分解不一定是唯一的,即使有积极性的约束。人们确实可以以正对角 D 矩阵的形式构造单位变换矩阵,然后获得以下形式的相同分解:

d 及其逆都是正系数的对角线

  • c ≤ min(n,f)是一个整数,表示所选元件的数量。与 K-均值聚类方法一样,该参数表示方法的参数自由度。确定组件的最佳数量需要一个额外的标准。
  • 在文献中通常将 W 矩阵的线称为因子

在这里可以找到关于 NMF 的精彩介绍。

如何解读分解

通常将 X 线视为同一类别(如客户、患者、公司等)对象的 n 次观察。每个对象都有 f 个属性,矩阵中包含的数据对应于相应对象的属性所取的值。

这种分解的主要效果是减少描述一个观察结果所必需的信息。以近似为代价,X 矩阵的原始观测值可以恢复为具有正系数的线性组合——H 矩阵的线。因此实现了大小的减小:包含在原始矩阵中的部分信息,包括每个个体的 f 数据,可以由每个个体的更小的 c 数据集合来近似概括。

非负分解的另一个有趣的效果是观察和属性的自然聚集的出现。潜在的数学原理在这篇论文中会带我们走得太远,但是你可以在这篇文章【2】中找到一个非常清晰的表述。直观地,可以根据主导因子,即 c 个因子中具有最高值的因子,对观察值进行聚类。同样,可以根据对其影响最大的因素对原始特征进行分组。

这种聚集的自然可能性与系数的正定性密切相关。这是与主成分分析(PCA)的一个重要区别,在 PCA 中,要求因子成对正交,这使得不可能控制系数的符号。

企业外部财务数据的应用

为投资者收集和发布非金融企业数据本身就是一个行业,参与者是评级机构。每个机构都有自己的标准和自己的评级方法,但从一个机构到另一个机构可以找到大量的标准。

我们感兴趣的是根据环境(E)、社会(S)和治理(G)这三个主要支柱来描述公司行为的数据。这些数据被称为行为得分。他们根据男女同酬、董事会中独立董事的比例、水污染、碳足迹等标准,对公司的表现进行评分,分值范围为 0 到 100 分。

这些分数通常由评级机构每年审核一到两次,主要由公司在申报的基础上进行审核。我们将把这些分数中包含的信息用于:

  • 根据比行业部门更多的内在信息确定同类公司群体,
  • 将变量分组,以减少影响因素的数量。

本文使用的数据是来自 Vigeo-Eiris research 的 77 个行为得分,Vigeo-Eiris research 是穆迪评级机构的子公司,专门从事全球上市公司的财务外评级。我们在此展示的数值经验是基于欧洲 500 只最大股票的得分,在 2011 年 9 月至 2020 年 6 月期间进行平均评估。

Vigeo 的行为评分分为 6 个方面:

  • 环境
  • 公司治理
  • 社交:商业行为
  • 社会:人权
  • 社会:人力资源
  • 社会:社区参与

关于评分方法的更多内容可在此处找到

这种数据的多样性和粒度使得有可能设想一个比简单基于工业部门的更相关的公司分组。我们将通过对分数数据应用 NMF 来检验这一假设。

缺失数据的替换

并非所有部门的所有行动都有行为得分,因此该系列有缺失值。

各种分数的部门平均填充率的图形视图。全球平均水平为 81.6%。

图表最左侧的分数具有 100%的填充率。例如,与商业行为(以 C_S 开头)或公司治理(以 C_G 开头)相关的分数就是如此。另一方面,高度专业化的得分,如当地污染治理(名为 EnV2_6)的填充率最低,在一些主题相关性较低的部门(金融、房地产),填充率可降至 0。

像大量基于矩阵代数的方法一样,NMF 不允许丢失数据。因此,我们将用相同分数的平均值替换原始矩阵中每个分数缺失的值,该平均值取自填写了该分数的所有公司。

填充缺失数据后要素的相关矩阵-平均成对相关为 41%。两个分数之间的相关性是跨 500 只股票计算的。

寻找最佳维度

我们现在几乎准备好启动 NMF 分解。我们现在要做的就是为参数 c 选择一个值。我们对该参数的最佳值知之甚少,但我们希望以下方案是正确的:

  • c == >数据保存的小值和不精确的模型,
  • c == >数据密集的大值和更精确的模型。

这个原则将帮助我们定义一个目标函数,该函数综合了稀疏度稳定性精度的概念。这些概念确实是预测系统可解释性的关键因素,正如这里的【3】,【4】所解释的。为了简单起见,在本文中我们将把稳定性放在一边,重点放在稀疏性和精确性上。

为了量化稀疏性,让我们观察大小为(n,f)的初始矩阵已经被分别大小为(n,c)和(c,f)的两个矩阵所代替。因此,我们可以定义由以下公式给出的稀疏分数:

sp(c)是 c 分量的稀疏分数,

当然,对于 c = 1,这个分数将是最小的,在理论情况下,所有的观测值将大致是单个向量的倍数。

为了量化精度,很自然地通过以下公式定义类似于线性回归 R 的精度分数 err(c ):

逼近误差的归一化度量。

我们可以通过结合精确性和稀疏性最终定义一个可解释性得分:

这个分数是一个介于 0 和 1 之间的数字,按照惯例 0 是可解释性的最佳水平,1 是最差水平。

当 c = 1 或 c = c时,分数被校准为 100%最大值;常数 c起着最大可接受维数的作用,这里设置为 20。

这里有几行你必须写的代码,计算一个给定维度 c 的 NMF 分解:

**>>> import** **numpy** **as** **np
>>> import pandas as pd
>>> from** **sklearn.decomposition** **import** NMF**>>>** X = pd.read_csv (scores_file_path)  # reading the score data
**>>>** c = 4 **>>>** model = NMF(n_components=c, init='random', random_state=0)
**>>>** W = model.fit_transform(X)
**>>>** H = model.components_
**>>>** err = model.reconstruction_err_

矩阵 H 和 W 是计算的主要输出,需要它们来重建近似矩阵和误差函数 err(c)。

下图是通过绘制 sp(c)、err(c)和 inter score(c)的值获得的。

c = 6 时可解释性得分最佳

该图清楚地显示了精度和简约性之间的权衡,c 在 2 到 6 之间急剧下降,随后随着 c 值的增大而缓慢加速上升,在下文中,出于示例的目的,我们保留 c=6 作为元件数量。

收集 NMF 福利

特征收缩

我们从非负因式分解中获得的最明显的好处是,最初的 77 个特征被 6 个正线性组合的缩减子集,即 元特征 所取代。

随着等级的增加,特性显得更有选择性。

该图显示了 6 个新的元特征在最初的 77 个特征上的系数分布(“负载”)。正如所料,这些系数分布在几个特征上,线性组合可能难以解释。元特征#1 尤其如此,随着元特征等级的增加,系数越来越集中。

主题分配给元特征

这个数组显示了每个元特性的 6 个最具影响力的特性。有趣的是,这些流入特征的主题是相互关联的。因此,我们可以为每个元特征分配一个主题。例如,特写 V1 讲述的故事大多是关于人权 (hrts)和人力资源(hr),而 V4 讲得很清楚的是环境 (env)。

NMF 交易了维度和可解释性,通过大幅降低维度(从 77 到 6),代价是获得更多的但仍有有意义的特征。

公司集群

有趣的是,NMF 的对称公式还提供了一种将所有公司分组为 6 个可能集群的自然方法:当股票的最大价值在列 I 中时,即当其在元特征#i 上的得分最高时,我们简单地将集群#i 分配给股票。让我们来看看这种将公司分组的新方法如何对应于通常的行业:

NC = 6 个分量的扇区/ NMF 簇对应关系

基于 NMF 的聚类带来了不包含在单独的部门中的信息。我们可以将前面图中的每一行作为各个元特征的每个扇区的“签名”。例如,能源部门在与环境主题相关的元特征#4 中得分最低。

缺失数据

记住,我们从一个包含缺失数据的 X 矩阵开始。我们已经用每一列的全局平均值填充了空的单元格,并用填充后的矩阵填充了我们的 NMF 算法。

既然我们已经有了股票的聚类,为什么我们不尝试通过用每列的平均值代替空单元格来改进缺失数据的填充,用相同聚类的股票来代替所有股票呢?这将提供对 X 的新估计,该估计又可以用作因式分解的输入。如此等等…

使用 NMF 对缺失数据进行自举可以减少 20%的近似误差。

与基于均值的先验知识相比,NMF 提供的线聚类允许以更高的精度插值数据矩阵的缺失值。这种方法在许多领域非常有用,主要优点是相对于像扇区这样的外部聚类的独立性:它是完全数据驱动的吗?

结论

本文旨在说明并支持如何使用非负矩阵分解作为一种强大的工具来创建观察值和特征的自然分组。这一点在生命科学领域早就被认识到,特别是在基因组学领域,减少特征的数量同时保持分析的可解释性是至关重要的。

将 NMF 应用于额外的金融数据是提取这些数据集中包含的有价值信息的一种创新方法。我们使用了特定提供商 Vigeo-Eiris 的数据,但好消息是,当使用多个数据源来展示公司的基本属性时,这种方法将非常稳健。

进一步阅读的参考资料

[1]丁,钟汉卿;陶;Jordan,M.I .凸和半非负矩阵分解(2010 年)。IEEE Trans。模式
肛门。马赫。Intelli。拱门。2010, 32, 44–55.

[2] P.Fogel,Y. Gaston-Mathé等人,使用非负矩阵分解的新型聚类方法在公共卫生环境
研究中的应用(2016),国际环境研究和公共卫生杂志。

[3] B. Yu 和 K. Kumbier,数据科学的三个原则:可预测性、可计算性和稳定性(pcs) (2019),arXiv 预印本 arXiv:1901.08152。

[4]诉玛戈特。比较可解释性的严格方法(2020),arXiv 预印本 arXiv:2004.01570。

[5] Vigeo-Eiris,网站(2020 年)

使用零样本来形成决策空间和防御敌对攻击

原文:https://towardsdatascience.com/using-null-samples-to-shape-decision-spaces-and-defend-against-adversarial-attacks-3ecd16b6596c?source=collection_archive---------35-----------------------

一种更自然的模型训练方法

在机器学习分类模型中,各个类别的最佳决策空间可能是复杂且不相交的,并且仅占据整个输入空间的很小一部分。尽管上图中的绳索纠缠在一起,但人们可以很容易地发现图像中不属于任何绳索的点(这是绝大多数的点)。然而,我们在机器学习社区中不允许我们传统训练的机器学习模型做同样的事情。在这幅图中,这样的模型会愚蠢地将每个点分配给一条绳索。这种不自然的条件可能是这些模型易受恶意(欺骗)攻击的根本原因。

这篇文章是我最近在 arXiv.org 上发表的一篇技术报告的摘要。在这里阅读完整的报告(和正式的引文):https://arxiv.org/abs/2002.10084

介绍

大约在 2012 年计算机视觉的新一波神经网络模型到来后不久[Krizhevsky,2012;LeCun,2015 年],人们发现,这种网络可以被巧妙设计的图像所欺骗,这些图像对人类来说,看起来与原始图像的改变很小,如果有的话[Szegedy,2013 年]。这种所谓的敌对例子给许多需要抵御敌对行为者的计算机视觉应用带来了问题。

通过向熊猫图像添加一点点噪声,常规训练的神经网络以很高的可信度将熊猫图像误认为长臂猿图像[Goodfellow,2014]。

显然,卷积神经网络(CNN)模型做出决定的方式与人类或任何其他哺乳动物都非常不同。

问题的一个可能来源是,模型被设计和训练为将图像分类为属于 N 类(对象)之一,而不管图像。例如,如果一个模型被训练来对从 0 到 9 的孤立数字的图像进行分类,然后显示一个“T”或一个“$”或一只驴子,它会将该图像分类为其中一个数字-这就是它被训练做的所有事情。

这意味着在可能图像的高维输入空间中,每个点都被分配给一个对象类。各个类的决策空间共同填充整个空间,尽管对于给定的对象类,真实样本仅位于学习决策空间的一个小的子区域内。决策空间彼此邻接,即使空间内的真实样本被很好地分开。

下面介绍的是一个玩具模型和任务,它允许这种情况的可视化,它使传统模型容易受到敌对攻击的方式,以及缓解该问题的策略。

典型的例子

图 1:玩具示例的最佳决策空间

在我们的玩具环境中,输入“图像”是三个像素的线性阵列,其中可能存在两个对象中的一个,每个对象是一对具有特定值的相邻像素。上图显示了两类物体(洋红色和青色)可能存在的位置。空间位置与类别身份无关,因此单个类别的对象样本存在于 3D 输入空间的两个隔离区域中。彩色区域代表两个类别的“最佳”决策空间,即在洋红色和青色区域之外不存在训练样本。

我们在这个分类任务上训练了一个简单、传统的 CNN,然后用跨越整个输入空间的样本来探测该模型。产生的决策空间如下所示。

图 2:传统 CNN 模型的学习决策空间

传统 CNN 的决策空间完全填充了输入空间——对于给定的架构和训练,它们必须如此——并且各个类的空间(图 2 的中间和右边的面板)比真实对象样本(图 1)的空间大得多。不难想象,当添加到真实对象样本中时,一点点噪声可能会将图像推过决策边界。

缓解这个问题的一个方法是(1)向模型添加一个 (N +1)ᵗʰ输出——该输出指示一个空类,而不是 n 个对象类中的一个,以及(2)在训练期间使用空样本以及对象样本。对于玩具示例,我们使用均匀噪声的三像素图像作为零样本。

图 3:零训练 CNN 模型的学习决策空间

在训练完这个零模型后,我们像对传统模型一样可视化决策空间。如上所示,决策空间已经大大缩小了。该图仅显示了两个对象类的决策空间。视觉上的空白空间是 null 类的决策空间。换句话说,如果输入图像没有落入其中一个对象类的决策空间,则允许该模型将输入图像“未分类”。

我们推测,随着输入空间维度的增加,玩具示例中传统模型所展示的情况只会变得更糟。习得的决策空间是错综复杂的,真实的对象样本可能位于与对象的因果特征不相关的决策边界附近——也就是说,使其成为其类成员的特征。

MNIST 数字识别的零训练模型

虽然 MNIST 数字分类通常被认为是一个“简单”的分类基准任务,CNN 模型仍然容易受到敌对攻击。已经取得了进展[Madry,2017],但即使是最好的常规 CNN 模型也仍然很脆弱[Schott,2018]。有人可能会说,其他模型类型已经“解决”了这个问题,例如 Schott 等人的综合分析模型【Schott,2018】。我们从这种模型中获得了有益的见解,但它们在计算上非常昂贵,对于使用 2020 年硬件处理照片级逼真图像的应用程序来说,不太可能是可行的解决方案。

因此,我们有动力在 MNIST 基准测试任务上测试我们的方法。我们首先训练了一个常规(或“基线”)CNN,在未受干扰的 MNIST 测试集上实现了 99%以上的准确率。然后,我们使用快速梯度符号方法(FGSM) [Goodfellow,2014]来创建具有足够噪声(噪声乘数的最小可能值,ε)的对立图像,以便基线模型将数字分类为除未受干扰的源图像之外的数字。示例如下所示。

图 4:从基线传统模型中创建的对立例子。带有红色边框的图像是那些模型犯了错误分类错误的图像,这是所有这些图像的设计。每个图上方的数字依次为:源图像标签、模型预测标签和预测标签的得分(概率)。

然后,我们训练了一个零模型,使用三种类型的零样本——均匀噪声、混合数字图像和平铺混洗图像。在训练期间,所有的空图像都被赋予空目标标签。

图 5:在零模型的训练中使用的零图像的例子。顶行:均匀噪声。中间一行:混合数字图像。底部一行:不同大小的随机图像。

在训练零模型之后,我们在从基线模型创建的敌对图像上测试它。如下图所示,大多数对抗性图像被归类为空图像(即“未分类”),而不是错误地归类为不同的数字。

图 6:从基线模型中创建的对抗性例子,并由空模型评分。图标题如图 4 所示。边框-绿色:数字分类正确。红色:分类错误的数字,蓝色:未分类。

最后,我们评估了一组基线模型和几组零模型的性能(每一组都用不同类型的零样本训练)。我们不是简单地在一组固定的对立例子上进行测试,而是通过将噪声乘数ε的范围从 0 变到 1 来改变扰动的程度。

图 7:基线和模型在一系列扰动下的性能(ε值)。左:零模型训练的混合数字零样本。中间:在混排数位零样本上训练的零模型。右图:在混合和混排样本上训练的空模型。

如上所述,用混合数位零样本(左图)训练的零模型有效地防止了对具有低扰动的图像的错误分类,优先将这样的图像分类为零。相比之下,用混洗数位零样本(中间的面板)训练的模型在防止对具有高扰动的图像的错误分类方面是有效的。当对这两种类型的零样本(右图)进行训练时,不管扰动的大小如何,零模型很少产生错误分类。

结论

在混合数字和混洗数字零样本上训练的 MNIST 零模型很少在扰动图像上犯数字误分类错误,并且它们准确地分类未扰动的数字图像。此外,我们的零训练方法在训练和推理期间都是计算有效的。

然而,应该强调的是,尽管这些图像中的数字很容易被人类识别,但是零模型不能正确地对具有适度扰动的图像进行分类。相反,这些模型用错误分类换取无效分类。这种折衷对于许多应用程序来说可能是完全可以接受的,因为错误分类可能是灾难性的,而空分类可以被忽略或以其他方式处理。

为了开发既能很好地概括又能抵御敌对攻击的模型,还需要额外的研究。一种策略可能是将我们的零训练方法与受损图像训练相结合,最近的研究表明,如果元参数值得到适当调整,就可以提高泛化能力[Rusak,2020]。

使用 ODBC 将任何数据库直接连接到 Jupyter notebook。

原文:https://towardsdatascience.com/using-odbc-to-connect-any-database-directly-to-jupyter-notebook-19741b4b748?source=collection_archive---------8-----------------------

嘘,如果你不喜欢 ODBC,也可以选择

无论您是将数据存储在本地还是远程系统中,无论是哪种类型的数据库管理系统, ODBC 都是解决方案。

介绍

ODBC 是一个开放的接口,使用 ODBC 驱动程序可以连接几乎所有的数据库管理系统。ODBC 驱动程序是由数据库管理系统提供的,它有助于连接特定的数据库系统。

因此,当数据科学家或分析师需要在他们喜欢的 Jupyter NB 中快速运行查询时,ODBC 就派上了用场。

现在对于 R 中 ODBC 的连接,你可以跟随其他教程,因为我将只演示 python 实现。

注意除了 ODBC 之外,您可以利用其他的包进行连接,但是大多数包只能连接特定的数据库管理系统。

用于 ODBC 的 python 包: pyodbc

微软 SQL

安装 MS-SQL 驱动程序—仅当数据在远程机器上时才需要安装

根据您的操作系统下载 MS-SQL 驱动

窗户

  1. 对于 windows,只需点击一下鼠标即可轻松安装。MS-SQL 的 ODBC 在这里可用

2.安装。msi 文件按照你的位。

Linux — Ubuntu

sudo su 
curl [https://packages.microsoft.com/keys/microsoft.asc](https://packages.microsoft.com/keys/microsoft.asc) | apt-key add -#Download appropriate package for the OS version
#Choose only ONE of the following, corresponding to your OS version#Ubuntu 14.04
curl [https://packages.microsoft.com/config/ubuntu/14.04/prod.list](https://packages.microsoft.com/config/ubuntu/14.04/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Ubuntu 16.04
curl [https://packages.microsoft.com/config/ubuntu/16.04/prod.list](https://packages.microsoft.com/config/ubuntu/16.04/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Ubuntu 18.04
curl [https://packages.microsoft.com/config/ubuntu/18.04/prod.list](https://packages.microsoft.com/config/ubuntu/18.04/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Ubuntu 18.10
curl [https://packages.microsoft.com/config/ubuntu/18.10/prod.list](https://packages.microsoft.com/config/ubuntu/18.10/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Ubuntu 19.04
curl [https://packages.microsoft.com/config/ubuntu/19.04/prod.list](https://packages.microsoft.com/config/ubuntu/19.04/prod.list) > /etc/apt/sources.list.d/mssql-release.listexit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql17
# optional: for bcp and sqlcmd
sudo ACCEPT_EULA=Y apt-get install mssql-tools
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
# optional: for unixODBC development headers
sudo apt-get install unixodbc-dev

Linux — Debian

sudo su 
curl [https://packages.microsoft.com/keys/microsoft.asc](https://packages.microsoft.com/keys/microsoft.asc) | apt-key add -#Download appropriate package for the OS version
#Choose only ONE of the following, corresponding to your OS version#Debian 8
curl [https://packages.microsoft.com/config/debian/8/prod.list](https://packages.microsoft.com/config/debian/8/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Debian 9
curl [https://packages.microsoft.com/config/debian/9/prod.list](https://packages.microsoft.com/config/debian/9/prod.list) > /etc/apt/sources.list.d/mssql-release.list#Debian 10
curl [https://packages.microsoft.com/config/debian/10/prod.list](https://packages.microsoft.com/config/debian/10/prod.list) > /etc/apt/sources.list.d/mssql-release.listexit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql17
# optional: for bcp and sqlcmd
sudo ACCEPT_EULA=Y apt-get install mssql-tools
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
# optional: for unixODBC development headers
sudo apt-get install unixodbc-dev
# optional: kerberos library for debian-slim distributions
sudo apt-get install libgssapi-krb5-2

Linux — RedHat

sudo su#Download appropriate package for the OS version
#Choose only ONE of the following, corresponding to your OS version#RedHat Enterprise Server 6
curl [https://packages.microsoft.com/config/rhel/6/prod.repo](https://packages.microsoft.com/config/rhel/6/prod.repo) > /etc/yum.repos.d/mssql-release.repo#RedHat Enterprise Server 7
curl [https://packages.microsoft.com/config/rhel/7/prod.repo](https://packages.microsoft.com/config/rhel/7/prod.repo) > /etc/yum.repos.d/mssql-release.repo#RedHat Enterprise Server 8
curl [https://packages.microsoft.com/config/rhel/8/prod.repo](https://packages.microsoft.com/config/rhel/8/prod.repo) > /etc/yum.repos.d/mssql-release.repoexit
sudo yum remove unixODBC-utf16 unixODBC-utf16-devel #to avoid conflicts
sudo ACCEPT_EULA=Y yum install msodbcsql17
# optional: for bcp and sqlcmd
sudo ACCEPT_EULA=Y yum install mssql-tools
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
# optional: for unixODBC development headers
sudo yum install unixODBC-devel

苹果操作系统

/usr/bin/ruby -e "$(curl -fsSL [https://raw.githubusercontent.com/Homebrew/install/master/install](https://raw.githubusercontent.com/Homebrew/install/master/install))"
brew tap microsoft/mssql-release [https://github.com/Microsoft/homebrew-mssql-release](https://github.com/Microsoft/homebrew-mssql-release)
brew update
brew install msodbcsql17 mssql-tools

安装 python 的 pyodbc — ODBC 包

  1. 安装 pyodbc 包。
pip install pyodbc

2.要检查驱动程序是否安装正确,请找到所有连接到 pyodbc 的驱动程序。

import pyodbc
pyodbc.drivers()

对于 MS-SQL,它将导致

['ODBC Driver 17 for SQL Server']

随着更多的驱动程序添加到您的系统,更多的驱动程序将被添加到列表中。

连接到数据库

  1. 用于远程连接。
# enter ip address and port number of the system where the database resides.
server = 'tcp:31.288.186.65,49170'
database = 'database_name' # enter database name
username = 'user_name'
password = 'pass_word' # add appropriate driver name
cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)cursor = cnxn.cursor()

2.用于本地连接(如果数据在本地计算机中)。

 server = 'tcp:31.288.186.65,49170'
database = 'database_name' # enter database namecnxn = pyodbc.connect('DRIVER={SQL Server};SERVER='+server+';DATABASE='+database+';Trusted_Connection=yes;')cursor = cnxn.cursor()

查询数据库

  1. 你可以在你的笔记本上查询数据库,即选择、插入、更新或删除。

您的查询可以直接转换为熊猫数据框架。

import pandas as pd# select command
query = ''' SELECT RecordID FROM tables''';
data = pd.read_sql(query, cnxn)
data.head()

替代品

或者,您可以使用 pymssql ,它的工作原理相同,但已经停止使用。不过,如果你想使用它,你必须安装

pip install "pymssql<3.0"

神谕

Oracle 也支持 ODBC 数据库。你需要根据你的操作系统为 oracle 安装一个 ODBC 驱动。

  1. 安装 Oracle ODBC 客户端。

oracle 网站下载 instant client basic 和 odbc 包。

2.如果下载的包是 zip,使用 wget 解压或者 rpm 获取 rpm 文件。

3.设置 ORACLE_HOME 和 LD_LIBRARY_PATH

export ORACLE_HOME=/usr/lib/oracle/12.2/client64
export LD_LIBRARY_PATH=/usr/lib/oracle/12.2/client64/lib

根据您的版本更改路径“/12.2/”。

4.在/etc/odbcinst.ini 集中:

[oracle_db]
Description=Oracle ODBC driver for Oracle 12c
Driver=/usr/lib/oracle/12.2/client64/lib/libsqora.so.12.1
FileUsage=1
Driver Logging=7
UsageCount=1

5.现在,打开 Jupyter NB,您可以轻松连接到您的 oracle 数据库。

import pyodbc
conn = pyodbc.connect('DRIVER={oracle_db};Host=1.1.1.1;Port=1521;Service Name=orcl.local;User ID=test1;Password=test1')

替代品

或者,您可以使用 Cx_Oracle ,它的工作方式类似,但只适用于 Oracle 数据库。

我的 SQL

  1. 对于 MySQL,它与上面的配置非常相似,您需要安装合适的 MySQL 驱动程序。
import pyodbc
conn = pyodbc.connect('DRIVER {MySQL};SERVER=localhost;DATABASE=test;UID=root;PWD=abc;')

替代品

对于 Mysql,有许多包可以连接到 python,比如

MONGO 数据库

  1. 对于 MongoDB ,安装 MongoDB 驱动并连接到 pyodbc。

可供选择的事物

或者,您可以使用 pymongo 来连接 python。

结论

根据连接性,有多个包来连接数据库,但是如果您只想要一个通用包, pyodbc 就是您想要的包,它使用 ODBC 接口来访问独立于数据库系统的数据。

此外,对于任何澄清,评论,帮助,赞赏或建议,只需张贴在评论中,我会帮助你。

如果你喜欢,你可以跟着我。

[## Gaurav Chauhan -中等

阅读 Gaurav Chauhan 在媒体上的文章。hopscotch.health 的数据科学家,每天,Gaurav Chauhan 和数千人…

medium.com](https://medium.com/@gauravc2708)

此外,如果你想要更多基于数据科学、人工智能或数据管道的内容,可以在我的社交媒体上连接我。

使用 Pandas 方法链接提高代码可读性

原文:https://towardsdatascience.com/using-pandas-method-chaining-to-improve-code-readability-d8517c5626ac?source=collection_archive---------4-----------------------

熊猫方法链接的最佳实践教程

照片由斯蒂芬·莱昂纳迪Unsplash 上拍摄

我们一直在讨论使用 Pandas 管道函数来提高代码可读性。在本文中,我们来看看熊猫方法链接

在数据处理中,经常需要对某一行或某一列执行操作以获得新的数据。而不是写作

df = pd.read_csv('data.csv')
df = df.fillna(...)
df = df.query('some_condition')
df['new_column'] = df.cut(...)
df = df.pivot_table(...)
df = df.rename(...)

我们能做的

(pd.read_csv('data.csv')
   .fillna(...)
   .query('some_condition')
   .assign(new_column = df.cut(...))
   .pivot_table(...)
   .rename(...)
)

方法链接在 Pandas 中一直可用,但是通过添加新的“可链接”方法,对链接的支持增加了。例如query()assign()pivot_table(),特别是[pipe()](/using-pandas-pipe-function-to-improve-code-readability-96d66abfaf8),用于在方法链中允许用户自定义方法。

方法链是一种编程风格,它按顺序调用多个方法调用,每个调用对同一个对象执行一个操作并返回该操作。

它消除了在每个中间步骤命名变量的认知负担。 Fluent 接口,一种创建面向对象 API 的方法依赖于方法级联(又名方法链)。这类似于 Unix 系统中的管道。

阿迪亚曼·科尔提

方法链接大大增加了代码的可读性。让我们深入教程,看看它如何提高我们的代码可读性。

源代码请访问我的 Github 笔记本

数据集准备

在本教程中,我们将处理来自 Kaggle 的泰坦尼克号数据集。这是一个非常著名的数据集,通常是学生学习数据科学的第一步。让我们导入一些库并加载数据来开始。

import pandas as pd
import sys
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'**df = pd.read_csv('data/train.csv')**
df.head()

我们将 train.csv 文件加载到熊猫数据帧中

泰坦尼克号数据预览

来自 Kaggle 的数据字典

让我们从检查缺失值开始。我们可以使用 seaborn 创建一个简单的热图来查看哪里缺少值

sns.heatmap(**df.isnull()**, 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')

缺失值的 seaborn 热图输出

年龄船舱上船 有缺失值。 年龄 缺失的比例很可能小到足以用某种形式的插补进行合理替换。看着 栏,好像缺了很多值。 着手 缺失的比例很小。

工作

假设我们被要求看一看从南安普顿出发的乘客,并计算出不同年龄组和p 等级 的存活率。

让我们把这项任务分成几个步骤,一步一步地完成。

  1. 数据清理:用某种形式的插补替换缺失的 年龄
  2. 选择从南安普敦出发的乘客
  3. 将年龄转换为年龄范围组:≤12 岁、青少年(≤ 18 岁)、成人(≤ 60 岁)和老年人(> 60 岁)
  4. 创建一个数据透视表来显示不同年龄组的存活率和 Pclass
  5. 通过重命名轴标签和格式化值来改进数据透视表的显示。

酷,让我们继续使用熊猫方法链接来完成它们。

1.用某种形式的插补替代缺失的年龄

正如在数据准备中提到的,我们希望用某种形式的插补来替换缺失的 年龄 。一种方法是填写所有乘客的平均年龄。然而,我们可以更聪明地处理这个问题,按乘客级别检查平均年龄。例如:

sns.boxplot(x='Pclass',
            y='Age',
            data=df,
            palette='winter')

我们可以看到,在较高的阶层中,较富裕的乘客往往年龄较大,这是有道理的。我们将根据年龄的 Pclass 使用这些平均年龄值进行估算。

pclass_age_map = {
  **1: 37,
  2: 29,
  3: 24,**
}def replace_age_na(x_df, fill_map):
    **cond=x_df['Age'].isna()
    res=x_df.loc[cond,'Pclass'].map(fill_map)
    x_df.loc[cond,'Age']=res** return x_df

x_df['Age'].isna()选择 年龄 栏,检测缺失值。然后,x_df.loc[cond, 'Pclass']用于有条件地访问 Pclass 值,并调用熊猫[map()](/introduction-to-pandas-apply-applymap-and-map-5d3e044e93ff)将每个值替换为另一个值。最后,x_df.loc[cond, 'Age']=res有条件地用res替换所有缺失的年龄值。

运行以下代码

res = (
  pd.read_csv('data/train.csv')
    **.pipe(replace_age_na, pclass_age_map)**
)res.head()

应根据年龄的 Pclass 替换所有缺失的年龄。让我们通过运行res上的热图来检查一下。

sns.heatmap(**res.isnull()**, 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')

太好了,成功了!

2.选择从南安普敦出发的乘客

根据泰坦尼克号数据字典,从南安普顿出发的乘客应该是乘坐 登上 ,价值S。让我们使用 Pandas query()函数进行查询。

res = (
  pd.read_csv('data/train.csv')
    .pipe(replace_age_na, pclass_age_map)
    **.query('Embarked == "S"')**
)res.head()

为了评估查询结果,我们可以使用value_counts()进行检查

res.Embarked.value_counts()**S    644**
Name: Embarked, dtype: int64

3.将年龄转换为年龄范围组:≤12 岁、青少年(≤ 18 岁)、成人(≤ 60 岁)和老年人(> 60 岁)

我们在文章 Pandas pipe function 中使用了一个自定义函数。或者,我们可以使用 Pandas 内置函数assign()向 DataFrame 添加新列。让我们从assign()开始。

**bins=[0, 13, 19, 61, sys.maxsize]
labels=['<12', 'Teen', 'Adult', 'Older']**res = (
  pd.read_csv('data/train.csv')
    .pipe(replace_age_na, pclass_age_map)
    .query('Embarked == "S"')
    **.assign(ageGroup = lambda df: pd.cut(df['Age'], bins=bins, labels=labels))**
)res.head()

熊猫assign()用于创建新列 年龄组 。新列是用 lambda 函数和 Pandas cut()一起创建的,用于将年龄转换为范围组。

通过运行代码,我们应该得到如下输出:

4.创建一个数据透视表来显示不同年龄组和p 类 的存活率

数据透视表让我们能够洞察我们的数据。让我们用它来计算存活率。

bins=[0, 13, 19, 61, sys.maxsize]
labels=['<12', 'Teen', 'Adult', 'Older'](
  pd.read_csv('data/train.csv')
    .pipe(replace_age_na, pclass_age_map)
    .query('Embarked == "S"')
    .assign(ageGroup = lambda df: pd.cut(df['Age'], bins=bins, labels=labels))
    **.pivot_table(
        values='Survived', 
        columns='Pclass', 
        index='ageGroup', 
        aggfunc='mean')**
)

第一个参数values='Survived'指定要聚合的幸存列。由于存活的值是10,我们可以使用聚合函数mean来计算存活率,因此使用了aggfunc='mean'index='ageGroup'columns='Pclass'会在输出表中将 年龄组 显示为行,将 Pclass 显示为列。

通过运行代码,我们应该得到如下输出:

5.通过重命名轴标签和格式化值来改进数据透视表的显示。

到目前为止,我们得到的结果不是很清楚。让我们继续改进显示。

bins=[0, 13, 19, 61, sys.maxsize]
labels=['<12', 'Teen', 'Adult', 'Older'](
  pd.read_csv('data/train.csv')
    .pipe(replace_age_na, pclass_age_map)
    .query('Embarked == "S"')
    .assign(ageGroup = lambda df: pd.cut(df['Age'], bins=bins, labels=labels))
    .pivot_table(
        values='Survived', 
        columns='Pclass', 
        index='ageGroup', 
        aggfunc='mean')
    **.rename_axis('', axis='columns')
    .rename('Class {}'.format, axis='columns')
    .style.format('{:.2%}')**
)

rename_axis()用于清除列标签。之后,rename('Class {}'.format, axis='columns')用于格式化列标签。最后,style.format('{:.2%}')用于将值格式化为带两位小数的百分比。

通过运行代码,我们应该得到如下输出

性能和缺点

在性能方面,根据 DataSchool [2],方法链提前告诉 pandas 所有事情,因此 pandas 可以更有效地计划其操作,因此它应该比常规方法有更好的性能。

方法链接可读性更好。然而,一个非常长的方法链可能可读性较差,特别是当在链中调用其他函数时,例如,在我们的教程中,在assign()方法中使用了cut()

此外,使用方法链的一个主要缺点是调试会更加困难,尤其是在非常长的链中。如果最后看起来有问题,你没有中间值来检查。

关于这个主题的更长的讨论,请参见汤姆·奥格斯伯格的 方法链接帖子【1】。

好了

感谢阅读。

请在我的 Github 上查看笔记本的源代码。

如果你对机器学习的实用方面感兴趣,请继续关注。

最后,这里有两篇你可能感兴趣的相关文章

参考

使用 Pandas 管道函数提高代码可读性

原文:https://towardsdatascience.com/using-pandas-pipe-function-to-improve-code-readability-96d66abfaf8?source=collection_archive---------10-----------------------

熊猫最佳实践的直观指南pipe()

照片由 June WongUnsplash 拍摄

在数据处理中,经常需要编写一个函数对某一行或某一列进行运算(如统计计算、拆分或代入值)以获得新的数据。

而不是写作

# f(), g(), and h() are user-defined function
# df is a Pandas DataFramef(g(h(df), arg1=a), arg2=b, arg3=c)

我们可以写作

(df.pipe(h)
   .pipe(g, arg1=a)
   .pipe(f, arg2=b, arg3=c)
)

熊猫从 0.16.2 版本开始引入pipe()pipe()在方法链中启用用户定义的方法。

方法链接是一种编程风格,它依次调用多个方法调用,每个调用在同一个对象上执行一个动作并返回它。

消除了在每个中间步骤命名变量的认知负担。 Fluent 接口,一种创建面向对象 API 的方法依赖于方法级联(又名方法链)。这类似于 Unix 系统中的管道。

作者阿迪亚曼·科尔西

方法链接大大增加了代码的可读性。让我们深入教程,看看它如何提高我们的代码可读性。

数据集准备

在本教程中,我们将处理来自 Kaggle 的泰坦尼克号数据集。这是一个非常著名的数据集,通常是学生学习数据科学的第一步。让我们导入一些库并加载数据来开始。

import pandas as pd
import sys
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'**def load_data():
    return pd.read_csv('data/train.csv')**df = load_data()
df.head()

我们创建了一个函数load_data()来将 train.csv 文件加载到 pandas DataFrame 中。

泰坦尼克号数据预览

数据字典来自 Kaggle

让我们从检查缺失值开始。我们可以使用 seaborn 创建一个简单的热图来查看哪里缺少值

sns.heatmap(**df_train_raw.isnull()**, 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')

缺失值的 seaborn 热图输出

年龄船舱有缺失值。 年龄 缺失的比例很可能小到足以用某种形式的插补进行合理替换。看着 栏,好像缺了很多值。 着手 缺失的比例很小。

任务

假设我们被要求完成以下任务

  1. 拆分为名和名
  2. 对于 性别 ,用 M 替换值,用 F 替换值母**
  3. 用某种形式的插补替换缺失的 年龄
  4. 将年龄转换为年龄范围组:≤12 岁、青少年(≤18 岁)、成人(≤60 岁)和老年人(> 60 岁)。

让我们继续使用pipe()一步一步地完成它们,

1.将姓名分为名和姓

让我们创建一个函数split_name(),它接受一个数据帧作为输入并返回一个数据帧。

*def split_name(x_df):
    def split_name_series(string):
        firstName, secondName=string.split(', ')
        return pd.Series(
            (firstName, secondName),
            index='firstName secondName'.split()
        ) # Select the Name column and apply a function
    **res=x_df['Name'].apply(split_name_series)**
    x_df[res.columns]=res
    return x_df*

x_df['Name'] 选择 名称 列(在熊猫中称为一个系列),熊猫[apply()](/introduction-to-pandas-apply-applymap-and-map-5d3e044e93ff)用于对系列的值应用split_name_series()函数。

运行以下代码

*res=(
    load_data()
    **.pipe(split_name)**
)res.head()*

我们应该得到如下输出:

split_name()的结果

2.对于 性别 ,用 M 替代值女*用 F*

让我们创建一个函数substitute_sex(),它接受一个数据帧作为输入并返回一个数据帧。

*def substitute_sex(x_df):
    mapping={'male':'M','female':'F'}
    x_df['Sex']=df['Sex'].map(mapping)
    return x_df*

x_df['Sex']选择性别列,然后选择熊猫[map()](/introduction-to-pandas-apply-applymap-and-map-5d3e044e93ff),用于将序列中的每个值替换为另一个值。

运行以下代码

*res=(
    load_data()
    .pipe(split_name)
    **.pipe(substitute_sex)**
)res.head()*

我们应该得到如下输出:

substitute_sex()的结果

3.用某种形式的插补替换缺失的 年龄

我们想用某种形式的插补来代替缺失的年龄。一种方法是填写所有乘客的平均年龄。然而,我们可以更聪明地处理这个问题,按乘客级别检查平均年龄。例如:

*sns.boxplot(x='Pclass',
            y='Age',
            data=df,
            palette='winter')*

我们可以看到,在较高的阶层中,较富裕的乘客往往年龄较大,这是有道理的。我们将根据年龄的 Pclass 使用这些平均年龄值进行估算。

*pclass_age_map = {
 **1: 37,
  2: 29,
  3: 24,**
}def replace_age_na(x_df, fill_map):
    **cond=x_df['Age'].isna()
    res=x_df.loc[cond,'Pclass'].map(fill_map)
    x_df.loc[cond,'Age']=res** return x_df*

x_df['Age'].isna()选择 年龄 列,检测缺失值。然后,x_df.loc[cond, 'Pclass']用于有条件地访问 Pclass 值,并调用熊猫[map()](/introduction-to-pandas-apply-applymap-and-map-5d3e044e93ff)将每个值替换为另一个值。最后,x_df.loc[cond, 'Age']=res有条件地用res替换所有缺失的年龄值。

运行以下代码

*res=(
    load_data()
    .pipe(split_name)
    .pipe(substitute_sex)
    **.pipe(replace_age_na, pclass_age_map)**
)
res.head()*

应根据年龄的 Pclass 替换所有缺失的年龄。让我们通过运行res上的热图来检查一下。

*sns.heatmap(**res.isnull()**, 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')*

太好了,成功了!

4.将年龄转换为年龄范围组:≤12 岁、青少年(≤18 岁)、成人(≤60 岁)和老年人(> 60 岁)

让我们创建一个函数create_age_group(),它接受一个数据帧作为输入并返回一个数据帧。

*def create_age_group(x_df):
    bins=[0, 13, 19, 61, sys.maxsize]
    labels=['<12', 'Teen', 'Adult', 'Older']
    **ageGroup=pd.cut(x_df['Age'], bins=bins, labels=labels)**
    x_df['ageGroup']=ageGroup
    return x_df*

pd.cut()用于将年龄转换为年龄范围组。

运行以下代码

*res=(
    load_data()
    .pipe(split_name)
    .pipe(substitute_sex)
    .pipe(replace_age_na, pclass_age_map)
    **.pipe(create_age_group)**
)res.head()*

我们应该得到如下输出:

create_age_group()的结果

好了

感谢阅读。

请在我的 Github 笔记本上查看源代码。

如果你对机器学习的实用方面感兴趣,请继续关注。

最后,这里有两篇你可能感兴趣的相关文章

利用词性分析电影评论

原文:https://towardsdatascience.com/using-part-of-speech-to-analyse-movie-reviews-3a3cfb9986d5?source=collection_archive---------32-----------------------

自然语言处理变得简单

惊讶于 POS 在数据分析中的强大功能。

马库斯·斯皮斯克在 Unsplash 上的照片

当我们谈论自然语言处理(NLP)时,很容易想到它在机器学习中的应用,特别是情感分析。像“这篇评论是正面的还是负面的?”,或者“这条推文到底讽刺不讽刺?”非常常见,所以我们的注意力通常集中在实际方面,比如使用哪种算法,或者如何在令牌中转换单词,等等。但是,当涉及到纯数据分析时,如何使用 NLP 来获得关于我们的数据的见解呢?

顺便说一句,如果你是 NLP 世界的初学者,你可能会喜欢在这里查看这个主题的简介。

在本文中,我们将使用最流行的 NLP 库之一 spacy 来执行文本分析并生成一些见解。包含所有代码和注释的笔记本在这里等着你。

什么是词性?

spacy 最强大的工具之一是它能够使用复杂的统计模型预测一个词在给定上下文中的功能。

词性指的是根据一个词的句法功能对其进行分类,比如它是动词还是名词等等。我们可以看看下面的例子:

来源:https://spacy.io/usage/linguistic-features

POS 标签及其描述的完整列表可在文档中找到。

关于数据

数据来自 IMDB 电影评论数据集,可在 kaggle 上获得。让我们检查第一行:

来源:作者

我们这里有关于评论的标签情绪:

  • 1 =正面评价;
  • 0 =负面评价。

然后我们有评论本身的文本。简单吧?

创建 NLP 模型

为了创建 nlp 对象,我们导入 SpaCy English 模型。简而言之,nlp 对象是 spacy 工作的核心,因为它执行繁重的任务,如将单词转换成记号、词条等。

我们还将导入英语停用词和一个带标点符号的列表。停用词是对我们的分析没有意义的常见词,如“the”和“an”。我们将使用这些对象来过滤我们想要保留的单词。

来源:作者

仅从评论中选择形容词

当人们写评论时,他们通常用词来描述他们如何喜欢或不喜欢某事,这些词被称为 形容词。 因此,使用几行代码,我们将创建一个循环,它将:

  • 迭代每个评论,使用 spacy 删除停用词和标点符号,然后预测并保存形容词到列表中;

来源:作者

然后,我们需要将这个列表转换成一个新列,返回到我们的数据框:

来源:作者

最后,使用一些 python 基础知识,我们可以选择正面和负面评论中最常用的形容词:

来源:作者

而且,当我们谈论数据分析时,为什么不在一些漂亮的图中展示我们的发现呢?

来源:作者

使用词类过滤器,我们发现“好”是正面评论中第一个最常见的形容词,但令人惊讶的是,它是负面评论中第二个最常见的形容词。这是一个有趣的发现,可以进一步研究,如根据情绪使用二元模型来发现“好”之前和之后使用的常用词,以及它为其他分析打开了大门,这些分析可以提供有价值的信息来构建报告和仪表板。

本文的目标是向您展示如何将 POS 用于数据分析,展示我们在学习 NLP 时所学概念的实际应用。我希望你喜欢,请在评论中让我知道你可能会发现的 POS 的其他用途。感谢阅读!

利用 PIL 图像进行图像变换

原文:https://towardsdatascience.com/using-pils-image-for-image-transformation-64a6d7b6e58b?source=collection_archive---------46-----------------------

在 Python 中轻松调整图像大小和连接图像

照片由 Hitesh ChoudharyUnsplash 上拍摄

我最近在做一个项目,涉及操纵图像,以便我们可以在其上运行无监督的机器学习算法。作为一个 Python 爱好者,我决定使用 Python 和它的库来为我操作,而不是走 Photoshop 路线。

在这篇文章中,我强调了我如何使用PIL中的Image子模块来创建我的项目所需的图像。

收集图像

我为这篇文章拍摄了两张图片,都来自 Unsplash。我特别使用了不同尺寸的图像,这样我们就可以了解不同尺寸的图像是如何相互作用的。

图片由 Ivars Utināns 提供

图片来自塞尔吉奥·索萨

请注意,第一幅图像较宽,而第二幅图像高度较长。把它们结合起来我们会很开心。

图片 1:https://unsplash.com/photos/DujBHPRqAKk
图片 2:https://unsplash.com/photos/JSye5r1eAVY

我通常使用 unsplash.com T21,因为它有大量各种各样的高质量图像,可以免费获取。显然,不要忽略对艺术家的适当肯定。是好的伦理!!

安装库

我在这里使用的是 Python 3.6,但是 Python 3.5 和更高版本的所有版本都应该可以正常工作。

pip install Pillow
pip install matplotlib

Pillow是包含我们的子模块Image的 PIL 包。我还决定安装matplotlib,因为它让在 Jupyter 笔记本上显示图像变得更加容易,不管图像的大小如何。例如,当我试图使用Image直接显示图像时,它打开了一个新窗口,图像全部放大。

如果你想在 Jupyter 笔记本中这样做,使用pip install jupyter安装它,启动 Jupyter 服务器并创建一个新的笔记本。

加载库

我们现在将加载已安装的库。我们需要来自PILImage子模块和来自matplotlibpyplot子模块,我们将以名称plt加载它们。你可以给它起任何名字,也可以什么都不用,用plt只是惯例。

from PIL import Image
import matplotlib.pyplot as plt

加载图像

使用Image.open(),我们可以简单地将图像加载到笔记本中(或者 Python 文件,如果你正在使用的话)。我们将在变量img1中保存第一幅图像,在变量img2中保存第二幅图像。

img1 = Image.open("image.jpg")
img2 = Image.open("image2.jpg")

要查看第一幅图像,我们只需使用以下命令:

plt.imshow(img1)

imshow()方法显示 Jupyter 笔记本中的图像,如下图所示:

plt.imshow 的结果(img1)

我们可以重复同样的操作来查看第二个图像,只需替换上面命令中变量的名称。

调整图像大小

使用Image调整图像大小非常非常简单。我们只是使用resize()方法,并为最终调整后的图像定义一个元组(宽度,高度)。在下面的代码中,我将图像 1 的大小调整为 1200x800,然后打印新旧图像的大小,最后显示结果图像。

img1_resized = img1.resize((1200, 800))
print("Original image size: {}".format(img1.size))
print("Resized image size: {}".format(img1_resized.size))
plt.imshow(img1_resized)## Output
# Original image size: (5464, 3640)
# Resized image size: (1200, 800)

已调整大小的图像

查看水平轴和垂直轴,您会看到新图像的大小现在调整为 1200x800。

水平连接

在我的项目中,我必须水平地一个接一个地组合几幅图像。这个想法是让图像具有相同的高度,但宽度增加。一旦我开始寻找可能的解决方案,我就能够编写出有效的代码。然而,与我项目中的正方形图像不同,我很好奇是否可以组合不同大小的图像并生成可用的图像。经过仔细的实验,我找到了一个适用于不同尺寸图像的解决方案。

result_img = Image.new('RGB', (img1.width + img2.width, min(img1.height, img2.height)))
result_img.paste(img1, (0, 0))
result_img.paste(img2, (img1.width, 0))
plt.imshow(result_img)

我首先使用Image.new()创建一个新图像。第一个参数声明我正在创建一个 RGB 图像,第二个参数是最终图像的宽度和高度。显而易见,当两张图片并排放置时,它们的宽度会简单地相加。然而,由于两者的高度不同,使用min()方法会将组合图像缩小到适当的高度,因此没有空白。

然后我粘贴从(0,0)开始的第一张图片和到(img1.width, 0)结束的第二张图片。生成的图像如下:

水平组合两幅图像

如果我们用max()方法替换min()方法,我们将在原本没有像素的地方得到黑色。在上面的代码中,如果我们使用max(),得到的图像应该是:

水平组合两幅图像(使用 max())

垂直连接

就像水平拼接一样,我们也可以垂直组合图像。为此,总高度将是两个图像的高度之和,而宽度将是两个图像中的最小值。

result_img = Image.new('RGB', (min(img1.width, img2.width), img1.height + img2.height))
result_img.paste(img1, (0, 0))
result_img.paste(img2, (0, img1.height))
plt.imshow(result_img)

垂直组合图像

这里,我们将在(0,0)再次粘贴图像 1,但是第二个图像将被放置在(0, img1.height)。同样,我们在这里使用了min()方法来代替max()方法。

保存图像

最后,一旦所有的转换完成,我们可以保存图像。如果我们将图像设为result_img,命令将会是:

result_img.save("new_image.jpg")

这里,新图像将以名称new_image.jpg保存。

本文到此为止。如果你有建议或问题,请在评论中告诉我。

使用预训练的深度卷积神经网络对新冠肺炎 CT 扫描进行二值分类

原文:https://towardsdatascience.com/using-pretrained-deep-convolutional-neural-networks-for-binary-classification-of-covid-19-ct-scans-3a7f7ea8b543?source=collection_archive---------33-----------------------

作者:劳纳克·索德,斯蒂芬·汉弗莱斯博士。

马萨诸塞州阿克顿-博克斯伯勒地区高中高年级学生;国家犹太健康定量成像实验室高中实习生

放射学助理教授;科罗拉多州丹佛市国家犹太健康定量成像实验室主任。

GitHub

领英

介绍

起源于中国武汉的高毒性新冠肺炎病毒导致了全球性的疫情,影响了许多人的生活。截至本文撰写之时,全球已有超过 1160 万人被诊断感染该病毒,超过 53.9 万人死亡。许多人群正在研究不同的方法来诊断新冠肺炎:RT-PCR(逆转录聚合酶链式反应)测试,抗体测试和 CT 扫描。虽然 RT-PCR 是 CDC 推荐的新冠肺炎诊断方法,但测试可能需要两天才能完成。此外,抗体测试只有在患者对病毒产生免疫力后才有用。使用 CT 扫描进行诊断是有希望的,尽管研究表明它对于单一的诊断测试是不可靠的。

然而,最近的研究已经使用人工智能来诊断新冠肺炎感染的肺部图像,以增强放射科医生的分析。在 Trehan⁴和 Markevych⁵撰写的文章中,他们使用 Tensorflow 库从头构建的卷积神经网络分别对 x 射线和 CT 图像进行分类。然而,在这篇文章中,我将使用迁移学习,使用 VGG 和雷斯内特等最先进的模型来分类新冠肺炎阳性和阴性肺部 CT 扫描。此外,我将与 PyTorch 合作。

项目工作流程

数据

我使用了来自大 Challenge⁶新冠肺炎 CT 的开源数据集,这是一组超过 750 个 PNG 的肺部 CT 图像,其中大约一半是新冠肺炎阳性的。

让我们来看一些示例图片。

数据集中的示例图像

你也许能分辨出右边的图像是新冠肺炎阳性的。为什么?因为左叶浑浊的白色区域是磨玻璃样阴影(GGO)的一个例子,这是在肺部 CT 中识别新冠肺炎的关键特征之一。正如你在右边的图片上看到的,没有这种毛玻璃外观的痕迹。注意包含毛玻璃特征的肺部 CT 图像并不总是新冠肺炎阳性;还有其他疾病,如感染性疾病、间质性肺病和急性肺泡疾病,在肺部 CT scans⁷.中也显示 GGO 但是,在这种情况下,这不应该是一个问题,因为这是一个二元分类问题。此外,并不是所有的图像都这么容易识别;例如,让我们看看下面的图像。

你怎么想呢?结果,它是新冠肺炎阳性的,因为两个肺叶都有 GGO。这不是一个更难的例子吗?让我们看看卷积神经网络是否可以更好地对这些图像进行分类。

数据预处理

在开始训练模型之前,我们首先要完成一些预处理步骤。

  1. 读入图像并给它们贴上标签
  2. 调整图像大小
  3. 将数据分成训练集、验证集和测试集
  4. 将图像转换为 PyTorch 张量
  5. 添加图像增强

那我们开始吧。注意,本文中的所有代码都可以在我的 GitHub 新冠肺炎分类库中找到。

目录结构:

丙:。/图像-已处理-新建

|————CT _ COVID

|————CT _ non vid

我们首先导入预处理所需的库。然后,对于每个目录中的每个图像,我们读入具有 RGB 通道的图像,将图像的大小调整为(224,224)并将图像及其相关联的标签附加到列表 training_data。我们必须调整图像的大小,因为我们稍后将使用的迁移学习模型只接受这种形状的图像。然后我们标记图像,1 代表 COVID 阳性,0 代表 COVID 阴性。注意:tqdm 库用于创建一个进度条。

现在,我们必须将数据分为训练集、验证集和测试集,并将它们转换为 PyTorch 张量。

让我们把这段代码分成几个部分。我创建了一个分割大小,它是整个数据的 10%,因此训练集是数据的 80%(大约 600 张图像),测试和验证集各占总数据的 10%(大约 75 张图像)。然后,对于每个集合,我使用列表理解将原始数据分解为图像(X)和标签(y)。然后,我将每个数组转换为 PyTorch 张量,并将其重新整形为格式(图像数量、通道数量、图像宽度、图像高度)。这种形状格式是 PyTorch 模型接受张量数据的方式。最后,我对图像的像素强度进行归一化。PNG 图像的像素强度在 0 到 255 之间;然而,在训练过程之前,它们需要在 0 和 1 之间被标准化。

在 PyTorch 中,数据加载器用于创建成批的训练图像,并对图像应用变换。因此,我们必须将我们的代码包装到一个数据集类中,我们可以将该数据集类与任何相关的转换一起提供给 DataLoader 对象。

init 方法本质上与上面的代码相同,都被格式化以适合数据集类。lengetitem 函数必须被覆盖,以指定如何访问我们的图像。我使用“数据”参数来指定集合是训练、验证还是测试。你可以在 PyTorch 文档中读到更多关于 Dataset 类的内容。

最后,我们必须创建数据加载器对象以及可以应用于图像的转换。

我使用了 Monai ,一个基于 PyTorch 构建的医学成像库,用于图像转换。LoadPNG()、AddChannel()和 ToTensor()转换对于将原始 PNG 图像转换为机器可学习的张量至关重要。对于训练集,我们还使用 ScaleIntensity()来确保图像强度是归一化的;RandRotate()、RandFlip()和 RandZoom()用于几何变换。RandGaussianNoise(),顾名思义,就是给图像添加噪点。这些变换模拟真实世界的 CT 图像,因为它们有时可以以不同的方式变形,并且图像中可能存在噪声。总的来说,这些图像增强技术将使模型更健壮,并可推广到低质量的图像。对于验证和测试集,我只添加了前三个转换,因为我不想修改测试集。

接下来,我实例化了 Dataset 类,并将其传递给 DataLoader 对象。我最初将批处理大小设置为 32,但是我们将在本文后面处理这个值并查看结果。我们可以查看数据集中的一些图像,看看这些图像进入模型后是什么样子。

与预处理前的原始图像相比,图像略有改变。

太好了!我们完成了预处理步骤,并准备使用迁移学习来开发我们的模型。

模型开发

我用迁移学习进行模型开发。我测试了三种不同深度的不同型号的性能:VGG-16⁹、VGG-19⁹和⁰. resnet-34 这三个模型都在 ImageNet16 数据集(1400 万个图像数据集,包含 1000 个不同的类)上进行训练,获得了最先进的精度。

第一步是决定是否要在 GPU 上训练。我有一个 Nvidia GeForce GTX 本地 GPU,所以我打算将该模型分配给 GPU。

注意:如果您的本地设备上没有 CUDA,设置 GPU 可能是一个非常复杂的过程。我强烈推荐你阅读这篇文章,了解一个非常简单的设置过程。它为 Tensorflow 设置了一个 CUDA 环境,但它与 PyTorch 的工作方式相同。

然后,我必须“冻结”模型中的所有权重。如果你想了解 PyTorch 中迁移学习的更多信息,请参考这篇文章。这意味着我使权重不可训练,所以它们不会通过梯度下降来更新。因此,我遍历模型参数并将 requires_grad 设置为 false。

以下是代码:

最后,我创建了一系列可训练层来替换模型中的最终分类层。最初的模型有 1000 个类,但是这是一个二进制分类问题,所以我需要以两个类的输出结束。

我使用了 torch.nn 库中的顺序模型。这个模型的好处是它允许输入一个字典。所以,我可以给每一层标上一个名字,以便跟踪不同的层。我选择使用 4 层,以允许从数据中学习足够的可训练参数。当然,这是一个可以调整到任何程度的超参数。此外,每层中神经元的数量是另一个可变的超参数。通常,你可能会看到 2 的幂被用来表示神经元的数量,所以我坚持使用它。最后,你可能会注意到,我在每一层之间使用了 dropout 正则化。这防止了模型过度拟合训练数据,这将导致验证和测试数据的精确度降低。

转移学习工作流

如前所述,我比较了三种最先进的模型。冻结权重和替换最终分类层的过程对于所有三个模型都是相似的,所以我在这里不打算展示。

现在我们的模型已经准备好进行训练了。

培养

与 Tensorflow 和 Keras 库不同,我们必须用 PyTorch 编写自己的训练循环。但是,这实际上是一个直观的过程,所以我们开始吧。

我将定义一个名为 train 的方法,它接受模型、训练加载器、验证加载器、优化器、损失函数、时期数以及潜在的学习率调度器的参数。该函数将训练模型并返回四个列表:训练准确度、训练损失、验证准确度和验证损失的列表。

该方法是这样开始的:

然后,我们循环遍历历元数(模型通过数据的次数),并初始化变量来跟踪损失和准确性度量(这在本文的后面会很重要)。

然后,我们遍历训练数据加载器中的图像和目标类,找到模型的预测,将模型的预测与实际情况进行比较(计算损失函数),并相应地更新权重。

我还写了一个更小的循环来计算正确预测的数量和总预测,这样我们就可以计算精度了。

接下来,我们将模型设置为验证模式,并计算验证损失和准确性。该过程类似于训练步骤,除了模型不应该在该步骤中学习。这意味着我们不会更新该数据的权重。我创建了一个名为 validation 的辅助函数来计算验证损失和准确性。我没有在这里展示,但是你可以在我的 GitHub 上看到完整的代码。

我们在每个时期之后打印出损失和准确性度量,并将这些度量附加到列表中。您可能想知道为什么我将所有的度量存储在一个列表中。当我们查看损耗和精度曲线时,这将派上用场。

现在我们可以使用该方法来训练我们的模型。

对于这次试验,我使用了二元交叉熵损失和 Adam 优化,因为这些是二元分类任务的标准。我还训练了 30 个时期的模型,因为以前的尝试表明,损失在这个点附近稳定下来。

这是 30 个时期后的输出:

结果

我对 VGG16、VGG19 和 ResNet-34 进行了不同数量的历元、批量大小和学习率调度程序的培训。这些是每个预训练网络收到的最佳模型。

讨论

在我们深入分析模型的性能之前,让我们更仔细地看看循环学习率调度。根据 Smith(最初的循环学习率论文的作者)的说法,这种类型的学习率调度背后的思想是允许学习率在一定范围内循环变化,而不是系统地增加或减少它。

循环学习率可视化

我使用循环学习率调度,因为它比我尝试的其他调度程序更容易调优,并且显示出更好的结果。

好吧,回到模型分析。

VGG16 模型在 30 个时期后具有最高的验证和测试准确性,而 VGG19 模型具有最高的训练准确性。ResNet-34 型号在所有设备中表现最差。VGG16 模型是唯一没有过度拟合的模型,这可能是因为该模型较浅,因此无法拟合如此复杂的函数。因此,较浅的网络通常表现更好,因为数据集非常小。ResNet 通常表现不佳,因为它比其他网络更深,因此可能需要更长的训练时间或更大的批量。

我在训练各种模型时注意到的一件事是,验证/测试的准确性有时高于训练的准确性。这很可能是由于数据量小和验证分割大小。图片总数大约是 750 张,我使用了 0.1 的验证分割大小,这意味着只有大约 75 张图片需要验证。因此,如果在某个时期内有更多的图像被正确分类,验证精度的提高将超过训练精度的提高。这是这个项目的一个限制,因为没有足够的数据来真正比较验证准确性和训练准确性。然而,我们仍然可以区分模型性能,因为验证准确性可以在模型之间进行比较,因为验证集中的图像数量保持不变。

虽然结果显示 VGG16 模型表现最佳,但测试数据集非常小(只有 74 张图像),因此如果不进行额外的分析,还不足以说 VGG19 模型更好。因此,我们将在评估部分查看进一步的分析。

测试数据评估

还记得我说过存储准确性和损失指标很重要吗?现在,我们可以绘制这些值与时期数的关系,并想象它们如何随着训练过程的进行而变化。

VGG16 损耗和精度图

VGG19 损耗和精度图

ResNet-34 损失和精度图

您可能会在图中注意到一个趋势,即验证图比训练图噪声大得多。这是因为验证集只有 74 幅图像,而训练集大约有 550 幅图像。因此,验证集中的几个错误可能会导致损失和准确性比训练集中的几个错误差得多。但总的来说,每种型号的损耗都随着时间的推移而减少。此外,如果您查看所有三个模型的训练曲线,ResNet-34 和 VGG16 看起来似乎都趋于平缓,而 VGG16 模型似乎将继续改善。在未来的工作中,我计划为更多的纪元训练该模型,以观察 VGG16 是否会继续改进。

我们可以继续基于接收器操作特征(ROC)曲线来评估我们的模型,ROC 曲线是假阳性率对真阳性率的曲线图。

VGG16(左)、VGG19(中)和 ResNet-34(右)ROC 曲线

ROC 曲线表明该模型能够很好地区分这两个类别。曲线下面积(AUC)值接近 1 表明很少有假阳性和假阴性。直线的 AUC 为 0.5,表示随机猜测的对照二元分类器。显然,VGG16 是 COVID 阳性和 COVID 阴性 CT 扫描之间的最佳区分器。正如我们对测试准确性的预期,VGG19 模型的 AUC 得分第二好,ResNet 最差。

可分性的另一个评估是混淆矩阵。二元分类器的混淆矩阵在一个易于阅读的矩阵中显示真阳性、真阴性、假阳性和假阴性的数量。

VGG16(左)、VGG19(中)和 ResNet-34(右)混淆矩阵

同样,VGG16 模型具有最好的结果,很少有错误标记的图像。当测试时,它没有假阴性,这真的很重要,因为如果新冠肺炎被错误地诊断为阴性,那么对患者的生存来说可能是一个严重的问题。其他两个模型有一些假阴性,甚至比 VGG16 模型有更多的假阳性。

为了增强我们对假阳性和假阴性的分析,我们可以将其中一些图像可视化。本质上,我们需要创建一个函数来遍历测试集,并将错误标记的数据组织到适当的类别中。这将允许我们看到分类器正在犯什么样的错误。

下面是这个方法的代码。

该函数接受所使用的模型、测试数据、损失函数以及在显示假阳性和假阴性之间的选择。我们首先创建两个列表来分别保存误报和漏报。接下来,正如我们在训练循环中所做的那样,我们遍历测试数据并对数据运行模型。然后,我们编写“if”和“else if”语句来测试分类是假阳性还是假阴性,并将错误标记的图像附加到适当的列表中。最后,根据该函数是否用于显示假阴性和假阳性,我们使用 Matplotlib 中的 figure 和 subplot 函数在一个网格中显示错误标记的数据。

以下是一些贴错标签的图片的例子。

假阴性的例子

误报的例子

正如您在假阴性显示中看到的,很难判断图像是否为 COVID 阳性。同样,假阳性似乎也有一些异常;然而,这种特殊的异常可能不是由于新冠肺炎。为了修复这些错误分类,我们可能会添加更多类似于假阳性和假阴性的图像,以使模型对这些图像更具普遍性。查看错误分类是改进模型开发的重要工具。

我们可以对模型进行的最后一个测试是霍斯默-莱梅休(HL)拟合优度测试。HL 测试用于模型校准:它将数据的多个子组中的观察值(预测值)与预期值(真实值)进行比较,以确定模型与数据的拟合程度。

这是 HL 测试的方程式。

这可能看起来很复杂,但实际上很简单。该测试遍历每个观察值和期望值,取其差值的平方,然后除以期望值。第一个求和符号是对创建的所有不同子组求和,第二个求和符号是对每个观察值和期望值求和。如果您熟悉统计学,您可能会注意到 HL 检验与卡方检验非常相似。

HL 测试的输出是介于 0 和 1 之间的卡方值和 p 值。较低的卡方值表示观察值和期望值之间的相关性较高。在我们的例子中,较低的值意味着我们的预测更准确地符合实际情况。此外,小于 0.05 的 p 值表明模型拟合不佳。远远大于 0.05 的 p 值表明有足够的证据表明模型校准良好,并准确地拟合了数据。想要更深入的了解这个测试背后的统计数据,可以参考这篇文章。

现在我们已经了解了测试工作的基本原理,让我们看看如何在代码中实现它。不幸的是,Python 没有提供实现 HL 测试的便捷方法,所以我使用了 R。

首先,我导入了 ResourceSelection 库,它有一个实现 Hosmer-Lemeshow 测试的方法。然后,我创建了两个向量,用于地面真实分类和二进制输出预测。我从我创建的测试方法中获得这些值,以获得测试精度(同样,你可以在我的 GitHub 上找到这个)。最后,我对这两组数据运行了 hoslem.test 函数。请注意,参数“g”表示我将数据分成的子组的数量。

下面是这段代码的输出。

我们可以用它们的二进制输出概率为我的另外两个模型写同样的代码。我在下表中组织了所有三个模型的结果。

VGG16 分类器具有最低的卡方值和最接近 1 的 p 值,这意味着它是数据的最佳拟合模型。因此,VGG19 是第二好的校准模型,最不适合的模型是 ResNet-34。尽管 ResNet-34 在 HL 测试中表现最差,但这并不意味着该模型校准不佳。事实上,高 p 值表明该模型很好地拟合了数据,只是没有其他两个模型拟合得好。这是 HL 测试相对于其他指标的优势。它允许我们判断模型中的拟合缺失是否具有统计学意义。我们知道,高 p 值和低卡方值表明所有三个模型都相对符合数据。

限制

尽管我们能够在训练我们的各种模型时得到好的结果,但是我们确实有一些需要注意的限制。首先,我们使用 PNG 图像,而不是医学成像中使用的标准 DICOM 格式。由于新冠肺炎疫情是最近的,DICOM 格式的原始 CT 扫描很难找到,获得使用患者数据的许可就更难了。使用 DICOM 图像的好处是它们比 PNG 图像更标准化,质量更高。我们使用的数据集包含来自在线文章、网站和博客的图像,因此质量不如原始 DICOM 图像。

我们工作的另一个限制是数据集中图像的数量。正如我之前提到的,拥有更少的数据可能会导致我们的验证准确性略有偏差。这就是为什么我们看到有时验证精度高于训练精度。

最后一个限制是,在我们的模型中使用之前,我们必须对图像进行下采样。我们使用的最新模型需要(224,224,3)的输入图像,而医学图像通常具有更高的分辨率,例如(512,512)和(1024,1024)。这种信息的丢失会导致关键的细节被遗漏,并阻碍我们模型的准确性。解决这个问题的一个方法是创建我们自己的模型,该模型接收更高分辨率的图像。这样,我们就不必对图像进行下采样。

结论

在本文中,我们看到了如何使用 Dataset 类和 Dataloader 对象对 CT 扫描进行分类预处理。然后,我们使用迁移学习在 CT 图像上微调 VGG16、VGG19 和 ResNet-34 预训练模型。然后,我们根据 ROC 曲线、混淆矩阵和 Hosmer-Lemeshow 拟合优度测试进一步评估每个模型。最后,我们讨论了项目中的一些限制,这些限制可以在未来的工作中得到改进。例如,未来的工作可能涉及到使用 DICOM 图像,如果它们是开源的。

现在我们已经在 ROC 曲线、混淆矩阵和 Hosmer-Lemeshow 测试上评估了所有三个模型,我们可以说 VGG16 模型在这组新冠肺炎 CT 扫描上表现最好。

  1. 世卫组织。冠状病毒疾病(新冠肺炎)。2020.https://www . who . int/docs/default-source/corona virus/situation-reports/2020 06 11-新冠肺炎-sitrep-143.pdf?sfvrsn=2adbe568_4
  2. 在荷兰,CT 与 RT-PCR 诊断新冠肺炎相关:一项前瞻性研究。2020.https://www . medrxiv . org/content/10.1101/2020 . 04 . 22 . 20070441 v1 . full . pdf
  3. 梅等人工智能实现新冠肺炎患者的快速诊断。2020.https://www.nature.com/articles/s41591-020-0931-3
  4. 崔汉,达克什。使用深度学习检测新冠肺炎。2020.https://towards data science . com/detecting-新冠肺炎-使用-深度学习-262956b6f981
  5. 马克维奇,伊霍尔。卷积神经网络能通过肺部 CT 扫描诊断新冠肺炎吗?。2020.https://towards data science . com/can-a-卷积-神经网络-诊断-来自肺部的新冠肺炎-CT-扫描-4294e29b72b
  6. 新冠肺炎 CT 大挑战。2020.https://covid-CT . grand-challenge . org/CT-新冠肺炎诊断/
  7. Knipe 等人,毛玻璃乳浊化。2019.https://radio paedia . org/articles/磨砂玻璃-乳浊化-3?lang=us
  8. 哈里森.金斯利。“介绍和预处理—使用卷积神经网络识别狗与猫 p . 1”YouTube,send ex 上传,2019 年 2 月 22 日,https://www.youtube.com/watch?v=gT4F3HGYXf4
  9. 用于大规模图像识别的非常深的卷积网络。2014.https://arxiv.org/abs/1409.1556
  10. 何等。图像识别的深度残差学习。2015.https://arxiv.org/abs/1512.03385
  11. 史密斯。训练神经网络的循环学习率。2017.https://arxiv.org/pdf/1506.01186.pdf
  12. 罗斯布鲁克。Keras 和深度学习的循环学习率。2019.https://www . pyimagesearch . com/2019/07/29/cyclic-learning-rates-with-keras-and-deep-learning/

使用 Pylint 编写干净的 Python 代码

原文:https://towardsdatascience.com/using-pylint-to-write-clean-python-code-660eff40ed8?source=collection_archive---------27-----------------------

因为代码质量很重要!

Pylint:启动您 Python 代码!

代码翻译器基本上是一个检查你的代码并给出反馈的程序。linter 可以告诉你程序中的问题,以及解决问题的方法。您可以随时运行它,以确保您的代码符合标准质量。

Linters 查看代码的各个方面并检测 lints:

  1. 逻辑 Lint:讲述代码错误、危险的代码模式
  2. 统计 Lint:查看格式问题

有许多 Python linters,如 Flake8、Pylint、Pylama 等。在本文中,我将讨论 Pylint,因为它处理逻辑和统计 lint。

安装 Pylint

如果您想从终端安装 Pylint,这是一个单行命令。

pip install pylint

我已经在我的代码编辑器 PyCharm 中安装了 Pylint。为此,你可以进入首选项>插件并搜索“Pylint”插件。从窗口安装并下载它,然后重新启动 IDE。

然后您可以为插件设置 Exec 路径,这样每当用户需要检查代码时,Pycharm 就可以执行 Pylint。为此,请转到设置/首选项,单击其他设置,然后单击 Pylint。在那里输入 PATH 环境变量,就可以开始了。

现在,你完成了!现在,每次运行代码时,您都可以使用 Pylint 来寻找重构、约定和其他小警告信号。

对于我的代码,你可以看到我有许多显示约定问题。在左下角,您可能会看到 4 个符号(红色、黄色、蓝色、红色),这些符号告诉用户代码中的“错误”、“警告”、“显示约定”和“重构”问题。

我有许多蓝色的,这意味着我的代码中有许多显示约定问题。您可以改进代码,然后再次打开 linter 来查看您的代码质量。

给航站楼的人

您可以简单地使用下面的命令来检查代码。

pylint example.py

Pylint 是一个令人惊叹的 linter,您的代码库中必须有它。因为代码质量很重要!

关于反馈和讨论,请通过我的 Linkedin 找到我!

用 Python 和蒙特卡罗来预测我的学生贷款还款

原文:https://towardsdatascience.com/using-python-and-monte-carlo-to-predict-my-student-loan-repayment-5866dc198b?source=collection_archive---------40-----------------------

管理个人财务的数据科学方法

图片来源:乔希·阿佩尔,UnsplashPixabay

我开始对我的学生贷款报表上的数字没有意义感到沮丧,并对为什么你不应该在有能力的情况下尽快还清贷款发表了猜测性的意见。我想衡量我还清全部贷款的可能性,并练习我的数据科学技能,所以我用 Python 编写的蒙特卡洛模拟了我的收入。在这里,我给出了一个调查结果的走查和评估。

我的 GitHub 上有完整的代码。

如果时间不够,请跳到方法结果

介绍

在英国,学生可以向英国学生资助局申请大学贷款来支付学费和生活费,该机构提供由学生贷款公司担保的贷款。一旦你以毕业生的身份进入就业市场,你就开始按月偿还贷款,作为你税收减免的一部分。重要的是,如果你在 30 年内没有还清贷款和利息,你的贷款就会被取消。它们也很可观——在许多情况下,毕业一年后大约有 4.2 万人。

毕业生毕业后从学生贷款公司收到的声明摘要示例。(图片由作者提供)。

基本原理

  • 我发现有关还款的文件晦涩难懂。这很令人困惑,网上和我的圈子里没有人能给我明确的答案。
  • 围绕还款的相互矛盾的观点“它来自我的税,所以我甚至没有注意到它”“大多数学生不会还清它,它会被取消,所以提前支付额外的钱可能是浪费你的钱” —但这概括了“大多数学生”,它跨越了科目、所上大学和行业之间的收入(因此摊销)的巨大差异。
  • 我想避开噪音,捕捉利息增长和每月纳税额之间的动态变化,量化我自己还清的概率。

问题

1.考虑到我可能的工资增长,我有多大可能在 30 年的付款窗口内还清我的学生贷款?
2。如果你设法存钱,或遇到一笔钱(如遗产),你应该以自愿付款的形式偿还部分或全部贷款吗?

事实

  • 自 2012 年以来,贷款的学生都是“计划 2”贷款,他们被分成几期,你在整个学位期间都会收到。
  • 截至本文发表之日,当毕业生月薪超过 2214 英镑或年薪超过 26568 英镑(税前)时,他们才开始偿还贷款。
  • 当你的雇主给你发工资时(在英国,通常是每月的最后一个工作日),贷款还款会从你的工资中扣除——如果你的收入超过门槛,这些还款应在毕业后的 4 月份到期。
  • 你从第一笔贷款的那一天开始计息(即在你进入大学后不久)。
  • 利息每天都在增加。
  • 计划 2 贷款被注销 30 年后的 4 月,你是第一次到期偿还,如果你还在偿还。

学生贷款不同于其他贷款

  • 你偿还学生贷款的金额是按照你收入高于某个阈值的百分比来计算的,因此它更像是一种税,而不是定期的贷款偿还。
  • 利率是可变的,而不是固定的——它们是按照通货膨胀的 RPI(零售价格指数)衡量标准加上 3%计算的,因此近年来利率一直高达 6.6%。
  • 每个学年(9 月 1 日)的利率都会发生变化,使用截至上一年 3 月的财年的 RPI 通货膨胀率(即 2019 年 3 月的 RPI 用于 2020 年 9 月 1 日设定的利率)。

方法

  • 在 Python 中构建一个模拟器,通过 30 年的贷款支付,贷款同时每天产生利息。
  • 使用蒙特卡洛模拟许多不同的工资轨迹(更多信息见下文)。

导入 Python 库

导入相关库

构建函数来计算每月从工资中扣除的学生财务费用

找出我在读大学期间收到的每期贷款的利率

  1. 使用一个简单的 web scraper(beautiful souplibrary)从政府网站获取过去学年的利率,并将它们存储在一个查找字典中。
  2. 构建一个函数,它可以取我学位期间的任何一天,并从字典中找到在该时间点支付的分期付款的利率。

网络刮刀,以建立一个利率和附带的检索功能查找字典。

毕业后计算欠款

为了计算我大学毕业后欠了多少钱,我模拟了整个大学期间每天的利息,加上来自英国学生资助局的任何新分期付款,使用的是熊猫的付款数据框架(从我的纸质对账单数字化而来)。

毕业后计算利息和贷款总额的函数。

寿命模拟

使用上述函数,可以计算就业开始时学生金融贷款的欠款额。毕业后的四月,你开始以每月纳税的方式偿还贷款。在这个模拟中,贷款的利息每天都在增长,在每月的最后一个工作日(b 月末)你的还款从你的工资中扣除,就像现实中一样。

函数来模拟整个职业生涯的终身收入和贷款的价值,因为你每个月支付它。

进入蒙特卡洛

蒙特卡罗方法是一类依靠重复随机采样来获得数值结果的算法——根据大数定律,可以通过取独立样本的样本均值来逼近随机变量的期望值。用简单的英语来说:“虽然我们不知道正确的答案或策略是什么,但我们可以模拟许多尝试,平均结果将接近正确的答案”。蒙特卡罗可用于许多带有概率元素或“风险”的问题,如赌博,或金融中的股票市场和投资组合表现。这里有一个很好的例子

模型—假设&考虑事项

由于我在整个工作生涯中支付的学生贷款金额(通过每月付款)是基于我在 t 时刻的收入,并且我不知道我会赚多少,我可以使用蒙特卡洛模拟我的职业收入。在模拟中:

  • 起薪(29,000 英镑)是毕业生起薪的中位数,基于毕业生就业计划——由学生雇主协会报告。
  • 我把贷款的 30 年支付期划分为职业生涯的不同工资段。
  • 我选择了 6 个独立的乐队,每个乐队 5 年长(但这可以是不同的)。
  • 每个工资级别代表时间 t 时的工资增长——该增长来自以平均值为中心的高斯分布,该平均值是我在时间 t-1 时总工资的百分比。
  • 随着模拟在我的职业生涯中的深入,这个百分比会降低,也就是说,大部分工资增长发生在最初几年(高百分比增长),然后会放缓(低百分比增长)。如下表(0,1) = 37.49%加薪。
  • 我按照这些薪带模拟职业生涯 10 万次。

蒙特卡洛模拟中使用的加薪数据框架。100,000 次 6 个薪带的重复——每列代表一个终生薪资轨迹。

  • 贷款利率定为 5.5% — RPI 为 2.5%加 3% ( 政府法)。为了说明这一点,我使用了英国历史 RPI 值的 5 年移动平均值,这是我从国家统计局(ONS) 下载的。虽然预测的 RPI 估计值可用,但当前的新冠肺炎危机及其经济影响可能会影响这些预测,因此我采用了 COVID19 之前的值,该值近年来一直保持在 2.5%左右。

具有 5 年滚动平均值的英国历史 RPI,用于通知模拟的利率。

  • 我计算了 30 年支付期后所有工资轨迹的平均贷款价值和累计还款总额(终身供款)。

翻译:

作为一名毕业生,大部分工资增长发生在最初几年(高增长%),然后趋于平稳(低增长%)——见财政研究所令人难以置信的彻底的新报告 本科学位对终生收入的影响

5 年内加薪 x%是不确定的——它可能略低于或高于 x%,或者你可能会有一系列或多或少的加薪(这些加薪的总和体现在 5 年的跳跃中)。蒙特卡洛通过从每个级别的正态分布中提取许多工资值(k = 100,000)来帮助缓解这一问题。这些平均值为 x%,但可以小于或大于 x%。我模拟了所有这些轨迹和我贷款的月还款额,并计算出在多少次模拟中我还清了全部贷款。

结果

50 贷款价值轨迹(上图)根据其对应的蒙特卡洛模拟工资轨迹。注意:只有 50 个模拟在这里运行,用于可解释的可视化。实际结果基于 1e5 模拟。

正如你在上面的图中看到的,只有一小部分贷款是通过每月扣款的方式还清的。这些贷款可以被识别为穿过零线(用红色标记)的轨迹,并且对应于最大的工资。多年来最低的工资导致贷款价值持续增长。

对于 100,000 次模拟,30 年付款期后:

  • 平均贷款价值为: 60,600.74
  • 只有在 3.54% 的工资轨迹中,贷款才完全还清,即越过了图中的红色零线(3,543/100,000)
  • 平均已付贷款总额为:99691.01

评估—你应该做什么?!

免责声明:我不是财务顾问——这里的任何想法都纯粹是基于一系列假设的模拟假设。这些只是我对潜在有效策略的想法,作为概念性练习的一部分。如果你对自己的个人财务状况不确定,可以向有资质的理财顾问寻求建议。

注意事项

当然,这个模拟包含几个警告:

  1. 工资级别:我假设 6 x 5 年的工资级别——不能保证毕业生的收入会遵循这种格式。在英国,没有关于特定领域的常规工资增长的足够数据,因为工资增长和晋升受许多变量的影响,包括学术资格(例如,理学学士与理学硕士和博士)、领域、公司和个人本身的表现。虽然有关于每个学科毕业生平均收入的数据,但这些数据并没有涵盖拥有特定学位的毕业生可以选择的许多路线/领域。在与我网络中的高级角色交谈后,我确定,对于 STEM 毕业生来说,平均而言,大约 5 年的加薪频率和所选的百分比接近现实。
  2. 利率:该模拟假设最近的 RPI 值在未来几年保持不变,但是由于 RPI 的变化,贷款的实际利率每年都会发生变化,这将影响贷款的复利速度。
  3. 起薪:我使用的是基于毕业生计划的平均起薪,但这并不能代表所有毕业生的工资——这影响了还款。
  4. 其他薪酬:模拟仅考虑基本工资,不考虑奖金、公司股份或其他形式的薪酬。

那么这里的结论是什么呢?关于偿还学生贷款,你应该采取什么策略?

图片来源:DaveRamsey.com

首先,应该强调的是,债务偿还是算术和心理学的结合,因为作为复杂的生物,我们关于债务的情感福利受到多种因素的影响。如果你有多笔债务要偿还,有不同的债务合并策略,例如滚雪球法或雪崩法

其次,从纯粹的数学意义上来说,支付学生贷款最低金额的最佳策略是在整个 30 年的还款期内保持低于还款门槛的工资水平,从而免费获得学位,因为你从未为此支付一分钱。然而,这只是在孤立于其他因素考虑贷款的情况下,显然不能反映一个典型毕业生的真实生活抱负。考虑到这一点,我考虑的策略是假定职业发展是渐进的,包括偿还你的贷款。

孤立贷款

实际上,在孤立的情况下,是的,你应该尝试用你遇到的任何额外的钱来偿还,因为结果显示,平均而言,在你的工作生涯中,你将支付超过两倍的贷款(99691.01/42000 = 2.37)。

至关重要的是,在这个问题中影响分期偿还的主要因素是利息和每月还款之间的动态变化。如果某个月的贷款利息是 20 英镑,你需要在月底支付比 20 英镑更多的还款来减少本金——这是基本的数学原理。

关键因素是你的工资增长速度,因为复合增长的力量。你的还款需要扣除利息,然后开始减少本金。但是,如果你的工资意味着你不能在早期设法克服利息,即使你的工资在以后的生活中增长到一个可观的数额,贷款将增长到一个需要不成比例的更高的工资来征服以后的月利息的数额,由于复合增长的非线性,这将更大。因此,要想通过每月纳税成功还清贷款,工资必须在最初几年增长足够快,这样还款才能很快超过利息,债务就不会“允许”增长。另一种选择是,如果你的工资太低,无法实现这一点,你可以每月自愿还款,以弥补差额。

现实——金钱的时间价值

当然,在现实中,你的贷款偿还不是孤立的其他因素。这是个多维度的问题!归根结底,你还可以将任何额外的钱投资于其他长期项目,因为金钱的时间价值有机会成本。金钱的时间价值是这样一个概念,即同样数量的金钱由于其潜在的收入和通货膨胀,现在比以后更有价值。

图片来源:中等

如果你遇到一笔钱,使你有能力偿还贷款(无论是从储蓄账户,遗产等。),你可以投资它,让它成长,利用复合增长,而不只是成为它的受害者。

事后分析

我做了一个事后分析来展示这一点。假设你没有还清贷款,而是在一项投资中采用了长期持有策略,其平均回报高于你通过每月工资扣除支付的贷款金额——要么一次性支付相当于贷款价值的金额(42,000 英镑),要么——因为这种情况对典型的毕业生来说非常罕见——初始投资 1000 英镑,每月储蓄 200 英镑。**

两种投资方案的月平均累积还贷额为 1000 MC 模拟工资。

3 种情况:

  1. 你遇到 4.2 万,马上还清贷款。每月还款(蓝线)将花费你大约。你一生中有 10 万英镑,而你一直在逃避。10 万—4.2 万= 5.8 万“利润”。
  2. 你遇到 42,000 英镑,以 5.0%的回报率(通胀后)进行投资,让它继续增长 30 年(橙色线)。30 年后,这大约值 187,000 英镑。18.7 万— 10 万= 8.7 万利润。
  3. 你投资 1000 英镑,用你每月存下来的钱(例如 200 英镑)来补充每月的投资(绿线)。30 年后,这大约值 17 万英镑。17 万–10 万= 7 万利润。

正如这些结果所显示的,即使模拟毕业生没有一大笔可用的钱,而是用更少的钱来投资他们每月的储蓄,他们仍然可以比完全还清贷款获得更大的利润。在大多数情况下,毕业生不太可能遇到这种一次付清的情况,试图在支付生活费用后存钱,然后支付学生贷款可能会让你永远追逐自己的尾巴,因为贷款利息同时复合超过你可以储蓄的部分——你最好投资它。

机会成本

这种机会成本适用于其他投资,如购买一栋房子并利用其升值,甚至适用于对自己的投资,如课程、研讨会和会议,这些可以发展你的技能并扩大你的人脉。虽然这很难用金钱来衡量,但它将产生更多的机会,并可能带来更大的利润。

结论

这不是一个非常明确的结论——有很多“如果”:

  • 如果贷款的利息低得多,很明显,只需通过每月扣款来支付,而不用担心额外的投资,因为你不太可能多付。
  • 如果你遇到一笔接近你学生债务价值的钱或者如果你每个月都存钱,投资可能会更有利可图,因为偿还贷款会产生巨大的机会成本。作为一名年轻的毕业生,你处于有利地位——你最初几年投入的时间/精力/金钱会影响你的职业生涯,而未来几年的延迟投资会大大降低你以后的投资回报率。
  • 如果你有钱,但不投资,那么你最好用这笔钱偿还贷款,否则你可以通过每月还款支付几倍,加上这些流动资金每年都会因通货膨胀而贬值。

这些结果无疑会在不久的将来影响我的个人理财决策。

同样令人欣慰的是,上面提到的报告发现,即使算上税收和学生贷款偿还,英国毕业生平均仍比获得学位多至少 10 万英镑,因为他们获得了额外的净实得工资。

最后的想法

这个项目确实让我明白了“不要全额还清贷款,因为贷款在之后的会被取消”的想法是错误的,因为它缺乏详细说明——它完全取决于工资,因为这决定了你是否会为你的学位支付过低或过高的费用。如果终身工资足够低,那么你可以通过每月还款来支付学位费用,在这种情况下,你不应该支付额外的费用或担心投资。然而,这些模拟表明,如果你最终收入在平均(最密集)区域(见结果中的工资阶梯图),那么你应该投资任何额外的钱,并支付最低还贷额。这是因为你以后需要投资来平衡你学位的超额支付。当然,这些结论是假设性的,是从预测利率和模拟工资中得出的,可能会有所不同。

除了让我对学生贷款系统有了更多的了解之外,这次练习也突出了一些重要的东西。我们没有足够的公开可用和可消费的数据来跟踪不同领域或职业的毕业生一生的收入。提到的报告分析了来自特定群体的毕业生和非毕业生的纵向数据,并于最近发表——像这样的研究需要提供给未来的本科生并易于理解。英国的大学本科课程现在每年的费用高达 9250 英镑,正如我们所看到的,你可能要支付数倍于此的费用。我们需要特定大学的特定课程毕业生的收入数据,这样候选人就可以根据他们将承担的巨额贷款,自己判断他们的课程和大学的投资回报率,而不是简单地在开放日被大学弄得眼花缭乱。

希望这篇文章已经很有见地了!下一个项目…

数据科学项目的一些有用链接:

**** [## 开启 2020 年数据科学学习之路的顶级资源

一步一步的指南,从初学者到中级,50+资源,使 2020 年成为你有意义的数据年…

medium.com](https://medium.com/omdena/top-resources-to-kick-off-your-2020-data-science-learning-path-5630470a801b) [## 教程:使用 BeautifulSoup 的 Python Web 抓取

在执行数据科学任务时,通常希望使用在互联网上找到的数据。你通常可以…

www.dataquest.io](https://www.dataquest.io/blog/web-scraping-tutorial-python/) [## Python 编程教程

欢迎使用 python 进行蒙特卡罗模拟实验。在我们开始之前,我们应该建立一个蒙特卡洛…

pythonprogramming.net](https://pythonprogramming.net/monte-carlo-simulator-python/)****

使用 Python 和 Robinhood 构建多头交易机器人

原文:https://towardsdatascience.com/using-python-and-robinhood-to-build-a-long-call-trading-bot-661b1f6d75ae?source=collection_archive---------16-----------------------

我过去一个月(一月到二月)的实际收获。

这是另一个我正在尝试自动化的交易策略。

所以对于那些不知道的人,我真的在努力了解被动收入的想法。在过去的三个月左右,我一直在大量研究交易。随着我越来越深入这个领域,越来越理解其中的动态,我想看看我是否能把这个领域的知识翻译成 Python。因此,让我们从理解什么是长时间通话开始。

什么是长途电话?

看涨期权是期权交易中的一种合约形式。它给了你以某一价格买入股票的“选择权”(执行价格 A)。从看涨期权中赚钱的方法是,如果你相信某只股票的价格会上涨。此外,如果你相信一只股票会持续上涨,那么它就有无限的盈利潜力。

艺术作品小敏

我们需要什么

对于那些不了解我的人,我已经写了两篇文章,这两篇文章已经在第一步帮助了我们。所以我们需要使用 Python 连接到 Robinhood。创建将为我们订购选项的代码。在所有的初始设置之后,实际的长期通话策略非常简单。

长通话的设置

  • 以执行价格 A 买入看涨期权
  • 我们希望股价等于或高于股价 A

编写代码

所以让我们从最后开始。要执行期权订单,我们需要从[robin_stocks](https://robin-stocks.readthedocs.io/en/latest/index.html)开始使用:

robin_stocks.orders.order_buy_option_limit(*price*, *symbol*, *quantity*, *expirationDate*, *strike*, *optionType='both'*, *timeInForce='gfd'*)

我们只需要期权合约的价格。我们将要使用的股票的符号。我们想要购买的合同数量。上述合同的到期日。股票的执行价格。期权类型设置为看涨期权。

对于这个例子,让我们使用特斯拉购买我们的订单。我通常通过 Robinhood 应用程序购买一个期权,其执行价格与股价相当。所以我们来抓一下特斯拉目前的股价。

current_price = r.stocks.get_latest_price(‘TSLA’)[0]

一旦我们有了这个价格,我们就可以抓住高于这个价格的看涨期权,选择最接近这个价格的期权。但在此之前,我们还需要考虑产品的有效期。我喜欢购买 30-45 天后的长期看涨期权。我已经创建了代码,将检查今天的日期,并找到 30-45 的日期。然后我选择最远的日期。一旦我们有了成交价格和截止日期,我们就可以把它插回到r.options.find_options_for_stock_by_expiration_and_strike(symbol, expirationDate, strike_a, optionType=’call’, info=None).中,现在它将搜索 30-45 天内最长的看涨期权,并为我们提供一个订单价格。对于订单价格,我们可以从最高卖价到最低卖价中选择一个数字。我只是倾向于选择low_fill_rate_buy_price它通常会立即执行我的订单。但是你可以根据自己的交易偏好随意摆弄这些数字。

我们现在可以使用robin_stocks.orders.order_buy_option_limit(*price*, *symbol*, *quantity*, *expirationDate*, *strike*, *optionType='call'*, *timeInForce='gfd'*)来执行我们的订单了

我们可以填写所有变量,订单现在已经发送过来了。这基本上是一个长期看涨期权,我知道一些投资者喜欢,但低于执行价格,但对我来说溢价太贵了。一切取决于你的交易策略。

我其他一些涉及 Python 和 Robinhood 的文章:

[## 使用 Python 和 Robinhood 创建一个简单的低买高卖交易机器人

所以我最近一直在折腾 Robinhood,一直在努力理解股票。我不是财务顾问或…

towardsdatascience.com](/using-python-and-robinhood-to-create-a-simple-buy-low-sell-high-trading-bot-13f94fe93960) [## 使用 Python 和 Robinhood 构建一个钢铁秃鹰期权交易机器人

所以我最近发现了期权交易的潜在收益。

towardsdatascience.com](/using-python-and-robinhood-to-build-an-iron-condor-options-trading-bot-4a16e29649b0)

以上代码可以在这里找到,请随时关注我的交易之旅这里

我也有家教和职业指导。

如果你们有任何问题、意见或担忧,不要忘记在 LinkedIn 上与我联系!

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

使用 Python 和 Robinhood 构建一个钢铁秃鹰期权交易机器人

原文:https://towardsdatascience.com/using-python-and-robinhood-to-build-an-iron-condor-options-trading-bot-4a16e29649b0?source=collection_archive---------1-----------------------

让·维拉在 Unsplash 上拍摄的照片

所以我最近发现了期权交易的潜在收益。我的思维非常技术性,注意到交易策略只不过是条件语句。所以我决定创建一个简单的铁鹰交易策略。

根据 OptionAlpha 的说法,铁鹰期权交易策略是与期权一起使用的利润最高、风险最低的交易策略。我将使用 Python 和 Jupyter Notebook 在 Robinhood 下这些期权订单。

让我们首先试着理解什么是铁鹰战略。对于那些熟悉期权的人来说,它们基本上是四种期权合约。有铁秃鹰,可以要求所有的呼吁,所有的 put 或基本的铁秃鹰。在这个例子中,我们将使用基础。要求同时做多卖出、做空卖出、做空买入和做多买入。执行这一策略的最佳时机是,当我们知道我们订购的股票在特定的时间框架内上下波动最小时。

图片来自 Options Bro—https://www . Options Bro . com/iron-condor-option-strategy-example/

我认识的大多数投资者都喜欢在到期后的 30 到 45 天内操作这个游戏。这就是时间衰减的好处。他们还在指数期权而不是个股上玩这种游戏,因为指数没有那么不稳定。但是我们可以在其他时间更深入地研究逻辑,让我们专注于创建可以工作的代码。

我们需要什么

对于那些不了解我的人,我已经写了两篇文章,这两篇文章已经在第一步帮助了我们。所以我们需要使用 Python 连接到 Robinhood。创建将为我们订购选项的代码。在所有的初始设置之后,实际的铁秃鹰战略非常简单。

铁鹰的设定

  • 我们希望股票处于执行价 A(低于股价)和 B(高于股价)之间
  • 买入低于执行价的看跌期权
  • 以执行价格卖出看跌期权
  • 卖出看涨期权执行价格 B
  • 买入高于执行价 B 的看涨期权
  • 所有这些合同都将有相同的到期日

编写代码

首先,让我们找一只股票来玩。我继续做了一些研究,并将在我们的代码中使用 Fitbit。Fitbit 在 11 月份左右有所上涨,但我相信它未来将横向移动。使我们可以很容易地看到我们可以订购哪些选项。我们只需要输入股票代码。

它现在将输出如下所示的数据帧:

因此,我们希望从这里开始关注的主要栏目是expiration_date,让我们从创建代码开始,它将找到从现在起 30 到 45 天的到期日期。然后,我们将使用这些日期来消除现有的期权订单。我们现在将有一个列表,将是我们的铁秃鹰完美的可用选项。

接下来的部分,我获取当前的股票价格,并把它附加到一个列表中,这个列表是我从执行价格列中得到的。然后,我对这些值进行排序,以便使用指数值来选择每个订单所需的执行价格。然后,在到期日期中,我将选择从今天起 30–45 天内最晚的一天。

接下来,我只需下 4 个不同的期权订单,并填入所有必要的信息。在所有这些之后,我们已经成功地创建了执行 iron condor 订单的 Python 代码!

我其他一些涉及 Python 和 Robinhood 的文章:

[## 使用 Python 和 Robinhood 创建一个简单的低买高卖交易机器人

所以我最近一直在折腾 Robinhood,一直在努力理解股票。我不是财务顾问或…

towardsdatascience.com](/using-python-and-robinhood-to-create-a-simple-buy-low-sell-high-trading-bot-13f94fe93960)

以上代码可以在这里找到,请随时关注我的交易之旅这里

我在这里也有家教和职业指导。

如果你们有任何问题、评论或顾虑,请不要忘记通过 LinkedIn与我联系!

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

使用 Python 和 Selenium 从街道地址获取坐标

原文:https://towardsdatascience.com/using-python-and-selenium-to-get-coordinates-from-street-addresses-62706b6ac250?source=collection_archive---------4-----------------------

澳洲墨尔本市(www.pxhere.com)

这是一篇分步介绍如何使用 PythonSelenium 从基于街道地址的谷歌地图中抓取坐标数据(纬度和经度值)的文章。

在这个例子中,我将使用一个包含所有澳大利亚慈善机构和非营利组织街道地址的官方数据集。最后,我将继续使用lyum,绘制墨尔本市的所有慈善机构和非营利机构的地图,以展示如何使用新获得的坐标数据。

要继续学习,您可以从我的 GitHub 库下载 Jupyter 笔记本。

软件包和设置:

Selenium 主要用于自动化 web 应用程序测试,但也可以用于从网站上抓取数据或在浏览器上执行重复性任务(www.rawpixel.com)

我们将需要 python 包来执行数据抓取如果还没有,可以使用pip:**pip install selenium**安装****

我们还需要一个网络驱动来与浏览器互动,所以你必须到这里下载到你的机器上(确保它与你当前的 Chrome 版本兼容)。****

现在让我们开始导入:

**from selenium import webdriver**

让我们也得到 tqdm ,一个必不可少的 progressbar python 包。估计你的代码的 web 抓取部分将花费多少时间是非常有用的(我使用 tqdm_notebook 是因为我在 Jupyter 笔记本上工作) :

**from tqdm import tqdm_notebook as tqdmn**

除此之外,我们还需要 熊猫 来读取和操作数据集:

**import pandas as pd**

最后,我们将得到来绘制地图上的坐标数据(可以使用pip install folium来安装)😗*******

****import folium****

我将在一个 Jupyter 笔记本上工作(你可以在这里下载)。如果您在 python IDE 中工作,所有这些步骤仍然适用。

数据集:ACNC 慈善和非营利注册

澳大利亚慈善和非营利委员会(ACNC)是澳大利亚慈善机构的国家监管机构

在涵盖过去 10 年的第 10 版 CAF 世界捐赠指数中,澳大利亚在全球排名第四。通过访问 ACNC 的网站,你会很快注意到他们让研究人员可以很容易地获取所有关于澳大利亚慈善机构和非营利组织的数据。

我们将使用的数据集可以在这里下载(以及一个解释变量的有用的用户注释文档):

该数据集展示了澳大利亚慈善机构和非营利机构的许多有趣方面:唯一标识符、法定名称、地址(用于通信)、注册日期、规模、目的、受益人等(阅读用户注释了解更多信息)。

这里我们主要感兴趣的是街道地址特征,它跨越了几列:地址 _ 行 _1地址 _ 行 _2地址 _ 行 _3城镇 _ 城市邮政编码国家。此外,为了简单起见,我们将只调查墨尔本市的慈善机构和非营利组织。****

数据清理和准备:

自由峰的 rawpixel.com/宏矢量

首先,让我们来看看之前从 ACNC 网站下载的关于熊猫的数据集:

请确保提供 Excel 文件在您的计算机上的路径。参数keep_default_na被设置为False,所以当某些值丢失时,我们将得到空值而不是NaN。稍后当我们将所有街道地址变量合并成一个变量时,这将非常有用。

acnc.shape 给出了 74155 行和 60

现在,让我们创建一个新的数据框架mel,在通过变量Town_City过滤后作为acnc的副本,以便只选择墨尔本市的慈善机构和非营利组织:

**mel = acnc[acnc.Town_City.str.contains('melbourne', case=False)][['ABN', 'Charity_Legal_Name', 'Address_Line_1', 'Address_Line_2', 'Address_Line_3', 'Town_City', 'State', 'Postcode', 'Country', 'Date_Organisation_Established', 'Charity_Size']].copy()**

我在这里做了两件事:我通过Town_City变量过滤了acnc数据帧,然后我从最初的 60 列中只选择了 11 列有用的列。copy()确保我们制作了经过过滤的acnc数据帧的正确副本。

我在这里没有使用acnc[acnc.Town_City == 'Melbourne'],因为我怀疑它可能以不同的方式编写。为了确保这是必要的:

**mel.Town_City.value_counts()**

正如我们在上面看到的,这个专栏包含了不同的方式来表明一个慈善机构确实位于墨尔本,其中一些使用了郊区,甚至是城市中非常特殊的地方,比如墨尔本大学。通过使用acnc.Town_City.str.contains('melbourne', case=False),我们确保了上述所有慈善机构都被计算在内(否则我们只能得到 1779 个正确标记的慈善机构)。

让我们看看新的mel数据框架是什么样的:

**mel.head()**

mel.shape 赋予 2722 行和 11

现在,让我们添加一个包含完整地址的新列Full_Address:

**mel['Full_Address'] = mel['Address_Line_1'].str.cat( mel[['Address_Line_2', 'Address_Line_3', 'Town_City']], sep=' ')**

str.cat()在这里有效,因为所有这些列都是 object 或 string 类型的。

下面是mel中第一个完整地址的例子:

**mel.Full_Address.iloc[0]Output:'G Se 11 431 St Kilda Rd   Melbourne'**

还有一点:这些完整的地址中,有些只包含邮政信箱号码(称为邮政信箱或邮政信箱)。这些地址对我们来说完全没有用,因为它们不涉及一个已存在的地方。这里有一个例子:

**mel[mel.Full_Address.str.contains('po box', case=False)].Full_Address.iloc[0]Output:'GPO Box 2307   Melbourne VIC 3001 AUSTRALIA'**

在继续之前,我们需要删除这些记录(或行):

**mel = mel[~mel.Full_Address.str.contains('po box', case=False)].copy()**

最后一件事:一些地址包含字符/,它可以破坏任何 URL。我们需要用空格替换任何斜杠:

**mel.Full_Address = mel.Full_Address.str.replace('/', ' ')**

探索谷歌地图:

rawpixel.com

在任何网络抓取工作之前,探索你想要从中提取数据的网站是很重要的。在我们的例子中,它是谷歌地图

首先,让我们研究一下使用 Google Maps 内部的搜索栏搜索完整地址是如何影响结果页面的 URL 的。为此,我将使用虚构的地址Grinch house mount crumpit whoville ,因为我希望谷歌地图不返回任何结果:

正如你在上面看到的,我们得到了www.google.com/maps/search/,后面是我们搜索的地址。换句话说,如果我们想在谷歌地图中搜索一个地址 XYZ,我们所要做的就是使用网址www.google.com/maps/search/XYZ,而不必与搜索栏本身进行交互。

这里的想法是在mel中生成一个新列,我们将www.google.com/maps/search/与数据帧mel,中的每一个Full_Address组合在一起,然后让 Selenium 遍历它们,一个接一个地访问 URL。

让我们创建新的Url列:

**mel['Url'] = ['https://www.google.com/maps/search/' + i for i in mel['Full_Address'] ]**

现在我们有了一个包含所有要抓取的 URL 的列,让我们看看地址G Se 11 431 St Kilda Rd Melbourne例如:

[www.google.com/maps/search/G 东南 11 区,墨尔本第一基尔达路 431 号](http://www.google.com/maps/search/G Se 11 431 St Kilda Rd Melbourne)

上面的链接给了我们:

以上地址对应的是慈善机构澳大利亚护士纪念中心。让我们在谷歌地图上按名字搜索一下:

我们得到完全相同的点,但在 URL 中没有相同的坐标。这是因为 URL 中的坐标与地图的居中方式相关联,而不是与标记相关联(如果放大或缩小,坐标会发生变化)。这就是为什么我们要直接从页面本身的源代码中提取坐标。

要查看源代码,请右键单击页面内的空白区域(地图外部),然后选择“查看页面源代码”(在 Mac 中为 CTRL+U 或 Command+U)。现在在源页面中搜索 -37.8144.9 :

你会在源代码中的很多地方找到我们正在寻找的坐标。但是如果它们包含在一个我们可以瞄准的 HTML 标签中,那么它们对我们来说是最有用的。幸运的是,这里有一个元标记我们可以利用:

现在,让我们注意它是一个带有属性contentmeta标签,属性content包含我们想要提取的 URL,属性itemprop的值image可以用来识别和定位这个特定的meta标签。

现在我们所要做的就是使用 Selenium 访问mel.Url中的每个 URL,并定位这个meta标签,以提取其属性content的值。

使用硒:

rawpixel.com—网络仓鼠

下面是我们将用来从 Google Maps 中提取包含坐标的 URL 的代码:

  • 第 1 行:我们创建了一个名为Url_With_Coordinates的空列表,稍后我们将用(你猜对了)我们想要提取的 URL 填充它;
  • 第 3 行到第 5 行 : prefs 在没有 javascript 和图片的情况下运行 Webdriver。这样,代码加载网页的时间会少得多。显然,如果您想要提取的内容依赖于 javascript,这不是一个好的选择。移除'images':2, 'javascript':2,网页将正常加载图片和 javascript
  • 确保指定你把 chromedriver.exe 文件放在你机器的什么地方。在我的例子中,为了简单起见,我把它放在 c 盘中。注意,路径中的反斜杠\需要加倍\\才能识别路径;
  • 第 9 行:这个 for 循环在mel.Url系列上迭代。包装我们的 iterable 的tqdmn()在执行完单元后添加了一个进度条。其参数leave=False确保操作完成后,杆消失;
  • 第 10 行:对于mel.Url中的每个 URL,网络驱动打开那个 URL(对于第一个 URL,你会看到一个 chrome 窗口打开,然后你会注意到它从一个 URL 到另一个 URL,直到mel.Url结束);
  • 第 11 行:首先,我们使用driver.find_element_by_css_selector搜索我们的meta标签,并且我们通过meta[itemprop=image]识别标签。之后,我们使用.get_attribute('content')提取属性content的值。然后使用append()将该操作的结果(包含坐标的 URL)添加到Url_With_Coordinates列表中。
  • 第 13 行:我们在脚本完成后关闭 webdriver (chrome 窗口)(这是一个很好的做法)。

下面是运行中的脚本和 tqdm 进度条(或 tqdmn,因为我使用的是 tqdm_notebook 子模块) :

tqdm 估计整个操作大约需要 32 分钟(大约每秒 1 次迭代或 URL)

NB 1: 下次运行笔记本的时候,你就不用再重新运行一遍网页抓取代码了,因为我们已经把结果保存在一个名为Url_With_Coordinates.csv的 CSV 文件里了。让我们改为读取该文件:

**import csvwith open('Url_With_Coordinates.csv', 'r') as f:
    reader = csv.reader(f, delimiter=',')
    for i in reader:
        Url_With_Coordinates = i
        break**

在你的测试中,你不会想让 for 循环遍历数千个地址,到头来却抛出一个错误。在正确执行脚本之前,您需要先测试几个值。在我们的例子中,测试代码将是这样的,只检查mel.Url的前 10 个值:

**for url in tqdmn(mel.Url[:10], leave=False):
    driver.get(url)
    ......**

现在让我们看看Url_With_Coordinates列表是什么样子的:

2009 年前 5 个网址

让我们将这个列表作为一列添加到我们的mel数据框架中:

**mel['Url_With_Coordinates'] = Url_With_Coordinates**

但是我们如何从这些 URL 中提取坐标呢?下面是如何使用 Python 的split()方法实现这一目的的直观解释:

该代码翻译成如下代码(该代码不会运行,因为没有定义url,它只是为了展示上面的解决方案是如何工作的):

**url.split('?center=')[1].split('&zoom=')[0].split('%2C')Output:[-37.8386737, 144.97706]**

现在使用上面的代码,我们将向我们的mel数据框架添加两个新列:lat表示纬度,long表示经度:

**mel['lat'] = [ url.split('?center=')[1].split('&zoom=')[0].split('%2C')[**0**] for url in mel['Url_With_Coordinates'] ]mel['long'] = [url.split('?center=')[1].split('&zoom=')[0].split('%2C')[**1**] for url in mel['Url_With_Coordinates'] ]**

最有可能的是,上面的代码给出了一个错误list index out of range:

这个错误意味着对于列Url_With_Coordinates中的一些 URL,split()方法没有按预期工作。也许一些 URL 没有我们用于split()方法的关键字。让我们寻找缺少&zoom=的 URL,例如:

**mel[~mel.Url_With_Coordinates.str.contains('&zoom=')]**

正如我们在这里看到的,我们有 5 个提取的 URL 以//www.gstatic.com/images ...开头的实例(因此我们得到了错误) :

**list(mel[~mel.Url_With_Coordinates.str.contains('&zoom=')].Url_With_Coordinates)Output:['//www.gstatic.com/images/branding/product/1x/maps_round_512dp.png',
 '//www.gstatic.com/images/branding/product/1x/maps_round_512dp.png',
 '//www.gstatic.com/images/branding/product/1x/maps_round_512dp.png',
 '//www.gstatic.com/images/branding/product/1x/maps_round_512dp.png',
 '//www.gstatic.com/images/branding/product/1x/maps_round_512dp.png']**

为了简单起见,因为 5 不是一个大数字,我们将从mel中删除这些实例:

**mel = mel[mel.Url_With_Coordinates.str.contains('&zoom=')].copy()**

现在,让我们重新运行将两列latlong添加到我们的mel数据帧的代码:

**mel['lat'] = [ url.split('?center=')[1].split('&zoom=')[0].split('%2C')[0] for url in mel['Url_With_Coordinates'] ]mel['long'] = [url.split('?center=')[1].split('&zoom=')[0].split('%2C')[1] for url in mel['Url_With_Coordinates'] ]**

成功了!下面是我们的mel数据框架的样子,其中每个慈善机构或非营利组织都获得纬度和经度值(这里的一些列被屏蔽了) :

**mel.head()**

让我们来绘制这些坐标,看看它们能有多精确。

使用叶子进行坐标映射:

在此下载该地图的 HTML 文件

颜色编码(慈善规模取决于年收入) :

  • 红色:大型慈善机构(100 万澳元以上);
  • 紫色:中等慈善机构(25 万澳元至 100 万澳元之间);
  • 橙色:小型慈善机构(25 万澳元以下);
  • 灰色:无数据。

下面是我们将用来映射这些坐标的代码:

我不会详细介绍如何使用 Folium,但我会在这里澄清一些要点:

  • 我在这张地图上使用了CartoDB positron,因为它对彩色标记提供了非常低的对比度(这使得它们更加明显)。使用默认图块OpenStreetMap很难看到标记;
  • 我使用folium.CustomIcon的参数icon_size=(..,..)根据慈善机构的规模改变了标记的大小。这背后的原因是为了防止居住在同一栋楼里的慈善机构互相掩盖。因为先画大的标记,然后在上面画小的标记,这样即使重叠的慈善机构也能被区分出来;
  • 我使用自定义标记(托管在 imgur 上),因为默认标记会大大降低导航速度,因为我们在地图上有大约 2000 个标记。对于自定义标记,您可以提供要使用的图像的 URL,或者您计算机上图像文件的路径。
  • 如果你点击一个标记,它会给你慈善机构的名称和地址,所以你可以验证定位是否正确;
  • 在上面的代码中,我可以只使用mel_map而不是mel_map.save('mel_map.html')后跟IFrame(src='mel_map.html', width='100%', height=500),但是当标记的数量很大时,最好将地图保存为 HTML 文件,然后使用IFrame()打开它(否则您将得到一个空白地图)。

这种方法有多准确可靠?

我们将使用已经有街道地址和坐标的华盛顿州 DC 的企业数据集来测试我们的方法——图片(CC0)pxhere.com

这是一个非常合理的问题。显然,从街道地址中获取坐标的最佳方式是从一个像 Google Maps 或 Bing 这样的知名 API 中获取,但是这些选项可能要花钱。

这种方法的准确性很大程度上受所提供的街道地址的精确度和正确性的影响。例如,在我们上面的例子中,你会注意到一个标记被扔在了印度洋的中央。经审查,地址65 Macarae Road Melbourne实际上应该是65 Mcrae Road Melbourne,因此出错。

为了从经验上测试我们的方法,我们将使用一个数据集,该数据集包含华盛顿州 DC 数千家企业的街道地址和坐标。我们将随机抽取 500 个商业地址,然后使用我们的方法从中生成坐标。之后,我们会将它们与数据集中列出的实际坐标进行比较。我们将在测试中使用的数据集可以从这里下载:

我们将用来测试我们方法的数据集

结果如下(测试详情可在 Jupyter 笔记本中获得) :

红色:数据集中的实际坐标—橙色:使用 Selenium 和 Google Maps 生成的坐标(此处下载 HTML 文件形式的地图)

正如我们在上面看到的,在华盛顿州 DC 随机抽样的 500 家企业中,生成的坐标非常接近实际坐标。我将橙色标记做得比红色标记小,这样当它们完全重叠时就都可见了(这就是为什么缩小时橙色标记看起来有红色边框)。

使用 python 包构建您的自定义数据集— MLDatasetBuilder

原文:https://towardsdatascience.com/using-python-package-to-build-your-custom-dataset-mldatasetbuilder-d23ffd6d4fd1?source=collection_archive---------40-----------------------

MLDatasetBuilder 是一个 python 包,可帮助您为 ML 数据集准备图像。

图片来源于【www.spritle.com】

作为一名 ML 新手,我需要找出为我们的机器学习训练模型准备数据集的最佳方法。根据我的上一篇文章,我为这个过程设计了一个 Python 包!

每当您训练自定义模型时,重要的是数据集。是的,当然,数据集在深度学习中起主要作用。模型的准确性将取决于数据集。因此,在训练自定义模型之前,您需要计划如何构建数据集?在这里,我将分享我关于构建数据集的简单方法的想法。

** [## MLDatasetBuilder

MLDatasetBuilder 是一个 python 包,可帮助您为 ML 数据集准备图像。每当我们启动一台机器…

pypi.org](https://pypi.org/project/MLDatasetBuilder/)

MLDatasetBuilder-版本 1.0.0

为机器学习构建数据集的 Python 包

每当我们开始一个机器学习项目时,我们首先需要的是一个数据集。数据集将是训练模型的支柱。您可以自动或手动构建数据集。MLDatasetBuilder 是一个 python 包,可帮助您为 ML 数据集准备图像。

[## karthick 965938/ML-数据集构建器

MLDatasetBuilder 是一个 python 包,可帮助您为 ML 数据集准备图像。每当我们启动一台机器…

github.com](https://github.com/karthick965938/ML-Dataset-Builder)

装置

我们可以使用下面的命令安装MLDatasetBuilder

pip install MLDatasetBuilder

如何测试

当您在终端中运行python3时,它将产生如下输出:

Python 3.6.9 (default, Apr 18 2020, 01:56:04) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

运行下面的代码可以得到 MLDatasetBuilder 包的初始化过程输出。**

>>> from MLDatasetBuilder import *
>>> MLDatasetBuilder()

初始化过程的输出

可用操作

准备图像移除不需要的格式图像,并重命名您的图像

#PrepareImage(folder_name, image_name)
**PrepareImage('images', 'dog')**

提取图像视频文件中提取图像

#ExtractImages(video_path, file_name, frame_size)
**ExtractImages('video.mp4', 'frame', 10)**
#OR
#ExtractImages(video_path, filename)
**ExtractImages('video.mp4', 'frame')**
#Default FPS will be 5

第一步——从谷歌获取图片

是的,我们可以从谷歌上获取图片。使用下载所有图像浏览器扩展,我们可以在几分钟内轻松获取图像。你可以查看这里关于这个扩展的更多细节!

从谷歌获取图片

步骤 2-创建 Python 文件

一旦你使用这个扩展下载了图像,你就可以创建一个名为 test.py 的 python 文件,目录如下。

download_image_folder/
|   _14e839ba-9691-11ea-a968-2ed746e9a968.jpg
|   5e5f7af12600004018b602c0.jpeg
|   A471529_Alice_b-1.jpg
|   image1.png
|   image2.png
|   ...
test.py

在 images 文件夹中,你可以看到许多 PNG 图像和随机文件名。

步骤 3 —准备图像

MLDatasetBuilder提供了一个名为 PrepareImage 的方法。使用此方法,我们可以删除不想要的图像,并重命名您已经从浏览器的扩展下载的图像文件。

*PrepareImage(folder_path, class_name)
#PrepareImage('download_image_folder', 'dog')*

根据上面的代码,我们需要提到图像文件夹路径类名

准备图像选项的输出

完成这个过程后,你的图像文件夹结构将如下所示

*download_image_folder/
|   dog_0.jpg
|   dog_1.jpg
|   dog_2.jpg
|   dog_3.png
|   dog_4.png
|   ...
test.py*

这个过程非常有助于在贴标签的同时给你的图像添加注释。当然,这将是标准化的事情之一。

步骤 4 —提取图像

MLDatasetBuilder还提供了一个名为**的提取图像的方法。使用这种方法我们可以从视频文件中提取图像。****

**download_image_folder/
video.mp4
test.py**

根据下面的代码,我们需要提到视频路径、文件夹名称、帧大小。文件夹名类名framesize 的设为默认值 5 并且不是强制的

**ExtractImages(video_path, folder_name, framesize)
#ExtractImages('video.mp4', 'frame', 10)
ExtractImages(video_path, folder_name)
#ExtractImages('video.mp4', 'frame')**

提取图像方法的输出

完成这个过程后,你的图像文件夹结构将如下所示

**download_image_folder/
dog/
|   dog_0.jpg
|   dog_1.jpg
|   dog_2.jpg
|   dog_3.png
|   dog_4.png
|   ...
dog.mp4
test.py**

什么是 2.0.0 版?

我计划在下个月发布 2.0.0 版本,这将包括一些额外的功能。

我的意思是这个包将提供超过 100 个带有注释文件的物体图片:)

贡献的

欢迎所有问题和拉请求!要在本地运行代码,首先,派生存储库,然后在您的计算机上运行以下命令:

**git clone https://github.com/<your-username>/ML-Dataset-Builder.git
cd ML-Dataset-Builder
# Recommended creating a virtual environment before the next step
pip3 install -r requirements.txt**

添加代码时,一定要在必要的地方编写单元测试。

接触

MLDatasetBuilder 是由 Karthick Nagarajan 创建的。请随时在TwitterLinkedin 或通过电子邮件联系我们!

原载于www.spritle.com**

用 Python & Stan 用一个不切实际的模型选出超级碗冠军

原文:https://towardsdatascience.com/using-python-stan-to-pick-the-superbowl-winner-with-an-unrealistic-model-e68c84c3e95a?source=collection_archive---------25-----------------------

贝叶斯逻辑回归的一个简单例子

戴夫·阿达姆松在 Unsplash 上拍摄的照片

本文将说明一种使用 Stan 构建简单、不切实际但有用的概率模型的方法,以:

  • 根据球队进攻、对手防守以及球队是否在主场比赛,预测每支 NFL 球队在一场比赛中的得分。
  • 使用该模型模拟一个时间表,其中每支球队与所有 31 支球队进行 100 次主客场比赛。
  • 使用结果为所有 32 个团队生成一个实力排名。
  • 使用该模型预测季后赛的结果&最终是超级碗。

原定目标

我本来是想找一个小问题,学习如何使用 Stan 进行贝叶斯建模。NFL 权力排名模型似乎是一个有趣的尝试。某一年的数据样本量很小,因此很有吸引力:

  • 32 支队伍
  • 16 场比赛
  • 512 种结果——每场比赛每队一种
  • 随着新分数的出现,该模型可以每周更新。

从斯坦的主页——https://mc-stan.org/——可以下载该软件。

关于斯坦

“Stan 是用于统计建模和高性能统计计算的一流平台。成千上万的用户依赖 Stan 进行社会、生物、物理科学、工程和商业领域的统计建模、数据分析和预测。

用户在 Stan 的概率编程语言中指定对数密度函数,得到:

  • 使用 MCMC 抽样的完全贝叶斯统计推断(NUTS,HMC)
  • 带有变分推理的近似贝叶斯推理
  • 优化的惩罚最大似然估计(L-BFGS)”

数据

数据很容易从 https://www.pro-football-reference.com/years/2019/games.htm 获得。只需单击共享箭头选项卡,即可获得要复制的数据的 csv 布局。

为了保持模型简单,模型需要的字段是:

  • 获胜队
  • 失败的队伍
  • 获胜团队得分
  • 失去团队分数
  • 主/从指示器—>@

不切实际的分数概率模型

在实际比赛中,得分事件产生 1、2、3 或 6 分,并且是 3 个以上因素的结果。

将被评估的模型将把分数仅仅作为球队进攻、对手防守以及球队是否在主场比赛的函数。

用来预测球队得分的不切实际的模型是:

  • 一个队可以得 0 到 60 分
  • 团队得分的点数被建模为具有 60 次试验的二项式分布,并且成功概率 p 如上所述被估计。

将使用 Pystan 对模型进行估计,并对模型中的特征进行先验分布,如下所述。

数据处理

抱歉——为读到这篇文章的任何数据科学家争论——我道歉。

进口:

import pandas as pd
import numpy as np
import pystan
from   pystan import StanModel

(1)读入保存的结果和重命名列的 csv 文件:

df         = pd.read_csv("~/ml/Data/NFL2019.csv")df.columns = ['Week', 'Day', 'Date', 'Time', 'Winner',    'away','Loser','Unnamed: 7', 'PtsW', 'PtsL', 'YdsW', 'TOW', 'YdsL', 'TOL']

(2)建立将城市队名称映射到队名称和队 id 的字典。最初,我认为我必须构建一个 256 X 65 X 的设计矩阵,因此团队 id 在下面的代码中是 col_id。稍后会详细介绍。

如果 col_id 没有从 1 开始,问题可能会在以后出现,因为 Stan 默认从 1 而不是 0 开始。

team_dict = {}
col_id    = 1for i in range(df.shape[0]):
    team_ = df.Winner[i]
    temp  = team_.split()
    temp  = temp[-1]
    if team_ not in team_dict:
        team_dict[team_] = dict(team=temp,k= col_id,)
        col_id += 1

带有 k 的前几个 team_dict 条目是 team_id:

{'Green Bay Packers': {'team': 'Packers', 'k': 1},
 'Minnesota Vikings': {'team': 'Vikings', 'k': 2},
 'Los Angeles Rams': {'team': 'Rams', 'k': 3},
 'Baltimore Ravens': {'team': 'Ravens', 'k': 4},
 'Philadelphia Eagles': {'team': 'Eagles', 'k': 5},

(3)构建反向字典,将列 id 映射回团队

X_dict = {}
for key in team_dict:
    x_key = team_dict[key]['team']
    X_dict[x_key] = team_dict[key]['k']

前几个 X_dict 条目:

{'Packers': 1,
 'Vikings': 2,
 'Rams': 3,
 'Ravens': 4,
 'Eagles': 5,

(4)构建 X 矩阵以馈送给 Stan。它将有 3 列:

  • 我们预测其得分的团队的 id
  • 它正在玩的队的 id(防御)
  • 基于数据中@符号的 home 0/1 指示器

此外,存储分数。

对于数据中的每一行,在 X 矩阵中创建两行,一行用于获胜的团队,一行用于失败的团队。idx_o & idx_d 是 team_dict 中攻防队的指数。

这导致在 3 列向量中存储可能非常稀疏的矩阵(在 65 列的每行中只有 3 个非零元素)。

X      = np.zeros((2*df.shape[0],3),int)
score  = np.zeros(2*df.shape[0],int)
row    = 0for i in range(df.shape[0]):
    idx_o    = team_dict[df.Winner[i]]['k']
    idx_d    = team_dict[df.Loser[i]]['k']
    X[row,0] = idx_o                         
    X[row,1] = idx_d
    score[row]     = df.PtsW.values[i]
    if df.away[i] != '@':
        X[row,2] = 1 
    row += 1

    idx_o = team_dict[df.Loser[i]]['k']
    idx_d = team_dict[df.Winner[i]]['k']
    X[row,0] = idx_o
    X[row,1] = idx_d
    score[row]     = df.PtsL.values[i]
    if df.away[i] == '@':
        X[row,2] = 1

x 矩阵的前几行:

array([[ 1, 20,  0],
       [20,  1,  1],
       [ 2, 21,  1],
       [21,  2,  0],
       [ 3, 25,  0],
       [25,  3,  1],

使用 Stan 评估模型

(1)Stan 的数据字典定义—相当简单,告诉 Stan 数据是什么以及有多少行(N)。

datadict  = { 
    'offense'  : X[:,0] ,
    'defense'  : X[:,1],
    'home'     : X[:,2] ,
    'score'    : score,
    'N'        : X.shape[0]
}

(2)定义 Stan 模型——Python 多行字符串。

组件:

  • 数据 —如上面的 datadict 中所定义。
  • 参数 —我们希望 Stan 估计的参数列表。一个用于每个队的进攻和防守,一个用于主场比赛和拦截。主队系数有一个上限。
  • 转换后的参数 —该模型估计 p 的对数,我们不切实际的二项式模型中的概率。函数 inv_logit 将登录次数转换成成功的概率(在我们的例子中是 60 次试验中每一次的分数)。
  • 模型— 在模型部分,先验分布与我们正在估计的概率模型(得分)一起指定。
stanCode = """
data {
int N;
int score [N];
int offense [N];
int defense [N];
int home    [N];
}
parameters {
vector      [32]       b_offense;
vector      [32]       b_defense;
real <upper = 0.05>    b_home;
real                   alpha;
}
transformed parameters{
  real mu [N];
  for (n in 1:N){
    mu[n]  <- inv_logit(alpha + b_offense[offense[n]] 
                              + b_defense[defense[n]]
                              + b_home * home[n]);
  }
}model {
alpha      ~ normal(log(23),1);
b_offense  ~ cauchy(0,0.5);
b_defense  ~ cauchy(0,0.5);
b_home     ~ double_exponential(0,5);
score      ~ binomial(60,mu);}
"""

(3)编译 Stan 模型——这需要一些时间。将打印出一条信息消息。

from pystan import StanModel
sm = StanModel(model_code=stanCode)

(4)估计模型系数——op 将是一个字典,包含每个队的进攻和防守系数、主客场系数、截距和预测 mu。

op = sm.optimizing(data=datadict)

实力排名

在这一点上,我们现在已经有了模型权重,它使我们能够通过在游戏中运行 sigmoid 变换,根据任何两个队的进攻和防守系数以及该队是否在主场,来预测他们在游戏中的得分。

我们可以模拟任何球队和其他球队比赛的结果…不限于实际比赛。那么为什么不去做呢?我们模拟了一个时间表,其中每支球队与其他球队“比赛”100 次,50 次在主场,50 次在客场。我们记录赢、输和平局。

首先,我们需要调整索引(team_ids ),因为模拟将在 Python 中进行,并构建一个团队列表来运行模拟。

team_list = []
for team in X_dict:
    team_list.append(team)
    X_dict[team] = X_dict[team] - 1
    print("{a:>20s}  {b:6.3f}  {c:6.3f}".format(
        a=team, 
        b=op['b_offense'][X_dict[team]],
        c=op['b_defense'][X_dict[team]]))

检查前几行——乌鸦和酋长队的进攻系数非常正,防守系数为负,而老鹰队接近平均水平。

 offense  defense
Packers   0.037   -0.203              
Vikings   0.167   -0.226                 
Rams      0.147   -0.131               
Ravens    0.752   -0.358               
Eagles    0.047    0.018                
Bills    -0.252   -0.450               
Chiefs    0.427   -0.226

模拟所有球队之间的 100 场比赛赛季:

wins   = np.zeros(len(team_list),int)
losses = np.zeros(len(team_list),int)
ties   = np.zeros(len(team_list),int)
for idx_1 in range(31):
    for idx_2 in range(idx_1+1,32):
        team_1 = team_list[idx_1]
        team_2 = team_list[idx_2]

        for game in range(100):
            at_home = game % 2 u      = op['alpha'] 
            u     += op['b_offense'][X_dict[team_1]] 
            u     += op['b_defense'][X_dict[team_2]] 
            u     += op['b_home'] * at_home
            prob   = 1\. / (1\. + np.exp(-u))
            pts_1  = np.random.binomial(60,prob) u      = op['alpha'] 
            u     += op['b_offense'][X_dict[team_2]] 
            u     += op['b_defense'][X_dict[team_1]] 
            u     += op['b_home'] * (1 - at_home)
            prob   = 1\. / (1\. + np.exp(-u))
            pts_2  = np.random.binomial(60,prob) if pts_1 > pts_2:
                wins[idx_1]   += 1
                losses[idx_2] += 1
            elif pts_1 < pts_2:
                wins[idx_2]   += 1
                losses[idx_1] += 1
            else:
                ties[idx_1]   += 1
                ties[idx_2]   += 1

结果——按获胜百分比排列的实力等级(平局算作 1/2 的胜负)。

report = pd.DataFrame(dict(
    team = team_list,
    won  = wins,
    lost = losses,
    ties = ties
))
report['winpct'] = np.round((report.won + 0.5 * report.ties) / (report.won + report.lost + report.ties),3)report.sort_values('winpct',ascending=False,inplace=True)
report.reset_index(inplace=True)
report = report[['team','won','lost','ties','winpct']]
print(report)

【2019 赛季模拟实力排名:

这些排名通过了一个意义上的检验——强队在顶部,弱队在底部。大部分球队都在中心,公羊队和牛仔队在排名上表现不佳,而海鹰队和老鹰队表现出色,进入了季后赛。

 team    won  lost  ties  winpct
0       Ravens  3025    53    22   0.979
1     Patriots  2823   230    47   0.918
2        49ers  2795   253    52   0.910
3       Chiefs  2653   379    68   0.867
4       Saints  2460   558    82   0.807
5      Cowboys  2269   726   105   0.749
6      Vikings  2270   735    95   0.748
7         Rams  2104   867   129   0.700
8       Titans  2014   942   144   0.673
9      Packers  1997   979   124   0.664
10    Seahawks  1882  1073   145   0.630
11       Bills  1882  1081   137   0.629
12      Texans  1589  1371   140   0.535
13  Buccaneers  1593  1377   130   0.535
14      Eagles  1585  1366   149   0.535
15    Steelers  1545  1391   164   0.525
16     Falcons  1487  1469   144   0.503
17       Bears  1342  1594   164   0.459
18    Chargers  1297  1666   137   0.440
19       Colts  1237  1716   147   0.423
20      Browns  1213  1739   148   0.415
21     Broncos  1212  1754   134   0.413
22   Cardinals  1032  1922   146   0.356
23       Lions   750  2199   151   0.266
24        Jets   603  2369   128   0.215
25     Raiders   586  2403   111   0.207
26     Jaguars   587  2415    98   0.205
27    Panthers   552  2441   107   0.195
28     Bengals   511  2479   110   0.183
29      Giants   473  2529    98   0.168
30    Redskins   209  2826    65   0.078
31    Dolphins   178  2853    69   0.069

模拟季后赛

用于获取获胜概率的 1 场比赛的函数:

def sim_games(team_1,team_2,row_id,report,s_round,add_home=True):
    wins   = np.zeros(2,int)
    losses = np.zeros(2,int)
    ties   = np.zeros(2,int)
    score  = np.zeros((1000,2),int) for game in range(1000):        
        u      = op['alpha'] 
        u     += op['b_offense'][X_dict[team_1]]   
        u     += op['b_defense'][X_dict[team_2]]  
        u     += op['b_home'] * add_home
        prob   = 1\. / (1\. + np.exp(-u))
        pts_1  = np.random.binomial(60,prob)

        score[game,0] = pts_1 u      = op['alpha'] 
        u     += op['b_offense'][X_dict[team_2]]   
        u     += op['b_defense'][X_dict[team_1]]  
        u     += op['b_home'] * add_home
        prob   = 1\. / (1\. + np.exp(-u))
        pts_2  = np.random.binomial(60,prob)

        score[game,1] = pts_2 if pts_1 > pts_2:
            wins[0]   += 1
            losses[1] += 1
        elif pts_1 < pts_2:
            wins[1]   += 1
            losses[0] += 1
        else:
            ties[0]   += 1
            ties[1]   += 1new_row = pd.DataFrame(dict(
        Round        = s_round,
        Visitor      = team_1,
        V_Wins       = int(wins[0]+ ties[0]/2),
        V_Score      = np.round(np.mean(score[:,0]),1),
        Home         = team_2,
        H_Wins       = int(wins[1]+ ties[1]/2),
        H_Score      = np.round(np.mean(score[:,1]),1)
),index=[row_id])report = pd.concat((report,new_row))
return(report) 

模拟季后赛的函数调用:

进行第一轮比赛,并将预计的获胜者传送到第二轮比赛。

report = pd.DataFrame()
row_id = 0
report = sim_games("Bills","Texans",
row_id,report,s_round='Round 1',add_home=True)
row_id = 1
report = sim_games("Titans","Patriots",
row_id,report,s_round='Round 1',add_home=True)
row_id = 2
report = sim_games("Seahawks","Eagles",
row_id,report,s_round='Round 1',add_home=True)
row_id = 4
report = sim_games("Vikings","Saints",
row_id,report,s_round='Round 1',add_home=True)
row_id = 5
report = sim_games("Bills","Ravens",
row_id,report,s_round='Round 2',add_home=True)
row_id = 6
report = sim_games("Patriots","Chiefs",
row_id,report,s_round='Round 2',add_home=True)
row_id = 7
report = sim_games("Seahawks","49ers",
row_id,report,s_round='Round 2',add_home=True)
row_id = 8
report = sim_games("Saints","Packers",
row_id,report,s_round='Round 2',add_home=True)
row_id = 9
report = sim_games("Saints","49ers",
row_id,report,s_round='Round 3',add_home=True)
row_id = 10
report = sim_games("Patriots","Ravens",
row_id,report,s_round='Round 3',add_home=True)
row_id = 11
report = sim_games("49ers","Ravens",
row_id,report,s_round='superbowl',add_home=False)

结果

 Round   Visitor  V_Wins  V_Score      Home  H_Wins  H_Score
0     Round 1     Bills     648     19.9    Texans     352     17.9
1     Round 1    Titans      77     16.6  Patriots     922     23.9
2     Round 1  Seahawks     685     26.3    Eagles     315     23.5
4     Round 1   Vikings     392     22.9    Saints     607     24.3
5     Round 2     Bills       9     15.4    Ravens     990     27.2
6     Round 2  Patriots     665     22.5    Chiefs     335     20.0
7     Round 2  Seahawks      63     21.3     49ers     936     29.5
8     Round 2    Saints     792     25.1   Packers     207     20.9
9     Round 3    Saints     253     23.2     49ers     746     26.6
10    Round 3  Patriots     233     20.5    Ravens     766     24.3
11  superbowl     49ers     166     24.3    Ravens     834     29.6

总结

2019 年的 NFL 结果被用来建立一个使用 PyStan 预测分数的模型,更重要的是作为一个如何使用 Stan 估计贝叶斯模型的例子。

随着数据的可用和赛季的进展,该模型可用于生成每周电力排名。

一些观察结果:

  • 尽管该模型在权力排名上做得相当好,但预测的分数似乎受到了先验的影响。
  • 具有更大分布的先验可能导致更宽的预测得分范围。
  • 模型的弱点包括模型是基于整个赛季到目前为止,没有额外的重量给予最近的分数,关键的伤病或球员回归。

问题&建议

如果您有任何问题、建议或需要预测模型,请随时发送电子邮件至:

bill.fite@miner3.com。

用 Python 分析《黑色大丽花谋杀案》的残酷歌词

原文:https://towardsdatascience.com/using-python-to-analyze-the-brutal-lyrics-of-the-black-dahlia-murder-with-genius-api-spacy-bfc7e0e8577f?source=collection_archive---------32-----------------------

发现死亡金属的主题

UnsplashActionVance 拍照

为什么是黑色大丽花谋杀案?

为了纪念他们即将于 2020 年 4 月 17 日发行的专辑verminus,也因为他们是我有史以来最喜欢的死亡金属乐队之一,我决定分析黑色大丽花谋杀案(BDM)的歌词,看看它们是如何随着时间的推移而演变的。早在 2006 年,我的朋友刻录了一张他最喜欢的金属歌曲的 CD,上面有 BDM 专辑中的“瘴气”。一听到这首歌,我就对生活着了迷,并看过他们五次现场表演。成立于 2001 年,BDM 迅速崛起为最受欢迎的美国死亡金属乐队之一。他们的最后七张专辑随着他们的第五张专辑仪式登上 Billboard 200 排行榜,在 2011 年达到 31 张的峰值!

https://en . Wikipedia . org/wiki/Ritual _(The _ Black _ Dahlia _ Murder _ album)

主题是黑暗和忧郁的,音乐感觉像是愤怒和抑郁的化身,但作为 21 世纪初一个焦虑的青少年,死亡金属的能量和愤怒引起了我的共鸣。我能感同身受。直到今天,我还能在旋律重复、双踢和抱怨声中找到安慰,因为它们让我处于恍惚状态,让我放松,让我集中注意力。我发现死亡金属是编码和锻炼的完美音乐,我喜欢随着节奏开始我的一天。

获取数据

我正在从 Genius.com 中提取歌词。由于重金属音乐中令人毛骨悚然的尖叫声和神秘的咆哮,歌词有时很难理解。这就是为什么我用天才来破译它们。Genius.com是一个标注歌词、收集音乐、专辑、艺人等琐事的平台。

虽然网站不允许用户使用 API 提取歌词,但我使用的是一个 Genius API 包装器,它用 BeautifulSoup 解析 Genius HTML。这个过程会产生一个熊猫数据帧,其中包含歌曲名称、url、艺术家、专辑和歌词。在我的 GitHub 上一个名为 getLyrics.py 的文件中找到这个包装。

[## 弯曲游戏/歌词-分析

媒体歌词分析。在 GitHub 上创建一个帐户,为 bend game/歌词分析开发做出贡献。

github.com](https://github.com/bendgame/lyrics-analysis)

#import dependencies
from getLyrics import GeniusLyricCollector#pass the api token and artist name into the lyric collector
g = GeniusLyricCollector(token, 'The Black Dahlia Murder')#load the dataframe with the data
songs_df = g.get_artist_songs()#display first 5 rows
songs_df.head()

要使用包装器,将您的 Genius API 令牌和艺术家传递到Genius lyric collector中。然后调用 get_artist_songs() 来填充 DataFrame。

歌曲 _df 数据帧

探索相册

在分析数据之前,最好根据需要探索和清理数据。马上,我注意到第 2 行没有列出专辑,所以我知道我需要清理数据,我也想添加一些功能。为了保留原始数据帧,以防我需要返回它,我将复制它并在副本上工作。我查看了附带的相册:

lyrics = songs_df[["Lyrics", 'Album', "Title"]].copy()
lyrics.Album.unique()

我只想要他们 8 张录音室专辑里的歌。Verminous 还没有发行,也没有歌词,Black on Black 是一个致敬专辑,而冷血墓志铭是一个 EP,所以我会排除所有这些。

特征工程

特征工程是使用数据挖掘技术和领域知识从原始数据中提取特征的过程。我想设计一些功能,让我深入了解专辑的词汇丰富性。我将使用几个不同的因素来定义词汇丰富性。我再加一个字数唯一字 字数唯一字/字数(词汇多样性)。虽然这是一个有用的度量,并且是一个简单的计算,但是用这种方法计算词汇多样性的基本问题是它对文本长度的敏感性。幸运的是,每张专辑的长度都差不多。已经设计了几种不同的方法来克服方法的问题,但是我将使用简单的计算。

songs_group = lyrics.groupby('Album')
album_stats = pd.DataFrame(columns=('Album', 'word_count', 'unique_count', 'lexical_diversity', 'tokens'))
i = 0
for name, album in songs_group:# Create a list of words for every word that is in an album
    album_tokens = []for lyric in album['Lyrics'].iteritems():if isinstance(lyric[1], str):
            words = lyric[1].replace('\n', ' ')
            words = words.split(' ')album_tokens.extend(words)# Find how many words and unique words are in the album
    unique_count = len(set(album_tokens))
    word_count = len(album_tokens)# Calculate the lexical richness of the album, which is the amount of unique words relative to
    # the total amount of words in the album
    album_stats.loc[i] = (name, word_count, unique_count, (unique_count / float(word_count)), album_tokens)

    i += 1

album_stats["released"] = [2015,2009,2013,2005,2017,2007,2011,2003]
album_stats = album_stats.sort_values('released').reset_index().drop(columns=('index'))
album_stats

我还添加了专辑的发行年份,并按栏目排序。

album_stats["released"] = [2015,2009,2013,2005,2017,2007,2011,2003]
album_stats = album_stats.sort_values('released').reset_index().drop(columns=('index'))
album_stats

见识

在 2019 年接受 kerrang.com 采访时,作词人、歌手和乐队主唱 Trevor Strnad 讨论了专辑,将它们从最差到最佳进行了排名。最后,他放了乐队的第二张录音室专辑《T2》中的《瘴气》。伴随着这张专辑长大,我喜欢它,但他说这张专辑感觉没有重点,部分是因为他们从早期的成功和的失败中走得如此之远。统计数据显示, Miasma 拥有最少的单词和最少的独特单词,这表明特雷弗关于主题的观点是正确的,与其他专辑相比,歌词没有那么明确。他没能给予这张专辑应有的关注。

使用 Plotly Express 可视化统计数据

为了可视化数据,我使用了 plotly express ,因为它只用了几行代码就产生了交互式的可视化效果!下面是一个直方图,显示了专辑的字数分布。

#import dependencies
import plotly.express as px#create plot
fig = px.histogram(lyrics, x="word_count", color = 'Album')#show fig
fig.show()

箱线图是可视化四分位数统计的好方法。使用 pandas 的 describe() ,也很容易生成原始四分位数。使用 Plotly Express,可以设置盒图中使用的四分位数算法。默认为线性。

#drop songs with no lyrics
lyrics = lyrics.loc[lyrics.word_count > 0]#count words for each song
lyrics['word_count'] = lyrics['Lyrics'].apply(lambda x :  len(x.split()))#count unique words
lyrics['unique_count'] = lyrics['Lyrics'].apply(lambda x: len(set(w for w in x.split())))#calculate lexical diversity
lyrics['lexical_diversity'] = lyrics['unique_count'] / lyrics['word_count']#display the quartile numeric data.
lyrics.describe()fig = px.box(df, x="Album", y="word_count", color = "Album")
fig2 = px.box(lyrics, x="Album", y="unique_count", color = "Album")
fig3 = px.box(lyrics, x="Album", y="lexical_diversity", color = "Album")fig.update_traces(quartilemethod="linear") # or "inclusive", or "exclusive"
fig2.update_traces(quartilemethod="linear") # or "inclusive", or "exclusive"
fig3.update_traces(quartilemethod="linear") # or "inclusive", or "exclusive"fig.show()

再一次,我们可以看到专辑《瘴气》没有其他专辑的广度和多样性。看着词汇多样性较低的专辑,我注意到这些都是主题明确的专辑:夜行仪式深渊不被允许的。例如,是他们排行榜上最高的专辑,但词汇多样性比其他专辑低。这可能是因为像“哦,伟大的燃烧的否定者”这样的歌曲有重复的模式,像圣歌,这是专辑的主题。

用空间探索歌词

SpaCy 是一个工业级的自然语言处理库。它是在幕后使用 Cython 开发的,使其快速有效地用于大规模文本处理任务,如词性(PoS)标记和命名实体识别(NER)。我将使用词性标注器来更深入地了解专辑中使用的歌词和主题。

如果你是 spaCy 的新手,我建议你看看他们的安装页面,因为他们有很多安装选项。SpaCy 与 Python 2.7 / 3.5+兼容,可以在 Unix/Linux、macOS/OS X 和 Windows 上运行。最新的 spaCy 版本可以通过 pipconda 获得。否则,这些命令将使您启动并运行。

pip install -U spacy
pip install -U spacy-lookups-data
python -m spacy download en_core_web_sm

使用 spaCy,我可以将文本分割成标记,并使用词性标记器来识别所有的名词、动词和形容词。我要看看每张专辑前 15 个形容词,看看会出现什么样的模式。需要注意的是,spaCy 功能强大,但并不完美。它使用统计模型来预测词性或命名实体

import spacy# load a medium-sized language model
nlp = spacy.load("en_core_web_sm")Unhallowed = album_stats['tokens'].loc[album_stats['Album'] == 'Unhallowed']a1 = nlp(str(Unhallowed[0]))

我不分析原始词,而是分析引理。词条解释是一种技术,通过将单词简化为词根形式,同时保持它是一个真实的单词,从而使语言规范化。另一种常用的规范化技术叫做词干分析。

#create a new dataframe
unhallowed = pd.DataFrame(columns=("token", "pos", "lemma"))# map with frequency count
pos_count = {}
i = 0
for token in a1:
    unhallowed.loc[i] = (token, token.pos_, token.lemma_)
    i+=1

#locate the parts of speech to keep and reset the index 
unhallowed = unhallowed.loc[unhallowed['pos'].isin(['PROPN', 'NOUN', 'VERB', 'AUX',' DET', 'ADJ', 'ADP'])].reset_index().drop(columns =('index'))

注意,在上面的代码片段中,我创建了一个 dataframe,并用单词 token、词性标记和词条填充它。当我计算 ADJ 的计数时,我将使用引理而不是令牌

unhallowed_freq = Counter(unhallowed['lemma'].loc[unhallowed['pos'] == 'ADJ'])#call the 15 most frequent
unhallowed_adjs = unhallowed_freq.most_common(15)unhallowed_adjs

15 个最常见的形容词

很明显,死亡、黑暗和人性的脆弱是 BDM 音乐的主题。“死亡”这个词在所有专辑中高居榜首,除了他们早期的两张热门专辑《不被允许的》和《夜间的》。虽然很多相同的词出现在每张专辑中,但还是有一些主题上的不同。例如,在夜间中,我可以看到“邪恶的”、“可怜的”和“死灵的”这些词出现了多次,它们在主题上代表了一个被抛入永恒黑暗的世界,就像这张专辑所传达的那样。使用 plotly express 条形图,数据易于可视化。

包扎

虽然歌词只是我喜欢的死亡金属的一小部分,但分析起来很有趣。即使没有运行 K-means 或 LDA 等技术,spaCy 也可以使用标记器和命名实体识别等功能来帮助识别文本中的主题。看黑大丽花谋杀案的歌词,它们代表了愤怒和堕落,抑郁和疯狂。虽然有些人可能会因恶意的描述而回避或感到腐败,但我拥抱它和音乐,我永远是快速双踢、催眠旋律重复片段和黑色大丽花谋杀案惊人的音域的粉丝。我迫不及待地想听听他们在verminus 中会推出什么。

有关 NLP 和数据科学的更多信息,请查看我的其他教程:

[## 使用 Python 中的自然语言工具包分析葡萄酒描述

用什么词来形容酒?

towardsdatascience.com](/analyzing-wine-descriptions-using-the-natural-language-toolkit-in-python-497ac1e228d5) [## K-Means 使用 Scikit-Learn & NLTK 对 Chardonnay 评论进行聚类

霞多丽是世界上最受欢迎的白葡萄酒。聚类有助于区分高于平均水平的葡萄酒吗…

towardsdatascience.com](/k-means-clustering-chardonnay-reviews-using-scikit-learn-nltk-9df3c59527f3)

谢谢大家!

—埃里克·克莱本

使用 Python 从国家名称列表创建世界地图

原文:https://towardsdatascience.com/using-python-to-create-a-world-map-from-a-list-of-country-names-cd7480d03b10?source=collection_archive---------2-----------------------

从国家名称列表中,获取纬度和经度以创建世界地图

最近,我参与了一个项目,根据一系列简短的国家名称(如美国)创建一幅世界地图。在这里,我展示了创建世界地图(或任何其他地图)的步骤。

数据

有两列(国家名称、用户百分比)的样本数据是我们的原始数据。

1.转换为阿尔法 2 代码和大陆

alpha 2 代码更便于以后的分析,因此简短的国家名称被转换为 alpha 2 国家代码。例如,美国转换为美国。Python 的 pycountry-convert 包用于处理转换。下面的 Python 代码片段显示了一个要转换的函数。

#installation
pip install pycountry-convert#function to convert to alpah2 country codes and continentsfrom pycountry_convert import country_alpha2_to_continent_code, country_name_to_country_alpha2def get_continent(col):
    try:
        cn_a2_code =  country_name_to_country_alpha2(col)
    except:
        cn_a2_code = 'Unknown' 
    try:
        cn_continent = country_alpha2_to_continent_code(cn_a2_code)
    except:
        cn_continent = 'Unknown' 
    return (cn_a2_code, cn_continent)

在这个步骤之后,原始数据被处理如下:

2.获取经度和纬度

第二,基于这些 alpha 2 国家代码提取经度和纬度信息。Python 的 geopy 可以使用第三方地理编码器和其他数据源轻松定位地址、城市、国家和地标的坐标。下面的 Python 代码片段展示了一个获取经度和纬度的函数。

#installation
pip install geopy#function to get longitude and latitude data from country namefrom geopy.geocoders import Nominatimgeolocator = Nominatim()
def geolocate(country):
    try:
        # Geolocate the center of the country
        loc = geolocator.geocode(country)
        # And return latitude and longitude
        return (loc.latitude, loc.longitude)
    except:
        # Return missing value
        return np.nan

在运行上面的函数并将 geolocate 拆分为两个独立的纬度和经度列后,显示了下表。

3.创建世界地图

有许多 Python 包可用于创建视觉上吸引人且信息丰富的地图,包括底图、散景和树叶等。这里,follow 用于创建某些用户分布的世界地图。Folium 的 CircleMarker()通过改变半径和颜色变量来描述数据是很有用的。

#installation
pip install folium# Create a world map to show distributions of users 
import folium
from folium.plugins import MarkerCluster#empty map
world_map= folium.Map(tiles="cartodbpositron")marker_cluster = MarkerCluster().add_to(world_map)#for each coordinate, create circlemarker of user percent
for i in range(len(df)):
        lat = df.iloc[i]['Latitude']
        long = df.iloc[i]['Longitude']
        radius=5
        popup_text = """Country : {}<br>
                    %of Users : {}<br>"""
        popup_text = popup_text.format(df.iloc[i]['Country'],
                                   df.iloc[i]['User_Percent']
                                   )
        folium.CircleMarker(location = [lat, long], radius=radius, popup= popup_text, fill =True).add_to(marker_cluster)#show the map
world_map

用户可以放大地图,查看更详细的国家用户分布情况。

在这个例子中,我在几个 Python 包的帮助下创建了一个世界地图。不用说,使用给定的坐标可以很容易地创建其他类型的地理地图。

使用 Python 创建专辑中样本的 Spotify 播放列表

原文:https://towardsdatascience.com/using-python-to-create-spotify-playlists-of-the-samples-on-an-album-e3f20187ee5e?source=collection_archive---------19-----------------------

(通过去飞溅

用循环和随机样本交换鼓循环和灵魂样本

介绍

作为一个狂热的嘻哈迷,我一直对采样极其感兴趣。对于门外汉来说,音乐采样是将一首现有歌曲的一部分重新用作新歌的一部分的行为。一个很好的例子是坎耶·韦斯特 2005 年专辑中的我回家的路,这是吉尔·斯科特-赫伦 1971 年专辑中的家是仇恨的地方一个人的片段 **

迟到的报名充满了从前面提到的苍鹭曲目到雪莉·贝西的经典曲目“钻石恒久远”的精彩样本,该曲目曾作为同名邦德电影的主题曲。听了无数遍《迟到的登记》( Late Registration )( T23 ),我想更深入地了解音乐,并探索韦斯特和联合制作人乔恩·布里昂(Jon Brion)通过采样对文字的影响。我开始在 Genius 上搜索歌曲,找到样本的名称,并手动将它们添加到 Spotify 播放列表中。不出所料,过了一段时间后,这变得很麻烦,所以我写了一个脚本来自动化这个过程。下面,我将以后期注册为例演示脚本是如何工作的,但是如果你想直接跳到代码中,你可以在我的 GitHub 上找到。**

网页抓取

第一步是生成来自延迟注册的样本列表。如前所述, Genius 拥有大量关于每首歌曲样本的众包数据,因此只需收集专辑中每首歌曲页面的链接,循环浏览它们,并从每首歌曲中收集样本信息:

Genius 关于“我回家的路”的曲目信息

使用 Python 模块 BeautifulSoup4 和请求

至少我是这么认为的。在使用这种策略运行 scraper 时,看到结果数据帧包含 12 个结果(其中两个是重复的),我怀疑 scraper 丢失了信息,所以我在 Genius song 页面上做了一些手动检查以找出原因。

天才歌曲页为《晚期》作者坎耶·韦斯特

事实证明,一些 Genius 歌曲页面上的样本信息位于介绍性制作注释中,而不是歌曲的信息框中。这是一个令人沮丧的认识,但它促使我设计一个解决方法。

从注释中获取样本数据

像 Spotify 一样,Genius 也有一个免费 API ,它允许用户提取关于特定注释的信息,所以一旦我分离出注释,我只需要提取样本信息。因为注释不是标准化的,所以解析文本以确定样本的名称并不简单。然而,我注意到,每当 Genius 用户引用样本时,他们都会提供一个附带的 Youtube 链接。多么体贴。利用这些信息,我能够开发一个补充的 scraper,该 scraper 隔离一个 Genius 产品注释,并通过在注释中抓取 Youtube 视频的标题来输出示例信息:

现在配备了这个更健壮的 scraper,我们可以看看从晚注册Genius URL 输出的数据:

信息框中的示例数据(作者截图)

来自注释的示例数据(作者截图)

接下来,我将这两个数据帧分别称为sample_datatitles。尽管由于数据帧中存在重复的轨道,刮刀可能会出现一些错误,但这实际上是刮刀正在工作的证据。后期注册的前两首曲目,“ 叫醒韦斯特先生”和“听见他们说”被这首华丽的娜塔莉·科尔曲目的样本出色地联系在一起,前者原样采样介绍性的钢琴部分,后者采用循环上的一段相同的钢琴部分作为其节拍的基础《钻石是永恒的》出现了两次,因为这张专辑包含了原版的"来自塞拉利昂的钻石",它是 Bassey 曲目的样本,也是这张专辑的主打单曲,还有一张由 Kanye 的导师 Jay-Z 演唱的混音版

使用 Spotify 创建 Spotify 播放列表

下一步是自动创建一个恰当命名的 Spotify 播放列表,这可以通过Spotipy模块轻松完成。启动Spotipy是一个的简短过程,它可以归结为向 Spotify 注册为开发人员,以获得一个客户端 id 和一个客户端密码,结合一个活跃的 Spotify 用户名,允许你通过 Spotify 的免费 API,通过 Python 对你的 Spotify 帐户执行无数操作。我使用 Spotipy 的“授权代码流”来授权使用我的帐户,然后创建一个 Spotipy 对象:

**token = util.prompt_for_user_token(username,scope,client_id=client_id,client_secret=client_secret,redirect_uri='http://localhost/') 
sp = spotipy.Spotify(auth=token)**

现在我们可以创建播放列表了:

**playlist_name = f"Samples in {album_title} by {album_artist}"    sp.user_playlist_create(username, name=playlist_name)**

Spotify 上的播放列表

非常简单。执行这段代码后,我们在 Spotify 上有了一个标题描述性的播放列表,可以添加歌曲了。

向播放列表添加曲目

不幸的是,将曲目添加到播放列表并不像提交您想要的曲目列表那么简单,但我们不做数据科学,因为这很容易。我们这样做是因为这很难,因为创建 Spotify 音乐播放列表的挑战将有助于组织和衡量我们最好的精力和技能,因为这是一个我们将接受的挑战,一个我们不愿意推迟的挑战,一个我们打算赢得的挑战!但是,我跑题了。

下面是我写的一些代码,用于从sample_data数据帧和 Youtube 视频标题列表中获取 Spotify 曲目 id 列表。我使用了Spotipyfuzzywuzzy,这是一个模糊字符串匹配模块,我稍后将讨论它的用途:

由于我采取了一些不直观的步骤,我们将更详细地分解这段代码。我通过实验发现,Spotify 搜索的第一个结果并不总是想要的曲目,这是由于 Spotify 搜索算法的特性,我不会假装理解,因此有必要检索多个结果:

**results = sp.search(q=f"{sample_data['title'][i]} {sample_data['artist'][i]} ", limit=5, type='track')**

然后,我必须验证五个回答中的哪一个是我首先想要的音轨。诚然,我第一次尝试这样做是一个彻底的失败。我当时年轻乐观,认为 Spotify 搜索结果的标题会与众包网站 Genius 从网上搜集的标题完全一致。他们没有。作为一名经验丰富的老手,我通过模糊(近似)字符串将 Spotify 结果的艺术家和曲目标题与sample_data数据帧中列出的艺术家和曲目标题进行匹配。下面是代码的简化版本:

**if fuzz.partial_ratio(Spotify_artist_name, sample_data_artist_name) > 90 and fuzz.partial_ratio(Spotify_track_title, sample_data_track_title) > 90:                    
track_ids.append(Spotify_track_id) #append track id** 

由于几乎不可能从 Youtube 视频的标题中区分艺术家和曲目名称,因此我们无法核实 Spotify 在注释工作流程中的结果。然而,这一工作流程产生了许多我们在其他情况下不会发现的额外样品,因此我发现其好处超过了潜在的成本。

关于模糊字符串匹配的简短说明

模糊字符串匹配包含一组量化两个字符串相似程度的方法。他们这样做的方法是计算使一个字符串与另一个字符串完全匹配所需的改变次数(想想替换一个字母,删除一个空格)。我将用一个例子来说明为什么我们需要这种技术:

如果你检查上面的sample_data数据框,你会看到“凯吉夫妇”的“天梦”在 Spotify 上,这支乐队的名字是“凯吉斯”艰难。显然,如果我们对艺术家姓名进行精确匹配,我们不会在播放列表中找到这个正确的样本。但是,通过使用fuzzywuzzy函数partial_ratio(),它是一个模糊字符串匹配算法的实现,我们得到:

**fuzz.partial_ratio("The Kay-Gees", "The Kay Gees")
100**

由于一些差异比这更具戏剧性,我将艺术家和曲目标题字符串的匹配阈值都设置为 90,但请随意试验您自己的!

将曲目添加到播放列表

幸运的是,将曲目添加到播放列表就像提交您想要的曲目的曲目 id 列表一样简单,这正是上面的GetTrackIDs()函数的输出。我们需要的唯一附加信息是我们之前创建的播放列表的playlist_id:****

现在我们将曲目添加到播放列表中:

**sp.user_playlist_add_tracks(username, playlist_id, track_ids)**

瞧啊。我们在 Kanye West 的晚注册上有一个相当广泛的歌曲样本播放列表。唯一明显的错误是管弦乐版的《钻石恒久远》(Diamonds is Forever),在我看来,这是一个小小的代价,以确保我们包括纽约社区合唱团 1977 年的《自从你走进我的生活》,它为西部经典的旋律提供了核心的号角,“快克音乐

我希望你阅读这个演示的乐趣有我制作它的一半多,因为我玩得很开心。我的 GitHub 上有更多与音乐相关的项目。

使用 PyRFC 使用 Python 将数据插入 SAP

原文:https://towardsdatascience.com/using-python-to-insert-data-into-sap-with-pyrfc-5eb2ceeda79b?source=collection_archive---------9-----------------------

约翰·汤纳在 Unsplash 上拍摄的照片

如何用使用最多的语言将一个机器学习方法的结果插入到 SAP 中开发机器学习模型?

今天,我们看到机器学习(ML)开发的日益增长,最大的开发工具是 Python,每天都有越来越多的人试图将 ML 模型的结果返回到他们的企业资源规划系统(ERPs)中。

SAP 是最常用的 ERP 系统之一,要将 ML 模型的结果插入其中,我们有两种选择,一种是在自定义 SAP 事务中手动插入,另一种是使用远程函数调用(RFC ),这是 SAP 系统和其他系统之间通信的标准 SAP 接口,因此 RFC 调用要在远程系统中执行的函数。

假设您可以使用机器人流程自动化系统(RPA)自动上传事务中的结果文件,那么我们应该选择哪一个呢?

嗯,如果我必须选择其中一个,我会选择第二个选项,因为它往往比第一个变化更小,更新更少,所以你可能会有更少的问题与未被注意的更新。

需要指出的重要一点是,RFC 有许多目标和方法有待开发,我们想要使用的是将数据插入表格

我们可以使用 PyRFC 包用 Python 来实现,但是它的第一次使用并不那么简单,所以为了帮助我们,我创建了一些函数和文档。

我们开始吧!

装置

-第一步:下载 SAP NW RFC SDK

SAP NW RFC SDK 是 SAP NetWeaver 远程函数调用系统开发工具包的缩写,它允许您管理 RFC。

我从 Informatica of 找到了一本非常好的如何下载它的手册,但它是针对旧版本的,现在一些图标已经完全不同了,所以我写下了步骤并重印了图片以更新它。

如果想看 Informatica 的手册,文末有链接作为出处。

下载这些内容的一个困难是你需要一个 SAP 账户,所以在开始这个旅程之前,试着去申请一个。

回来,下载步骤:

来源:作家

来源:作家

来源:作家

  • 单击下载软件图标。

来源:作家

观察,一旦您登录,用户图标的颜色会变成蓝色

  • 单击支持包和修补程序选项卡。

来源:作家

  • 从列表中单击“按类别”。

来源:作家

  • 从列表中单击其他组件。

来源:作家

  • 从列表中单击 SAP NW RFC SDK。

来源:作家

  • 单击 SAP NW RFC SDK 的最新版本。

来源:作家

  • 选择要下载的操作系统

来源:作家

  • 单击超链接。zip 文件来下载库。

来源:作家

  • 提取中的文件和文件夹。压缩到您的 SAP 文件夹

来源:作家

突出显示的文件夹和文件是. zip 文件中的内容。

一旦你做到了这一点,一切都从 SAP 端设置好了。

-第二步:安装软件包

这比上一步简单一点,只需在 Python 终端上分别编写和运行这两行代码。

pip install pyrfcpip install pynwrfc

我想你可能只需要运行第二个,但是我已经安装了它们,我更愿意告诉你也安装它们,而不是你不能使用 PyRFC 包。

编码

酷,现在你已经到了有趣的部分!

Obs:如果您必须打开虚拟专用网络(VPN)才能访问 SAP,那么在使用这些功能时您也需要打开它。

对于这个开发,我们将有两个文件:常量。和 sap_rfc_connection .py

里面的常数。我们存储三样东西,带有 SAP 登录凭证的字典,RFC 函数和表名,以及每次 RFC 调用要插入的行数。

现在,在 sap_rfc_connection 中。py 我们有两个主要功能:

  • desc 功能区
def rfc_func_desc(dict_sap_con, func_name):
    '''consult the RFC description and needed input fields

    Parameters
    ----------
    dict_sap_con : dict
        key to create connection with SAP, must contain: user, passwd, ashost, sysnr, client
    func_name : str
        name of the function that you want to verify

    Returns
    -------
    funct_desc : pyrfc.pyrfc.FunctionDescription
        RFC functional description object
    '''
    print(f'{time.ctime()}, Start getting function description from RFC')
    print(f'{time.ctime()}, Start SAP connection')
    # create connection with SAP based on data inside the dict
    with Connection(**dict_sap_con) as conn:
        print(f'{time.ctime()}, SAP connection stablished')
        # get data from the desired RFC function
        funct_desc = conn.get_function_description(func_name)
        # display the information about the RFC to user
        display(funct_desc.parameters[0],funct_desc.parameters[0]['type_description'].fields)
        # return it as a variable
        return funct_desc
    # how the whole command is inside the 'with' when it ends the connection with SAP is closed
    print(f'{time.ctime()}, SAP connection closed')
    print(f'{time.ctime()}, End getting function description from RFC')

现在是它如何工作的一个例子,这个函数的主要目标是让你得到完整的 RFC 描述,并把它存储到一个变量中,这样你以后可以参考它(你也可以查看 PyDrive 文档,看看你可以用它做的所有事情),并向你显示使用 RFC 需要什么,比如字段名和数据类型。

来源:作家

来源:作家

这很有用,因为有了它们,我们就知道了数据框的结构/列名以及每个字段中必须使用的数据类型,以免出错。

在我的例子中,我创建了下面这个数据框来测试。

来源:作家

数据框创建后,我们可以继续第二个功能。

  • df_to_sap_rfc
def df_to_sap_rfc(df, dict_sap_con, func_name, rfc_table):
    '''ingest data that is in a data frame in SAP using a defined RFC, checking if the dataframe has the same size, column names and data types

    Parameters
    ----------
    df : pandas.DataFrame
        dataframe that is going to be used to insert data to SAP
    dict_sap_con : dict
        dictionary with SAP logon credentials (user, passwd, ashost, sysnr, client)
    func_name : string
        name of the RFC function
    rfc_table : string
        name of the rfc table you are going to populate

    Returns
    -------
    None
    '''
    # get needed parameters from RFC
    lst_param = get_rfc_parameters(dict_sap_con, func_name)
    # check dataframe input
    check_input_format(df, lst_param)
    # insert data
    lst_res = insert_df_in_sap_rfc(
        df, dict_sap_con, func_name, rfc_table)

这个函数包含了另外三个方法,但是为了不在它们上面浪费太多时间,前两个方法是检查数据帧是否符合 RFC 请求的格式(列名和数据类型),如果不符合,它就停止执行,以免将错误的数据插入 SAP。

真正的“神奇”发生在第三个函数中,insert_df_in_sap_rfc 函数:

def insert_df_in_sap_rfc(df, dict_sap_con, func_name, rfc_table):
    '''Ingest data that is in a data frame in SAP using a defined RFC

    Parameters
    ----------
    df : pandas.DataFrame
        dataframe that is going to be used to insert data to SAP
    dict_sap_con : dict
        dictionary with SAP logon credentials (user, passwd, ashost, sysnr, client)
    func_name : string
        name of the function that you want to remotelly call
    rfc_table : string
        name of the table which your RFC populates

    Returns
    -------
    lst_res : list
        list of dictionaries with field names and data types used in RFC
    '''
    print(f'{time.ctime()}, Start data ingestion to SAP process')
    # create an empty list that is going to recive the result
    lst_res = []
    # get the quantity of rows of the dataframe
    rows_qty = len(df)
    # define the number of execution, getting the entire part of the division and 
    # adding 1 to it, to execute the last rows that don't achieve the quantity of 
    # an extra execution
    iter_qty = (rows_qty // c.rows_per_exec) + 1
    print(f'{time.ctime()}, Start SAP connection')
    # create connection with SAP based on data inside the dict
    with Connection(**dict_sap_con) as conn:
        print(f'{time.ctime()}, SAP connection stablished')
        # for each iteration
        for i in range(iter_qty):
            # define the first and last row for this execution
            f_r = i*c.rows_per_exec
            l_r = min((i+1)*c.rows_per_exec, rows_qty)
            # define an auxiliar dataframe with only the rows of this iteration
            df_aux = df.iloc[f_r:l_r]
            print(f'{time.ctime()}, Rows defined')
            # convert this dataframe to a json format, oriented by records
            # this is the needed format to do a multirow input with a RFC
            # by last all the json data must be inside of a list
            lst_dicts_rows = eval(df_aux.to_json(orient='records'))
            # once we have the desired rows well formatted we must tell for
            # which table we are going to insert it
            dict_insert = {rfc_table: lst_dicts_rows}
            print(f'{time.ctime()}, RFC input format applied')
            print(f'{time.ctime()}, Start sending rows {f_r} to {l_r-1}')
            # with everything set just call the RFC by its name 
            # and pass the connection dict
            try:
                result = conn.call(func_name, **dict_insert)
                exec_ind = True
            except:
                result = None
                exec_ind = False
            print(f'{time.ctime()}, Rows {f_r} to {l_r-1} sent')
            # save the row's numbers, execution indicator and the result of the call in the list
            # as a dict
            lst_res.append({'row':f'{f_r}_{l_r-1}', 'exec_ind':exec_ind, 'rfc_result':result})
    # how the whole command is inside the 'with' when it ends the connection with SAP is closed
    print(f'{time.ctime()}, SAP connection closed')
    print(f'{time.ctime()}, End data ingestion to SAP process')
    return lst_res

长话短说,这段代码获取 constants.py 中定义的每次执行的行数,并计算将数据框完全插入 SAP 的迭代次数。

之后,它会将您的数据框分割成多个片段,将它们转换成一个面向记录格式的. json 文件,并将其全部放入一个列表中,最后,它会存储在一个字典中,该字典的关键字是 RFC 表名。

这种来来去去可能有点混乱,但是它创建了一个字典,允许我们一次向 SAP 表中插入多行。

最后,如果一切都设置好了,您将得到如下结果:

来源:作家

如果你对这些文件感兴趣,你可以通过下面的链接在我的 Git Hub 中看到它们:

[## rfpcordeiro/python-sap-RFC-connection

使用远程函数调用(RFC)更容易地使用 PyRFC 将数据插入 SAP 的函数 GitHub 是 over…

github.com](https://github.com/rfpcordeiro/python-sap-rfc-connection)

如果您正在考虑在一个必须定期重复的过程中使用 PyRFC,也许下面的这篇文章可以帮助您安排它。

[## 自动化并安排 Python 代码的执行

让您的代码按照您的计划工作

medium.com](https://medium.com/better-programming/automate-and-schedule-your-python-code-executions-22c3ed27794e)

这就是了!

嗯,有了这个,我想你现在可以使用 Python 和 RFC 更轻松地将数据插入 SAP 了!

希望这篇文章对你有帮助!

特别感谢莱昂纳多·拉莫斯和安德森·泰西托利,当我和他们一起解决了一百万个 RFC 问题的时候,他们忍受了我

来源:

https://kb . informatica . com/h2l/how to % 20 library/1/0953-howtodownloadandstallsapnetweaverrfcsdklibraries-h2l . pdf

https://python . hotexamples . com/examples/pyrfc/Connection/call/python-Connection-call-method-examples . html

https://sap.github.io/PyRFC/client.html#examples

https://sap . github . io/py RFC/install . html # sap-NW-RFC-SDK-installation

使用 Python 将股票数据可视化为蜡烛图

原文:https://towardsdatascience.com/using-python-to-visualize-stock-data-to-candlestick-charts-e1a5b08c8e9c?source=collection_archive---------5-----------------------

M. B. M.Unsplash 上拍摄的照片

烛台数据是一个非常重要的方式来显示数据在股票市场如何移动。有些人可能用它来观察股票价格。有些人可能还会添加颜色,以便更好地形象化。许多人还用它来绘制交易模式。它们也非常有用,因为它们不是显示一个股票价格,而是有四个不同的价格点。这些包括开盘价、收盘价、最高价和最低价。

图片由https://www.newtraderu.com/提供

概述代码

假设你以前有 Python 知识,我将在一个 Jupyter 笔记本中创建这一切。我将使用[pandas_datareader](http://pandas_datareader)从雅虎获取数据。然后,我将使用[plotly](https://plotly.com/)来绘制这些信息,将它们可视化到烛台上。所以一个简单的大纲应该是这样的:

  1. 导入必要的库
  2. 使用pandas_datareader从雅虎获取数据
  3. 将数据存储到数据帧中
  4. 将数据框与plotly烛台格式匹配
  5. 使用plotly将数据框中的数据可视化

创建代码

从 Jupyter 开始,我做所有必要的进口。

对于这个例子,我将使用微软作为我的股票。我将股票代码设置为一个变量,然后使用pandas_datareader从 Yahoo 获取信息并存储到一个变量中。它应该自动保存为 DataFrame 对象。对于日期,我只是把它设置为去年年初。

为了让plotly理解我们的数据,我们需要将它与正确的信息相匹配。他们把它变得简单,使用“痕迹”,把痕迹看作图形的选项。我们从数据帧中定义我们想要使用的内容,然后在选项中设置这些内容。

我们现在可以在plotly中设置图表布局。

然后我们现在可以展示可视化。它应该看起来像这样。请随意使用工具来改变它。

奖金

均线也可以作图!大多数交易者使用均线来观察股票的走向。有些人还使用不同的日期,在这些天里,如果 T2 交叉(当不同的移动平均线交叉时)发生,他们可以用它作为买入或卖出的信号。将它添加到我们的代码中非常简单。我们只是为每条均线创建单独的轨迹。我创建了一个 30 天均线的轨迹和一个 50 天均线的轨迹。

更新后的图表应该如下所示。

这是使用 Python 将股票绘制成烛台的简单方法。使用跟踪可以添加更多的数据。我希望这对刚开始学习 Python 并且对金融数据感兴趣的人有所帮助。此外,这还深入研究了数据可视化,这使得模式的识别更容易。

我其他一些涉及 Python 和 Robinhood 的文章:

[## 使用 Python 和 Robinhood 创建一个简单的低买高卖交易机器人

所以我最近一直在折腾 Robinhood,一直在努力理解股票。我不是财务顾问或…

towardsdatascience.com](/using-python-and-robinhood-to-create-a-simple-buy-low-sell-high-trading-bot-13f94fe93960) [## 使用 Python 和 Robinhood 构建一个钢铁秃鹰期权交易机器人

所以我最近发现了期权交易的潜在收益。

towardsdatascience.com](/using-python-and-robinhood-to-build-an-iron-condor-options-trading-bot-4a16e29649b0)

以上代码可以在这里找到,请随时关注我的交易之旅这里

我在这里也有家教和职业指导!

如果你们有任何问题、评论或顾虑,请不要忘记通过 LinkedIn与我联系!

posted @ 2024-10-16 08:59  绝不原创的飞龙  阅读(125)  评论(0)    收藏  举报