TowardsDataScience-博客中文翻译-2020-九十一-
TowardsDataScience 博客中文翻译 2020(九十一)
帕特里克 vs 斯奎沃德:用合成数据训练投票检测人工智能
我们训练了一个 CV 模型,仅使用合成数据来检测投票,以实现更加可靠和安全的投票。
帕特里克·波斯特风格转移艺术(图片由作者提供)
投票问题
现在是 2020 年 11 月,美国正在投票。
人们在讨论投票过程,而且并不赞成1。随着邮寄投票可能达到历史最高数字,我们的投票系统受到了更多的审查。这些担忧的来源集中在当前最先进的投票方法的准确性和速度上。这导致了选民对投票技术的不信任:即使你去投票,你的选票是否被正确计算?
我们决定解决光学扫描选票带来的问题。光学扫描纸质选票系统仍被广泛用于统计选票(见下图 1)。不幸的是,就目前的情况来看,光学扫描选票是出了名的不可靠。当其中一台机器出现故障时,将使用人工来验证结果(即有人查看)。
图 1:1980 年和 2016 年美国投票技术的变化(来源:www.electiondataservices.com)
我们的想法
我们提出的解决问题的想法是:一个简单的 CV(计算机视觉)模型来计算光学扫描选票。在 Zumo 实验室,我们正在创建合成数据来训练下一代 CV 模型,因此我们一直在寻找 CV 模型的酷用例。在理想的世界里,人类会检查每一张选票。但这并不可行。历史上,视觉计算系统一直被要求将技术提高到类似人类的精确度。
作为工程师,我们相信系统中的故障保险越多,系统设计就越好。该系统可以与现有系统一起使用,甚至可以取代现有系统。
计算光学扫描选票是一个很好的用例。模型不应受到烧蚀或光学扫描仪校准问题的影响。更重要的是:我们训练的模型可以部署在任何带有 RGB 摄像头的设备上,因此不需要昂贵的定制硬件。
可靠、安全的投票是可能的,下面是我们是如何做到的。
实验
在这个实验中,我们模拟了比基尼岛的一次选举,章鱼哥与现任的帕特里克·斯达进行了较量。
(请注意,在本文中,我们的目的只是展示这种系统可能拥有的能力,而不是完全封闭完整系统的外观。)
对于这个模型,我们决定使用一个带有雷斯内特-FPN 主干的屏蔽 R-CNN 。该模型配置来自脸书研究中心提供的 Detectron2,它为流行的架构提供了示例配置3。我们从头开始训练这个模型,以执行 Patrick 和 Squidward 两个类别的实例分割和包围盒检测。我们在合成数据上充分训练了这个模型,为每个填写的选票提供了边界框和分段掩码。
图 2:我们的综合训练模型在真实图像上的检测
数据
我们使用的数据集大致分为两半,一半是 Squidward 的投票,另一半是 Patrick 的投票。请注意,在本文中,我们没有将重复投票或无效投票考虑在内。
图 3:(左)实像(中)预测(右)合成像
你可能会注意到背景不同的视觉外观:这是一种叫做域随机化的技术。域随机化允许模型忽略背景,专注于重要的事情(即投票)。
我们的数据是使用流行的 Blender 软件和一些内部工具的组合生成的,我们希望在未来开源这些工具(注意!).该数据集可在我们的网站 app.zumolabs.ai 上获得。
模型训练:失败与学习
让我们回到我们的海底村庄,我们实验的试验场。比基尼岛的选举开始后,投票委员会开始意识到选民们还不明白他们应该如何投票。不知情的选民不明白他们应该填充气泡,并决定在选票上画圈。
图 4:比基尼岛的投票者正在围着盒子打转!
回实验室去!我们当前的模型(上面训练的那个)不能检测这些圆,因为它只在填充的盒子上训练。简单快捷的解决方案:我们能够在创纪录的时间内生成一批新的合成数据,涵盖这个案例。
这是合成数据的巨大优势之一:一旦检测到边缘情况,就可以轻松生成数据集并训练模型来解决边缘情况。下面你可以看到改进的模型能够检测不正确的格式,显示了合成数据在帮助解决你尚未遇到的问题方面的力量。
图 5:我们根据添加的合成数据训练的模型现在可以识别投票者的选择
需要考虑的事项:隐私
选民希望他们的投票是秘密和安全的。在我们生活的世界里,人们对自己的隐私和数字足迹更加关注。许多 CV 系统使用以前收集的数据进行训练,这通常会引起许多隐私问题,因为最终用户和公司通常不知道后果和问题。
我们在 100%合成数据上训练上述模型。这意味着不需要记录投票,因此模型可以根据收集的真实数据进行训练。我们相信 CV 模型不仅可以使计票过程更加可靠,而且当 CV 模型在 100%合成数据上训练时,CV 模型还可以确保秘密和安全的投票。
关闭思路
我们希望现在我们已经建立了一些信任,关于计算机视觉模型如何被用作投票过程中的一个组成部分。最重要的是,我们希望展示合成数据如何让我们训练这些模型来适应不断变化的条件(快速捕捉所有边缘情况!)并把所有隐私问题抛到脑后。
如果你有自己的理论,或者你想用一个模型来测试你自己的直觉,但缺乏训练数据,或者如果你不同意我们关于投票过程的想法和结论,请通过 norman@zumolabs.ai 告诉我!很乐意聊天。
感谢您的阅读,并在今年 11 月通过投票发出您的声音![4]
参考资料:
1帕姆·费斯勒。"为解决(选票)问题而战:解决缺席选票问题的竞赛."(2020 年 10 月),NPR。(https://www . NPR . org/2020/10/19/924705412/race-for-a-ballot-cure-the-scramble-to-fix-minor-absent-ballot-problems)。
[2]投票技术。麻省理工学院选举实验室。(https://electionlab.mit.edu/research/voting-technology)。
3检测器 2。(https://github.com/facebookresearch/detectron2)。
[4]去投票吧!(https://www.vote.org)。
注意你的言辞!
乔恩·雅克的插图
重要的数据定义从出错的项目中吸取的教训
在当今世界,我们都希望有好的数据来工作和指导我们,但事实是,好的数据很难得到。这需要大量的思考——我们用语言思考——所以如果你真的停下来,专注于你用来定义数据的语言,你就有更大的成功几率创造出好的数据。
下面,我讲述了一些公司的简短故事,当他们的设计师忽略了他们思维中重要而微妙的错误时,这些公司遭受了巨大的损失。事实是,任何人都很容易犯这种错误,因为我们每天都在本能地使用语言:语言是如此自然,我们根本没有考虑过它的使用。通过阅读这些故事并思考它们的教训,希望你会受到启发,不时地仔细检查你公司的语言使用,以避免类似的痛苦。
在我们陷入他们的噩梦之前,尽管,让我们来一次短暂的旅行,回到一个更加和平的时代。
"今天早上你在湖边看到了多少只羚羊?"格罗克询问道。
“七个,”爱斯梅拉达沮丧地回答。
“嗯。我想知道其他人在哪里?”格罗克正在为洞壁设计一种新的装饰品。他应该以某种方式纳入这一最新趋势吗?他也很担心。他还记得每天早上大批羚羊来到湖边的时候。他皱起了眉头,因为他没有记录几年前有多少人来过——太多了,确切的数字似乎并不重要。
在我们继续之前,你有没有注意到一些令人惊奇的事情?在我们的故事中没有一个计算机或技术专家!如今有一种普遍的误解,认为数据与技术有关,但事实并非如此。数据一直伴随着我们,使用数据是人类思维的基础。我们都做数据。这是每个人的事。
本质
上面 Grok 和 Esmeralda 的短片的美妙之处在于,它向我们展示了数据分析是多么简单和自然。它通常是这样进展的:
- 有一些我们感兴趣的东西
- 与一个相关的问题 ,导致
- 提问 ,这引起我们去寻求
- 回答 (这就是数据进来的地方),最后,
- 考虑如何最好地记录和讲述我们所学的 故事
这太简单了,甚至连希腊人都能做到!所有五种元素的共同点?思维——想法——用语言表达。
在你得到数据之前,你必须浏览大量的语言。稍后再讨论数据?更多的语言。
在技术的历史进程中,数据被集中到技术中,因为数据处理开始用计算机处理数据,但是增加计算机从未改变数据本身的语言基础。如果你想精通数据,你需要精通你的语言。
词语真的很重要
没有单词的数字是没有意义的。42,有人吗?有些话,没有上下文,也基本没有意义。物。那里,你感觉好些了,对吗?你很清楚我在说什么。
我们都多次看到数据上下文和含义问题,最近我们上了一堂大师课,因为我们看到了全球疫情的展开。考虑一下美国疫情早期的这篇文章: 官方冠状病毒数字不对,大家都知道 。文章讨论了可用数字的差异和限制,并声明这些数据是不可信的,因为用于收集它们的过程缺乏统一的思想。它详细描述了疾病预防控制中心如何只关注旅行者的测试,而不是更广泛的测试。核心问题是,疾病预防控制中心关于谁需要接受测试以获得所寻求的答案的概念或定义考虑不周,定义过于狭隘。他们没有对我们面临的问题进行足够深入的思考,无法提出正确的问题,因此收集到的数据集很差。讨论的问题都不是技术问题,都是思想/概念/定义问题。
在大多数日常对话中,快速或非正式的定义不会引起大问题,因为我们通常可以通过稍后一点额外的对话来纠正误解。“哦,你在数有红点的蛋!明白了。”然而,当我们应对国际危机,或根据收集到的数据计划经营一项业务时,我们根本无法承受即兴操作。精确的、深思熟虑的词语很重要。
所以,尽量避免这些错误
没有什么比错误更有教育意义,所以让我们从别人的痛苦中提取一些教训。
谁是你的客户?
1999 年,在新宽带技术的推动下,美国的互联网发展迅猛。一个早期的创业公司正在全国范围内推广它的网络,在最后一英里使用 DSL 以最快的速度连接家庭和企业。作为一名第一年的新员工,我来到公司管理数据团队,当时顾问们正在推广最初的数据系统。
高管们向顾问们宣布了他们简化的战略:为了快速进入 T2,公司将只向 T4 的互联网服务提供商销售产品,依靠他们作为中间人来引入我们的最终用户。“我们唯一的客户类型是互联网服务提供商,因此要相应地建立客户关系管理和网络订购系统。我们将成为批发商。我们永远不会有任何直接客户。”所以开发团队放弃了传统的、更广泛的客户定义,即从你的公司购买东西的任何人,并在任何地方编程。系统中没有任何个人可以直接从我们这里购买的条款。你大概能猜到这将走向何方。
不到一年,随着全国网络接近全面部署,该公司发现它消耗了太多的现金,需要加快收入增长来抵消烧钱速度:我们只是因为 ISP 不能足够快地加入进来而损失了太多的钱。领导层决定公司必须转向并增加直接面向零售客户的销售。然而,为了实现这一点,所有的客户和订单处理系统都必须进行认真的检查,花费大量的费用和时间。
第一课:尽可能坚持传统的、普通的定义。有许多理由证明时间证明的定义,如客户,存在。选择错误的数据定义会严重限制业务的灵活性和速度。
第二课:如果你为常用词创建自己的自定义定义,你就有可能在以后引起意外的误解。例如:当你与陌生人合作时,无论何时你使用“客户”这个词,你都只意味着“ISP”
账户不是客户
不久之后,我接受了一个大型知名科技公司初创部门的任务。他们正在进入一个新的市场,并一直在为该企业构建系统。在我工作的前几周,我注意到项目参与者互换使用“Account”和“Customer”这两个词,作为彼此的同义词,我开始意识到他们已经实现了他们的订单跟踪系统,好像这两个词是相同的。我有点惊讶,一个成熟的科技公司的开发团队竟然没有抓住这种区别,因为他们是经验丰富的开发人员,但这表明,如果不小心,即使是专家也很容易陷入微妙的词汇陷阱。
我假设在他们早期规划时考虑的示例场景中,他们的客户只有一个帐户。现在,当他们到达系统和集成测试时,他们开始遇到真正的问题。“对于希望在每个州都有独立账户的大型多州客户,我们该怎么办?他们仍然是同一个客户,我们想把他们的所有收入都记在一家公司名下,我们该如何处理呢?”这个系统的构思和设计方式,他们不能。
第三课:如果你在交替使用多个同义词,请注意!你很可能有不同的想法,而不是一个想法。彻底探究这些同义词的细微差别。
什么时候杆子不是杆子?
在一家地区电力公司的任务中,我被要求审查他们的资产管理系统,该系统已经发展了多年,当然,已经实现了一项在计算机化(电力由电线杆传输)出现之前就已创建的业务的自动化。
当他们构建他们的系统时,他们创建了一个电杆数据库表,在该表中,他们逻辑地记录了他们在现场安装的电杆。一切都很好,除了,不幸的是,设计者从来没有把这根柱子从它被植入地面的地理位置中分离出来。
结果呢?不仅用电杆编号标识电杆,还在工作指令、现场地图等上标识电杆的安装位置。电线杆和它的位置被认为是同一个东西,即使稍微思考一下,很明显它们是不一样的,就像你不是你家的街道地址(即使你现在整天在家工作!).
这个错误在许多方面引起了问题。例如,为了更换 Pole 2000,他们需要派遣一个工作团队到 Pole 的现场安装第二个“Pole 2000”,嗯…我们如何将它放入系统中?—然后尝试发出工作指令,将电力线从(现有)杆 2000 移动到(新)杆 2000。该系统根本无法处理这些问题,因为它只允许在一个位置使用一个电极,因此他们必须使用外部纸质记录和离线手动处理来管理更换。
第四课:事物和它们的位置经常会被误认为是对方。为了方便起见,我们通常这样称呼一些东西——“你左眼上方的痣”或“IP 地址为 10.1.127.10 的服务器”——但它并不使事物及其位置完全相同。如果你检查你的对象的整个生命周期和它的位置,你可能会发现它们分离的重要地方。
关于上面的教训,有一点要注意:这也是今天互联网中一个非常糟糕的问题的根源,IP 地址既作为设备的网络位置又作为其身份。因此,即使是最早的、真正聪明的互联网奇才也会被这种微妙之处绊倒。事实证明,知识产权问题可能是如今黑客可以用虚拟匿名攻击我们所有人的最大原因——解决这个问题的成本将是巨大的。很明显,一些前期的概念错误,如果没有被发现,会对以后产生巨大的后果!(了解更多关于 IP 的问题。)
不要和人类打架:你会输的
我的一个朋友是一家大型跨国保险公司数据仓库项目的高级架构师。他们花费了数年时间和数百万美元在所有地区系统之间建立数据转换“管道”,将数据带入他们希望成为一个巨大的新数据仓库。他们很兴奋能让一切正常工作,并看到数据开始流动…直到他们深入研究结果。
结果发现:不同地区的员工出于完全不同的目的使用类似名称的字段。虽然对于设计团队来说,数据看起来是一致的、同质的(定义看起来也是一致的),但在实际使用中,数据是相当不一致的。在许多情况下,他们发现系统的用户不知道(或选择忽略)官方定义。当数据最终流入仓库时,每个人都发现它根本没有讲述一个可靠的故事,也不能用于制定业务决策。在花费了数年和数百万之后,结果是一场虚拟的灾难。
第五课:即使你写了很棒的数据定义,也不要指望用户会阅读或遵守它们。他们会看到字段的名称,并输入他们认为属于的任何内容,有时他们甚至会故意输入不适当的数据,如果这有助于他们完成今天的工作。在您依赖任何用户提供的数据之前,请确保它通过了良好的验证和健全性检查。
给商业领袖的最后提示
当你读这篇文章时,除了上面的教训,请带走这两个额外的想法:
- 数据永远不会超越我们任何人,它只是语言的巧妙运用。深入参与,推动贵公司数据的发展。未来的公司将需要一个由高层领导的强有力的数据战略。
- 即使在你完全参与之后,如果你让有经验的人帮助你领导你的数据管理,你的公司可能会做得更好,正如我们所见,没有什么比错误更能教会你!在你面试的时候,寻找一个在职业生涯中犯过和/或解决过关键数据错误的人,并自由地谈论他们学到了什么。像任何经验丰富、有思想的设计师一样,优秀的数据人员有很多故事,关于他们下次会做得不同的事情!我个人不会雇用任何首席数据官、数据架构师或数据科学家,他们不乐意告诉你他们犯过的许多错误以及他们从中吸取的教训。
1我的同事,
Jon Jacquet 2020 PNDLM 专门为本文创作的插图|经许可使用
【2】什么?你认不出“生命、宇宙和一切的答案”,正如道格拉斯·亚当斯在银河系漫游指南中记录的那样?简单证明所有数字都需要一个好故事来解释它们!
3开始了解 IP 地址问题的一个好地方是第 4 节背景中的 rfc4423 。第三段指出:“IP 编号是两个名称空间的混淆,即主机网络接口的名称和位置的名称(‘混淆’是统计学中使用的一个术语,用于讨论合并为一个的指标,索引增加,但信息值损失)。”我加了粗体。不幸的是,这很难理解。我的翻译是:他们把两个想法融合在一起,在这个过程中失去了独立的意义和重要的相关能力!正如你不是你家的地址一样,设备也不是它的 IP 地址。
也许这方面最好的书是由波音公司解决该问题的主要设计师之一写的:理查德·h·潘恩,超越主机身份协议:结束我们所知的黑客行为 (2009)。他帮助波音公司开发并在书中描述的解决方案后来成为官方技术标准。上面提到的 RFC 是相关的标准文件之一。不幸的是,许多年后,这些标准在很大程度上被忽视了——毫无疑问,这让各地的黑客和间谍感到非常高兴。
动手 PCA 数据预处理系列。第一部分:缩放变压器
尝试使用 PCA 却卡在处理阶段?看看这个。
王思韵。树枝上毛茸茸的小鸟【水彩】(2020)。我们用低聚艺术来表示 PCA 的感觉,就是降维。但是 PCA 能做的不止这些。
系列介绍
在本系列中,我们将探索缩放数据和 PCA 的结合。我们希望看到,每当我们遇到新的数据集时,我们如何才能更好地为机器学习任务准备数据。旅程由三部分组成。
- 第一部分:定标器和 PCA
- 第二部分:认识离群值
- 第三部分:分类数据编码
我们将在这篇文章中做什么
- 简要回顾定标器和 PCA 的背景
- 介绍要处理的数据集和任务
- 对数据集执行缩放变换
- 对缩放变换后的数据集进行主成分分析并评估性能
你将学到什么
- 理解定标器的重要性及其与 PCA 的密切关系
- 明智选择定标器
- 使相关和漂亮的可视化:)
在你开始阅读之前,我们明确建议你先玩一下笔记本(在 Colab 和 Github 上都有。请在这篇文章的末尾找到链接。)
定标器和 PCA
众所周知,PCA 对数据集的规模很敏感,因此将每个要素调整到合理的规模对 PCA 至关重要。在这一部分,我们将了解不同的定标器如何影响 PCA 结果。
这篇文章的灵感来自 Scikit-Learn 文档中的这个教程。您将看到我们使用相同的葡萄酒数据集测试不同的定标器,并将不同定标的数据传递到 PCA 步骤。这款小玩具展示了 scaler + PCA 组合的强大功能,也许你想把它放在自己的工具箱里。
主成分分析简介
在深入了解更多细节之前,为了让我们的旅程更加完整,我们想先简单介绍一下 PCA。已经理解了后面的数学知识的读者可以直接跳到下一部分。
术语警告
PCA 是“主成分分析”的缩写,其主要功能之一是降低数据集的维度(列)。并且是通过线性代数的奇异值分解 (SVD)来完成的。直观上,数据是以矩阵的形式出现的,矩阵的列是高维空间的轴。用这样的轴表示的数据点有时对于机器学习模型来说很难探索。SVD 所做的是找到数据集的更智能的表示(通过原始轴的线性组合)。新的抽象轴表示的数据更加模型友好。
再多说几句:新的一组轴是相互正交的(甚至更好,正交)。这个属性对于基于树的模型来说尤其可爱。新的一组轴按照它们表示数据集信息或可变性的能力排序
数据缩放器
总体思路
我们为什么需要定标器?想象一个房价数据集,包含房间数量、面积和单个公寓的价格等特征。这三列的可变性有很大的差异:房间数的最大差异很难超过 10 个房间,即数量级为 1;公寓面积的最大差异可以是 100 平方英尺的数量级;价格的差异很容易达到 10K 订单。
scaler 和 PCA 有什么关系?如果我们要对这样一个列的可变性按顺序不同的数据集应用 PCA,那些相对“平滑”的特征的效果将被最激烈的特征(本例中的价格)完全淹没。为了解决这个问题,我们对数据进行了缩放。
SciKit-Learn 中的一行代码提供了许多不同的定标器,如最常用的标准定标器和最小-最大定标器,以及其他非线性定标器。在这篇博文中,我们感兴趣的是在应用 PCA 之前测试所有这些可用的定标器,并看看它们如何与 PCA 一起工作。如果你有兴趣,这里有一个详细的演示在这篇文章中测试的所有定标器。
开始测试吧!
让我们开始吧。首先,我们需要一些准备代码。我们将如下使用葡萄酒数据。
葡萄酒数据集 是一个图例数据集。这些数据是对意大利同一地区三个不同种植者种植的葡萄酒进行化学分析的结果。对于三种类型(产地)的葡萄酒中发现的不同成分,有十三种不同的测量方法。数据集仅包含数字要素。我们的目标是使用 13 种不同的测量方法,找出目标标签(原点)。首先,我们来快速看一下数据。
# note: the code in this post is mainly for illustration purpose. More details, please refer to the original notebook. link at the end of the blog.df_wine.head()
df_wine.describe()
我们诚挚地邀请您注意特征可变性顺序的差异:“非类黄酮 _ 酚类”的标准偏差为 0.4,而“脯氨酸”的标准偏差为 746。我们将在后面看到,如果不对数据集进行任何缩放,PCA 的结果将会因那些具有巨大差异的特征而产生很大偏差。
关于任务
数据集的目标列包含三个标签,因此它可以是一个多类分类任务。在这里,我们通过屏蔽不同数量的真实标签来模拟半监督和无监督的学习情况。我们做这个额外步骤的原因很快就会清楚了。让我们首先将数据可视化,以便获得一些见解。
探索性数据分析
数据非常干净。所有的特征都是数字变量。所以我们没有任何繁琐的数据清理工作要做。使用 seaborn 的 pairplot 和 box plot 可以很好地揭示数据集。
fig, axes = plt.subplots(nrows=1, ncols=len(df_features.columns), figsize=(len(df_features.columns)*2,4))
for index, col in enumerate(df_features.columns):
ax = axes[index]
sns.boxplot(y=df_wine.loc[:,col], x=df_wine.loc[:,'target_original'], palette=customer_palette, ax=ax)
ax.set_ylabel(ax.get_ylabel(), fontsize='x-large')
ax.set_xlabel("")plt.tight_layout(pad=0.5)
plt.show()
(提示:在单独的窗口中打开以获得更好的视图)
g = sns.pairplot(data=df_wine,
vars=df_features.columns,
hue='target_original',
corner=True, palette=customer_palette,
hue_order=target_order)
(提示:在单独的窗口中打开以获得更好的视图)
# target_large_missinng
g = sns.pairplot(data=df_wine,
vars=df_features.columns,
hue='target_large_missinng',
corner=True,
palette=customer_palette,
hue_order=target_order)
(提示:在单独的窗口中打开以获得更好的视图)
# target_all_missinng
g = sns.pairplot(data=df_wine,
vars=df_features.columns,
hue='target_all_missinng',
corner=True,
palette=customer_palette,
hue_order=target_order)
(提示:在单独的窗口中打开以获得更好的视图)
您可能想知道为什么用不同的颜色将相同的数据可视化三次。
思考时间
我们要求你在三种情况下区分三种不同的目标类别。
- 场景 1:使用颜色辅助(第一对图)
- 场景 2:使用弱颜色辅助(第二对图)
- 场景三:没有颜色辅助(最后一对图)。
这里的要点是,在没有目标标签或只有有限数量的标签的情况下,人眼区分不同的目标类别变得非常困难(注意:没有目标标签的场景类似于无监督学习的情况;标签数量有限的情况类似于半监督学习情况)。
虽然人类可能看不到特征之间更高维的交互,但机器学习模型可能会发现一些模式。然而,为什么不尝试以一种更智能的方式来表示数据呢?例如,在 PCA 之后加上一个适当的定标器。
比较不同的缩放器
第一眼
在这里,我们将使用 SciKit-Learn 的预处理子类中所有八种不同的缩放器来转换数据集。我们没有太注意调整定标器的超参数。我们绘制变换数据集的 PCA 的前两个分量的散点图,始终保持逐渐减少颜色辅助的精神。
# customer visualization function
# show scatter of 1st and 2nd PCA component
pca_scatter_plot(X_pca_dict, y_train.iloc[:,1:])
不要迷失在大量的散点图中。让我们仔细看看那些有助色的。我们的结论是,有了这个数据集,无论我们选择哪种定标器,都最好对数据进行定标。但是有一个例外:标准化器。规格化器以一种奇怪的方式缩放数据:按行而不是按列。我们不确定这种定标器何时有用,但我们很乐意与您讨论何时使用它。
如果您想知道为什么缩放的数据集比未缩放的数据更容易区分每个类。我们很高兴你问了。原因是缩放后的变量可以同等比较。相反,机器学习模型所看到的未缩放特征的重要性受其缩放比例的影响很大。
思考时间
这里有一个小练习,供有 PCA 数学知识的读者参考。不同类别的集群并不总是以相同的顺序出现,比如从左到右是红色、蓝色和绿色;它可以是绿色、蓝色和红色。为什么会出现这样的排列现象?
让我们继续深入研究 PCA 对不同规模数据集的作用。以下是 PCA 后每个特征的箱线图,不同的定标器按行区分。
# customer visualization function show PCA components
pca_box_plot(X_pca_dict, df_target.loc[:,'target_original'])
(提示:在单独的窗口中打开以获得更好的视图)
同样,在没有任何转换的情况下,几乎所有有趣的信息都集中在第一个组件上,给空间的其余部分留下很少的变化。我们认为第一个成分中最重要的成分是原始数据中的“脯氨酸”。实际上,这些框的行为几乎与使用上述原始特征空间为“脯氨酸”绘制的框的行为相同。
如果我们看看其他缩放器(除了规格化器),我们可以看到当从第一个分量(即,最有信息的分量)到最后一个分量(即,最没有信息的分量)时,盒子反弹越来越少的趋势。更有趣的是,几乎所有的定标器都会对第三个分量产生截止效应。在这个阶段,我们可能有足够的信心,前两个组件是有代表性的。
我们可以更深入地研究一下协方差矩阵。因为 PCA 直接作用于协方差矩阵并输出主成分。这是故事的开头。
让我们通过 Seaborn 的热图函数来可视化协方差矩阵。
警告注释
一个重要提示:我们通常用热图来表示皮尔逊相关系数矩阵(CC 矩阵),但这两个矩阵有一个关键区别:一个协方差矩阵中的值不一定在(-1,1)之间,对角线上的值也不一定都是 1。这就是为什么我们使用下方的三角形热图来绘制对角线值。
trans_heat_plot_abs(X_trans_dict, y_axis_labels=df_features.columns)
此时,与散点图和/或盒状图相比,热图可能不是很直观,因为标签信息没有进入热图。此外,一些颜色条的精细渐变使得一些热图彼此非常相似。
然而,我们确实从这些热图中得到了一个线索,即矩阵如何适应 PCA。经验法则是,热图越丰富多彩,PCA 结果越好。通常,PCA 不喜欢看起来简单的热图,并且会输出不太有趣的主成分。在我们的例子中,由于“脯氨酸”是最突出的,PCA 将会关注它。这和我们之前的分析是一致的。另一个简单的方法是归一化转换数据。根据热图,我们可能会猜测五氯苯甲醚中的一种成分主要来自“镁”;因此,标准化器+ PCA 给出了沉闷的结果。
# plot the explained_variance cumsum using previously calculated PCA instances.
pca_plot_cumsum(instance_pca_dict)
聚类算法如何喜欢我们处理过的矩阵?
通过观察由 PCA 发现的特征空间的新方面的前两个维度,我们或多或少对预处理结果感到满意。然而,能够看到更高维度相互作用的聚类算法会像我们一样偏爱相同的变换矩阵吗?让我们使用机器学习聚类模型对数据进行聚类。
我们在标记数据集上执行聚类任务的一个优势是,我们知道正确的类数。接下来,我们将看到 k 均值与真实标签的吻合程度。
这里我们采用了博客的第一个度量标准: V-measure score 。我们不会详细讨论这个指数;你只需要知道:
V-measure 得分有界在[0,1]范围内:接近 0 的值是不好的,1.0 代表完全匹配。
df_scores
我们比较了三个场景:两个原始特征的聚类,两个信息量最大的主成分分析,以及整个特征集。我们选择了两个特征或组件来保持与之前散点图的一致性。虽然更多的列可能会更彻底地揭示数据集的信息,但我们认为至少从一开始,两个维度就足够了。
让我们仔细看看上表。
- 首先是,让我们关注最后一列,它包含使用整个数据集的 V-measure 得分。无论我们选择哪种缩放器,缩放数据总是更好。(除了“规格化器”)
- 其次,对于特征空间的原始方面,使用整个数据集总是更好。选择“似乎”能更好地说明聚类的特征子集似乎不是一个好主意。(只看第一列和第三列的对比)。
- 另一方面,对于 PCA 变换的数据,情况就不同了。有时,只使用几个组件比使用所有组件会产生更合理的集群。(看一下第二列和第三列的对比)。即使在某些测试中,前两个组成部分的选择并没有超过总体,但两个选择之间的 V-measure 得分非常接近。(因为这里我们比较的是 2 列矩阵和 13 列矩阵的计算来源。)
- 最后是,查看第二列,我们发现最适合这项任务的定标器是两个非线性定标器和标准定标器。因此,在直接应用最常用的标准定标器之前,您可能希望通过简单的测试来尝试更多的定标器。
我们想就 PCA 显示的少即是多现象多说几句。人们经常争论说,丢弃原始数据集中的信息是不明智的;这样做有时非常危险。然而,主成分分析揭示了数据矩阵中最重要的模式,同时过滤掉了一些细节。这些细节可能只是噪音。因此,采用前几个 PCA 组成部分不仅有助于减少条件性,还可以使信息更加顺畅,使事情更加清晰。
kmeans = KMeans(n_clusters=3, random_state=RANDOM_STATE)pca_cluster_contour_plot(X_pca_dict,y_train, kmeans)
摘要
在本帖中,我们通过展示散点图,比较了 PCA 作用于不同尺度数据的结果。从视觉上我们可以看到,这种组合确实比没有预处理的数据集的模式更清晰。在应用 PCA 之前,外卖将始终检查数据集中每个要素的方差,如果方差之间存在较大差距,则使用适当的缩放器缩放数据。根据你的任务,“适当”的定义可以有所不同。
下一次,我们将通过引入合成异常值和分类特征来扩展这种定标器+ PCA 组合的限制。
我希望你喜欢这篇文章。请随时留下评论。
声明:该系列由莫克菲和王思韵王思韵合作。第一次发布:2020 年 6 月 8 日。
笔记本链接:
[## blog-PCA-part I _ kefei _ 0608 . ipynb
合作笔记本
drive.google.com](https://drive.google.com/file/d/1UQ-f7e1ImdLX7mJuxY-pY1RWkXDwn-cc/view?usp=sharing) [## kefeimo/数据科学博客
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kefeimo/DataScienceBlog/blob/master/1. PCA/blog-PCA-part I_kefei_0608.ipynb)
用于冠军推荐的基于内容的 PCA 建模(英雄联盟)
注:以下所有代码可在这里找到。
之前我写了一篇文章关于我们如何在游戏《英雄联盟》中使用图形网络来帮助提供冠军推荐。这种技术被称为“用户-用户协同过滤”,我们利用我们所知道的关于一个人的信息来寻找相似的用户,然后根据我们所知道的他们喜欢什么来进行推荐。
为了帮助说明这一点,我们将使用经典的亚马逊例子。想象一下,你已经在你的亚马逊购物篮中添加了一款 PS4 和最新的 FIFA 游戏,该算法会查看之前一起购买了 PS4 和 FIFA 的所有用户,然后找到他们倾向于在他们的购物篮中添加哪些其他项目,即最新的 NFL 游戏 Madden,然后向你推荐。
今天,我们来看一种不同形式的推荐算法,称为“基于内容的模型”。相反,这种技术会根据物品的相似性将它们联系在一起,例如,如果你正在购买 EA 制作的 PS4 体育游戏,那么这里有一些 EA 制作的其他 PS4 体育游戏。当你没有关于用户偏好的信息时,比如刚刚发布产品时,这种技术是有利的。
然而,有将近 150 个 LoL 冠军,我们不想把所有的时间都花在给他们贴上我们需要的各种标签上。因此,相反,我们要做的是使用他们的游戏内统计数据来“描述”冠军,例如他们每场比赛的平均杀伤数或他们造成的客观伤害。
为此,我们可以分析 150,000 个钻石游戏。请注意,我将这一点仅限于顶级、中级和 ADC 玩家,只是考虑到他们的统计数据中固有的差异支持和 junglers(即小黄人的低黄金)。
对所有冠军的数据进行平均后,首先要注意的是许多统计数据之间有一些非常明显的相关性。“杀戮优先”和“杀戮”等属性几乎完全相关,这并不奇怪(前者表明玩家已经疯狂杀戮了多少次,后者是该游戏中总共杀戮了多少次)。
图表说明了如此大量的属性所产生的多重共线性问题。
处理这种程度的多重共线性的常用方法是排除(挑选 kills,删除 killing prees)或聚合(kills * killingSprees)。然而,有一个更好的解决方案,称为主成分分析(PCA),它能够提取这些属性之间的核心关系,而无需人工干预或删除潜在的关键驱动因素。
PCA 是一个相当复杂的主题,需要理解特征向量/值,并且有很多关于它的很好的文章,所以我在这里不赘述。相反,我会说,主成分分析试图做的是尽可能多地捕捉数据中的差异,同时尽量减少变量的使用。
每个成分解释的原始数据的方差百分比,总和为 100%。
在将 PCA 拟合到数据集之后,我们发现超过 30%的数据方差可以拟合到单个分量中,然后在第二个分量中找到超过 16%的方差,在第三个分量中找到大约 11%的方差,以此类推..
但是这些组件是什么呢?为了帮助理解它们是由什么组成的,它们来自哪里,请看下图,它说明了哪些变量是第一个组成部分的一部分。很明显,除了客观伤害、最大的多重杀戮、疯狂杀戮的数量、造成的伤害和总杀戮数之外,金钱是最大的贡献者。可以肯定地说,这个组件正在捕捉与跺脚车道相关的变量。如果我们加上“物理”伤害被指定的事实,你几乎可以看到菲奥拉/瑞文/Trynd one 的把戏出现在你的眼前。
说明哪些原始变量与第一个成分高度相关的图形。
第二个组成部分包含了两个主要属性:占领塔和伤害自缓解(格挡/格挡/免疫/减少等)..).然而,您可能会想这与基于内容的推荐模型有什么关系!嗯,我们现在有两个组成部分,包含了冠军之间超过 50%的差异。这些可以被认为是描述的代理,在那里我们有“杀死所有人的冠军”而不是“运动游戏”,并且“由 EA 生产”变成了“高炮塔伤害”!然后,我们可以在 2D 空间中绘制出这些描述性的组件,我们可以开始看到它们是如何组合在一起的(警告,大的旧图形向您展示):
前两个组件的 2D 表示,可用作推荐引擎的基础。冠军的颜色取决于他们的主要角色,但数据不一定是从那个位置的球员那里收集的。
注意:尽管“支持”冠军在这里以黄色显示,但数据实际上仅来自农场。也就是说,你在上面看到的基兰数据是冠军在顶级、中级或 APC 比赛时的数据。
那些关注的人会注意到组件 1 是颠倒的,在 X 轴上高伤害/死亡被记为低。组件 2 不反转,因此 Y 轴上的高数字表示炮塔获取和伤害减轻的批次。为了确保它像预期的那样工作,看看左上方的冠军(即,造成大量物理伤害,占领塔和减轻伤害);菲奥拉& Tryndamere (Trynd 的 ult 算作伤害减轻)。在底部中间我们可以看到卡塔琳娜和卡瑟斯,他们在伤害和杀戮上得分相对较高,但是不能击碎炮塔和减轻伤害。我觉得不错。
接下来的步骤很简单,推荐基于与他们当前比赛的冠军欧氏距离(直线)最短的冠军。你经常玩宝石骑士吗?试试毛凯。暗影之拳?汽水怎么样?不死的蒙多博士?你会喜欢我们的儿子西昂的。
如果我们想在这方面有所发展,我们就要向更高维度发展。如果你回到显示每个组件中有多少差异的图表,我会说有一个论点是基于 3 维,甚至 5 维来构建模型。其余的工作是一样的,但是考虑到观想变得棘手,我们现在就把它留在那里!
我希望这为潜在的推荐类型提供了另一种见解,可能值得探索,PCA 提供的好处,虽然我使用英雄联盟作为我的领域,但这些可以很容易地应用于任何其他领域。我建议回到大图,找到你的主要部分,看看你是否同意它周围的那些是类似的玩法——让我在下面的评论中知道!
你已经看到文章的结尾了!我叫 Jack J,是一名将人工智能应用于竞技游戏和电子竞技的专业数据科学家。我是 iTero 的创始人。GGT3和jung . GG。你可以在 Twitter 上关注我,加入 iTero Discord 或者给我发邮件 jack@itero.gg 。下一场见。
最初发表于:【https://itero.gg/blog】T21
PCA 清楚地解释了——何时、为什么、如何使用它以及特性的重要性:Python 指南
在这篇文章中,我解释了 PCA 是什么,何时为什么使用它,以及如何使用 scikit-learn 在 Python 中实现它。此外,我解释了如何在 PCA 分析后得到特征的重要性。
手工制作草图作者制作。
1.简介和背景
主成分分析 (PCA)是一种众所周知的无监督 维数 约简技术,通过原始变量(特征)的线性(线性 PCA)或非线性(核 PCA) 组合构造相关特征/变量。在本帖中,我们将只关注著名且广泛使用的线性 PCA 方法。
相关特征的构建是通过将相关变量线性转换成较少数量的不相关变量来实现的。这是通过使用协方差/相关矩阵的特征向量(也称为主分量(PCs))将(点积)原始数据投影到缩减的 PCA 空间来完成的。
结果 预计 数据实质上是原始数据的线性** 组合捕捉** 数据 ( Jolliffe 2002 )。
总之, PCA 是将数据的正交 变换成一系列不相关的数据,这些数据存在于缩减的 PCA 空间中,使得第一个分量解释数据中的最大差异,而每个后续分量解释的较少。
——我的邮件列表只需 5 秒:https://seralouk.medium.com/subscribe
-成为会员支持我:https://seralouk.medium.com/membership
2.何时/为何使用 PCA
- PCA 技术在处理多个 - 共线性存在于特征 / 变量之间的数据时特别有用。
- 当输入特征的尺寸较大(如变量较多)时,可使用 PCA。
- PCA 还可以用于去噪和数据 压缩。
如果你想在交互式路线图和活跃的学习社区的支持下自学数据科学,看看这个资源:https://aigents.co/learn
3.主成分分析方法的核心
设**X**
是包含形状为[n_samples, n_features]
的原始数据的矩阵。
简而言之, PCA 分析由以下步骤组成:
- 首先,
**X**
中存储的原始输入变量是 z 值,这样每个原始变量(**X**
列)的平均值和单位标准差为零。 - 下一步涉及协方差矩阵
**Cx= (1/n)X'X**
的构造和 特征分解 (在 z 得分数据的情况下,协方差等于相关矩阵,因为所有特征的标准偏差为 1)。 - 特征值然后按照代表数据中递减方差的递减顺序排序(特征值等于方差——我将在下面第 6 段使用 Python 来证明这一点)。
- 最后,原始 归一化 数据到约简 PCA 空间的投影(变换)通过乘以(点积)原始归一化数据与协方差矩阵即 PCs 的前导 特征向量得到。
- 新的减少的 PCA 空间最大化原数据的方差。为了可视化投影数据以及原始变量的贡献,在联合绘图中,我们可以使用双绘图。
4.有意义组件的最大数量
有意义的 成分有一个上 界,可以用 PCA 提取。这与协方差/相关性矩阵(**Cx**
)的 秩 有关。具有形状为[n_samples, n_features/n_variables]
的数据矩阵**X**
,则协方差 / 相关性矩阵将为[n_features, n_features]
,其中最大秩等于min(n_samples, n_features)
。
因此,我们可以在处拥有最 min(n_samples, n_features)
有意义的 PC 组件/维度归因于协方差/相关矩阵的最大值 秩 。
5.使用 scikit-learn 和 Iris 数据集的 Python 示例
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.decomposition import PCA
import pandas as pd
from sklearn.preprocessing import StandardScaler
plt.style.use('ggplot')# Load the data
iris = datasets.load_iris()
X = iris.data
y = iris.target# Z-score the features
scaler = StandardScaler()
scaler.fit(X)
X = scaler.transform(X)# The PCA model
pca = PCA(n_components=2) # estimate only 2 PCs
X_new = pca.fit_transform(X) # project the original data into the PCA space
让我们在 PCA 变换之前和变换之后绘制数据,并使用花 (y)
的相应类对每个点(样本)进行颜色编码。
fig, axes = plt.subplots(1,2)axes[0].scatter(X[:,0], X[:,1], c=y)
axes[0].set_xlabel('x1')
axes[0].set_ylabel('x2')
axes[0].set_title('Before PCA')axes[1].scatter(X_new[:,0], X_new[:,1], c=y)
axes[1].set_xlabel('PC1')
axes[1].set_ylabel('PC2')
axes[1].set_title('After PCA')plt.show()
上面的 PCA 输出代码。
我们可以看到在 PCA 空间中,方差是沿着PC1 PC2 最大化(解释了 73%的方差)和PC2(解释了 22%的方差)。两者加起来解释了 95%。
print(pca.explained_variance_ratio_)
# array([0.72962445, 0.22850762])
6.原协方差矩阵特征值等于约简空间方差的证明
数学公式和证明
假设存储在**X**
中的原始输入变量是 z 得分的,这样每个原始变量(**X**
列)具有零均值和单位标准偏差,我们有:
作者写的 Latex 代码。
上面的**Λ**
矩阵存储了原始空间/数据集的协方差矩阵的特征值。
使用 Python 验证
最大方差证明也可以通过估计约简 空间的协方差矩阵来看:
np.cov(X_new.T)array([[**2.93808505e+00**, 4.83198016e-16],
[4.83198016e-16, **9.20164904e-01**]])
我们观察到这些值(对角线上有方差)等于存储在pca.explained_variance_
中的协方差的实际 特征值:
pca.explained_variance_
array([2.93808505, 0.9201649 ])
7.特征重要性
每个特征的重要性由特征向量中对应值的量级来反映(量级越高,重要性越高)。
让我们找到最重要的特征:
print(abs( pca.components_ ))[[0.52106591 0.26934744 0.5804131 0.56485654]
[0.37741762 0.92329566 0.02449161 0.06694199]]
这里,pca.components_
具有形状[n_components, n_features]
因此,通过查看 PC1 (第一主成分)即第一行行
[[0.52106591 0.26934744 0.5804131 0.56485654]
我们可以得出结论,对于 PC1 来说,特性 1、3 和 4** 是最重要的。同样,我们可以说特征 2** 和那么 1 就是对于 PC2 来说最重要的。
综上所述,我们看特征向量的分量的绝对值对应于 k 最大特征值。在 sklearn 中,组件按解释的方差排序。 这些绝对值越大,特定特征对该主成分的贡献越大。
8.双地块
****双标图是在 PCA 分析后可视化一体化的最佳方式。
在 R 中有一个实现,但是在 python 中没有标准实现,所以我决定为此编写自己的函数:
def biplot(score, coeff , y):
'''
Author: Serafeim Loukas, serafeim.loukas@epfl.ch
Inputs:
score: the projected data
coeff: the eigenvectors (PCs)
y: the class labels
''' xs = score[:,0] # projection on PC1
ys = score[:,1] # projection on PC2
n = coeff.shape[0] # number of variables
plt.figure(figsize=(10,8), dpi=100)
classes = np.unique(y)
colors = ['g','r','y']
markers=['o','^','x']
for s,l in enumerate(classes):
plt.scatter(xs[y==l],ys[y==l], c = colors[s], marker=markers[s]) # color based on group
for i in range(n):
#plot as arrows the variable scores (each variable has a score for PC1 and one for PC2)
plt.arrow(0, 0, coeff[i,0], coeff[i,1], color = 'k', alpha = 0.9,linestyle = '-',linewidth = 1.5, overhang=0.2)
plt.text(coeff[i,0]* 1.15, coeff[i,1] * 1.15, "Var"+str(i+1), color = 'k', ha = 'center', va = 'center',fontsize=10)
plt.xlabel("PC{}".format(1), size=14)
plt.ylabel("PC{}".format(2), size=14)
limx= int(xs.max()) + 1
limy= int(ys.max()) + 1
plt.xlim([-limx,limx])
plt.ylim([-limy,limy])
plt.grid()
plt.tick_params(axis='both', which='both', labelsize=14)
调用函数(确保首先运行加载虹膜数据和执行 PCA 分析的初始代码块):
import matplotlib as mpl
mpl.rcParams.update(mpl.rcParamsDefault) # reset ggplot style# Call the biplot function for only the first 2 PCs
biplot(X_new[:,0:2], np.transpose(pca.components_[0:2, :]), y)
plt.show()
使用我的自定义函数的 PCA 双图。
我们可以再次从视觉上验证****a)方差最大化 b) 特征 1、3 和 4 是 PC1 最重要的。同样,功能 2** 和功能 1和功能 1 都是功能功能对于 PC2 最重要的。******
此外,指向相同 方向的箭头(变量/特征)表示它们所代表的变量之间的相关性,而指向与 方向相反的箭头表示它们所代表的变量之间的对比。
使用代码验证上述内容:
# **Var 3 and Var 4** are extremely **positively** correlated
np.corrcoef(X[:,2], X[:,3])[1,0]
0.9628654314027957# **Var 2and Var 3** are **negatively** correlated
np.corrcoef(X[:,1], X[:,2])[1,0]
-0.42844010433054014
那都是乡亲们!希望你喜欢这篇文章!
最新帖子
使用可从《先知脸书》公开获得的预测模型预测股票价格
towardsdatascience.com](/time-series-forecasting-predicting-stock-prices-using-facebooks-prophet-model-9ee1657132b5) [## 用新冠肺炎假设的例子解释 ROC 曲线:二分类和多分类…
在这篇文章中,我清楚地解释了什么是 ROC 曲线以及如何阅读它。我用一个新冠肺炎的例子来说明我的观点,我…
towardsdatascience.com](/roc-curve-explained-using-a-covid-19-hypothetical-example-binary-multi-class-classification-bab188ea869c) [## 支持向量机(SVM)解释清楚:分类问题的 python 教程…
在这篇文章中,我解释了支持向量机的核心,为什么以及如何使用它们。此外,我还展示了如何绘制支持…
towardsdatascience.com](/support-vector-machines-svm-clearly-explained-a-python-tutorial-for-classification-problems-29c539f3ad8) [## 关于 Python 中的最小-最大规范化,您需要知道的一切
在这篇文章中,我将解释什么是最小-最大缩放,什么时候使用它,以及如何使用 scikit 在 Python 中实现它
towardsdatascience.com](/everything-you-need-to-know-about-min-max-normalization-in-python-b79592732b79) [## Scikit-Learn 的标准定标器如何工作
在这篇文章中,我将解释为什么以及如何使用 scikit-learn 应用标准化
towardsdatascience.com](/how-and-why-to-standardize-your-data-996926c2c832)
敬请关注并支持这一努力
如果你喜欢并发现这篇文章有用,关注我吧!
有问题吗?把它们作为评论贴出来,我会尽快回复。
参考
1 Jolliffe,I. T. 主成分分析。纽约州纽约市:斯普林格出版社,2002 年。
https://en.wikipedia.org/wiki/Principal_component_analysis
https://stattrek.com/matrix-algebra/matrix-rank.aspx
和我联系
- LinkedIn:【https://www.linkedin.com/in/serafeim-loukas/
- https://www.researchgate.net/profile/Serafeim_Loukas研究之门:
- https://people.epfl.ch/serafeim.loukasEPFL简介:
- 堆栈 溢出:https://stackoverflow.com/users/5025009/seralouk
在 2 分钟内解释并实施 PCA
【警告】超级容易!
PCA 或主成分分析是最著名的降维算法之一。它的主要目的是减少给定数据的大小,根据需要保留尽可能多的信息。在本文中,我将简要解释 PCA 算法并实现它。我们走吧!
- 作为第一步,让我们准备数据。我将使用 Iris 数据集作为一个玩具示例。
2.现在,让我们将数据集居中,如下所示:
在代码中,它看起来就像这样(我不仅从相应的列中减去了每一列的平均值,还除以标准偏差以标准化数据)。
3.现在,我们应该找到数据的协方差矩阵。
协方差只是两个特征之间的线性相关性度量。因此,我们正在计算每对特征之间的线性相关性度量。
4.已知协方差矩阵,让我们找到它的特征分解(即找到它的特征值和相应的特征向量)
特征向量是一个向量,当应用线性变换时,它不会改变方向。在下图中,你可以看到两个红色的特征向量。
同样,每个特征向量有一个相应的特征值,它等于特征向量的大小。
让我们借助 numpy.linalg 模块来寻找特征分解。
从协方差矩阵中找到的特征值越大,对应的特征向量的方差就越大。因此,为了选择 k 个最佳分量,我们需要选择 k 个具有最大特征值的特征向量。
5.我现在将选择协方差矩阵的前 2 个特征值。
6.最后一步是将初始数据集投影到这两个向量所跨越的子空间上。
注意,得到的 x_reduced 值是数据集初始列的某种线性组合,这导致了方差最大的两个分量。
7.最后,让我们画出我们的 2 个分量,来检验结果。
希望,这篇短文对你有帮助。感谢您的阅读!
你可以在我的 网站 上查看其他帖子
用动态图形可视化解释主成分分析
PCA 和 Plotly 实用指南。
PCA(主成分分析)是一种无监督的学习算法,它在数据集中寻找特征之间的关系。它也被广泛用作监督学习算法的预处理步骤。
使用 PCA 的目的是通过找到尽可能解释数据集中差异的主成分来减少特征的数量。主成分是原始数据集特征的线性组合。
PCA 的优点是使用比原始数据集少得多的特征保留了原始数据集的大量差异。主成分是根据它们所代表的方差来排序的。
在这篇文章中,我们将首先实现一个 PCA 算法,然后用 Plotly 创建动态可视化来更清楚地解释 PCA 背后的思想。
这篇文章更实际一些。如果你想更深入地了解 PCA 实际上是如何工作的,这里有更详细的理论方面的帖子。
Plotly Python (plotly.py)是一个基于 plotly javascript (plotly.js)构建的开源绘图库,它提供了一个高级 API ( plotly express )和一个低级 API ( graph objects )来创建动态和交互式可视化。使用 plotly express,我们可以用很少的代码行创建一个很好的情节。另一方面,我们需要用图形对象编写更多的代码,但是对我们创建的内容有更多的控制。
让我们从导入相关的库开始:
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.datasets import load_breast_cancer
我将使用乳腺癌数据集,它包含 30 个特征和一个指示细胞是恶性还是良性的目标变量。为了简化可视化,我将随机选择 5 个特征。
dataset = load_breast_cancer()
X, y = load_breast_cancer(return_X_y = True)df = pd.DataFrame(X, columns=dataset.feature_names)
df = df.sample(n=5, axis=1)
df['target'] = ydf.head()
让我们创建一个 scatter_matrix 来显示特征对的散点图。我们得到了特征对在分离目标类方面有多成功的概述。
import plotly.express as pxfig = px.scatter_matrix(df, dimensions=df.columns[:-1], color='target', title='Scatter Matrix of Features', height=800)
有些特征更能区分目标阶层。
让我们应用主成分分析来用 2 个主成分表示这 5 个特征。
X = df.drop(['target'], axis=1)#Normalize
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_normalized = sc.fit_transform(X)#Principal components
pca = PCA(n_components=2)
components = pca.fit_transform(X_normalized)
PCA 类的一个属性是explained _ variance _ ratio _,顾名思义,它告诉我们每个主成分解释了总方差的多少。
pca.explained_variance_ratio_
array([0.69369623, 0.20978844])
原始数据集中 90%的方差由两个主成分解释。
让我们用主成分创建一个散布矩阵。
labels = {
str(i): f"PC {i+1} ({var:.1f}%)"
for i, var in enumerate(pca.explained_variance_ratio_ * 100)
}fig = px.scatter_matrix(components, labels=labels, dimensions=range(2), color=df['target'])fig.show()
Plotly 还提供了 3D 散点图,当我们有 3 个主要组成部分时,它会很有用。为了实验 3D 图,我们首先需要再次对数据集应用 PCA 以创建 3 个主成分。
pca = PCA(n_components=3)
components = pca.fit_transform(X_normalized)
我们现在可以创建一个 3D 散点图。
var = pca.explained_variance_ratio_.sum()fig = px.scatter_3d(components, x=0, y=1, z=2, color=df['target'],title=f'Total Explained Variance: {var}',
labels={'0':'PC1', '1':'PC2', '2':'PC3'})fig.show()
两个主成分的总解释方差为%90。当添加第三个主成分时,它增加了%7。
出于演示目的,我们使用了乳腺癌数据集的子集。随意使用整个集合,看看主成分解释了总方差的多少。
绘制主要成分将有助于深入了解数据集中的结构以及要素之间的关系。
感谢您的阅读。如果您有任何反馈,请告诉我。
一行代码中的 PCA。
复杂问题的简单解决方案!图片来自 Pixabay
利用主成分分析进行维数约简
在真实世界的数据中,有大量的要素,很难分析或可视化这些海量的数据。因此,我们在数据预处理阶段使用降维来丢弃冗余特征。
降维 是将数据投影到一个更低维度的空间,这样更容易对数据进行分析和可视化。然而,维度的减少需要在准确性(高维)和可解释性(低维)之间进行权衡。
但关键是保留最大方差特征,减少冗余特征。
PCA 代表 主成分分析 。以下是维基百科对五氯苯甲醚的描述。
给定二维、三维或更高维空间中的点的集合,可以将“最佳拟合”线定义为最小化从点到该线的平均平方距离的线。可以类似地从垂直于第一条线的方向中选择下一条最佳拟合线。重复这个过程产生一个正交 基,其中数据的不同维度是不相关的。这些基向量称为主成分,几个相关的程序主成分分析 ( PCA )。
从广义上讲,PCA 就是找出有效的成分(特征),丢弃多余的特征。于是,我们计算主成分来实现降维。
说到这里,首先,让我们理解 PCA 背后的数学原理,然后进入那行神奇的代码。
假设我们有一个数据集 X,有“n”个数据点和“d”个列/特征。我们想把这个 d 维转换成 d `,比 d 小很多。
PCA 降维背后的数学:
1。标准化数据:
- 数据标准化意味着使数据的均值为 0,标准差或方差为 1。
- 标准化有助于数据归入相同的范围,并且不受单位/度量的限制。
- 这可以使用 sklearn.preprocessing 轻松完成
2。计算数据的协方差矩阵:
- 协方差矩阵是通过基于每列对每个元素应用方差来形成的。
- 协方差矩阵是列数大小的正方形对称矩阵。
3。矩阵的特征分解:
- 特征向量:特征向量是当对其应用线性变换时,其方向保持不变的向量。
- 特征值:特征值是变换过程中使用的比例因子。
- 求协方差矩阵 s 的特征值和对应的特征向量。
- 对于大小为 dxd 的协方差矩阵,我们得到‘d’个特征值和‘d’个特征向量。
4。对协方差矩阵进行特征值分解
- 将 d '个特征向量中的每一个排列成向量 v 中的列。现在,向量 v 的形状是,dxd '
- 数据集 X(nxd)和 v (dxd
)的点积产生大小为 nxd
的降维数据集。让我们称之为 x`。
现在,数据集,X 个 n 个数据点和 d 个特征被简化为 X’个 n 个数据点和 d’个特征的数据集。
然而,降维对数据的保留几乎没有什么好处。
为了计算保留百分比,我们使用特征值。这里有 d 个特征向量,在这 d 个特征向量中,我们使用了 d 个特征向量。
这是一个简单但非常有用的公式,用于在降维后保留数据。
保留值的数学公式
到了实现部分,我们可以使用 python 来执行这些步骤。然而,sckit-learn 提供了一个以更简单的方式执行 PCA 的库。
本文使用的数据集是 Kaggle 著名的 MNIST 数字识别器 数据集。
数据描述:
- 数据文件包含手绘数字的灰度图像,从 0 到 9。
- 每幅图像高 28 像素,宽 28 像素,总共 784 像素。每个像素都有一个与之关联的像素值,表示该像素的亮度或暗度,数字越大表示越暗。该像素值是 0 到 255 之间的整数,包括 0 和 255。
- 训练数据集(train.csv)有 785 列。第一列称为“标签”,是用户绘制的数字。其余的列包含相关图像的像素值。
数据集中的每个像素列都有一个类似 pixelx 的名称,其中 x 是 0 到 783 之间的整数,包括 0 和 783。为了在图像上定位这个像素,假设我们将 x 分解为 x = i * 28 + j,其中 I 和 j 是 0 到 27 之间的整数,包括 0 和 27。那么 pixelx 位于 28×28 矩阵的第 I 行和第 j 列(由零索引)。
加载数据:
import pandas as pd
mnist_data = pd.read_csv("mnist.csv")
使用 Pandas 将 CSV 格式(逗号分隔值)的数据文件加载到数据框中。
mnist_data.shape
输出:(42000,785)
查看数据,我们发现有 42,000 个数据点和 785 个特征。
mnist_data.head()
输出:
head()对数据的输出
Head()给出了数据的前 5 个数据点。
第一列标签是目标变量。剩下的 784 列是特征。
target_variable = mnist_data["label"]
features_variable=mnist_data.drop("label",axis=1)
将标签列分配给 target_variable,其余列分配给 features_variable。
print(target_variable.shape)
print(features_variable.shape)
输出:(42000,)
(42000,784)
现在,让我们看看如何使用 PCA 来减少这个维数为 42000 个数据点和 784 个特征的特征变量。
利用 PCA 实现降维的分步实现:
- 数据标准化:
from sklearn.preprocessing import StandardScaler
standarized_data = StandardScaler().fit_transform(features_variable)
fit()执行数学运算,transform()将这些值设置为数据,并将数据转换为标准形式。
②。使用 sckit 的 PCA-learn:
Sckit-learn 提供了分解,通过它可以处理 PCA 的所有数学运算。
from sklearn import decomposition
pca = decomposition.PCA(n_components=2)
从 sckit-learn 导入分解并为 PCA()创建一个对象。这里 n_components 是我们期望降维后的特征数。
这是降低数据维度的一行代码:
pca_data = pca.fit_transform(standarized_data)
输入是标准化数据,输出应该是降维数据。
现在,让我们打印输出并查看减少的数据,
print(pca_data.shape)
print(pca_data)
输出:
使用 PCA 降维后的 MNIST 数据
观察:
- pca_data 中的每一列都是我们感兴趣的主要成分之一。
- 对于 42,000 个数据点,使用一行代码可以将特征的数量从 784 个减少到 2 个。
剩下的问题是,如何确定要保留的特征的数量。Scree 阴谋来拯救。
scree 图用于确定探索性因子分析(FA)中要保留的因子数或主成分分析(PCA)中要保留的主成分数。
import numpy as np
pca.n_components = 784
pca_data = pca.fit_transform(standarized_data)retention_value = pca.explained_variance_ / np.sum(pca.explained_variance_);
cumulative_retention = np.cumsum(retention_value)
这里,我们将 n_components 设置为 784,即数据点的总数,以便查看用于约束每个值的保留值。
标图标图
import matplotlib.pyplot as plt
plt.plot(cumulative_retention)
plt.grid()plt.xlabel('n_components')
plt.ylabel('Retention value on scale of 1')
碎石图
观察:
从 Scree 图中,我们观察到,对于约 90%的保留率(这对数据分析非常有用),需要限制 200 个特征。
然而,为了数据可视化,建议减少到二维。
谢谢你的阅读。我也将在未来写更多初学者友好的帖子。请在媒体上关注我,以便了解他们。我欢迎反馈,可以通过 Twitter ramya_vidiyala 和 LinkedIn RamyaVidiyala 联系我。快乐学习!
PCA(主成分分析)5 分钟直观讲解
当你有 3 个以上的维度时,创建 2D 图
每个人都明白一个好的图表。
但是我们如何显示 4+维的数据呢?
在 1 维中可视化星团是小菜一碟。
太容易了
在二维空间中寻找星团很容易。
容易的
看到三维的星团开始吮吸。我们可以编辑节点大小或不透明度来区分第三维。
没那么容易
5 尺寸?别提了。
但是……PCA 可以把你的 5 维投射到 2 维,而不会丢失(太多)信号。
5 秒钟 PCA 是什么?
PCA 通过将相关特征组合成新特征,将高维数据投影到低维中。
相关特征在视觉上模糊了聚类,无助于训练模型,并且增加了复杂性。所以没什么大不了的。
使用 PCA 在图片中 3D 到 2D
比起 3D 图表,我更喜欢 2D 图表。
我们将使用 PCA 将 3D 数据转换成 2D 数据。无论是 10 维还是 100 维,这个过程都是一样的。
我们将跳过数学,试着直观地理解它。
1.绘图数据
假设我们的数据如下所示。左侧是要素 x、y 和 z。右侧标绘了这些点。
假设绘制的数据点被缩放。
2.找到数据的中心
这是每个特征的平均值:x、y 和 z。
3.移动数据点,使中心现在位于(0,0)
注意数据点的相对位置不变。
4.找到最适合的线
最佳拟合线称为 PC1(主成分 1)。
PC1 最大化点与最佳拟合直线以直角相交的距离的平方和。
PC1 是 x、y 和 z 的线性组合,意味着它包含 x、y 和 z 的每个部分。.)
5.查找 PC2
PC2 是与 PC1 垂直(以直角相交)的最佳拟合直线。
PC2 也是每个 x、y 和 z 的线性组合。
PC1 和 PC2 现在都解释了我们功能中的一些差异。
可以通过计算“加载分数”来测量每个 PC 中的相对重要性 x、y 和 z。
6.旋转图表,使 PC1 为 x 轴,PC2 为 y 轴
旋转后,我们的数据现在只是二维的!集群很容易被发现。
如果你一开始就有 3 个以上的维度会怎样?
在您的数据集中,PC 的数量与特征或示例的数量一样少。
你可以通过比较特征值(到原点的距离的平方和)并构建 scree plot 来计算每个 PC 的解释方差。根据定义,PC2 解释的方差小于 PC1,PC3 解释的方差小于 PC2。
决定保留多少台电脑。在我们的例子中,我们决定不考虑 PC3。
剩余 PC 的数量将决定最终图表的维度数量。
结论
就是这样。我试着让它尽可能的平易近人。
研究数学有助于你对它如何工作有更深的直觉,但我不认为这应该是在 Sklearn 中使用 PCA 的先决条件。
你现在已经有了一个非常基本的了解。
基于 NumPy 的主成分分析
只使用 NumPy 库中的函数实现 PCA 的关键步骤
由 Pixabay 授权
主成分分析
这是一种常用于线性降维的技术。PCA 背后的思想是找到尽可能保留更多信息的数据的低维表示。
让我们开始遵循接下来的步骤。
加载库
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
资料组
这是模式识别文献中的经典数据库。数据集包含 3 类,每类 50 个实例,其中每类涉及一种鸢尾植物。从 UCI 机器学习库中检索。
iris = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data",
header=None)
iris.columns = ["sepal_length","sepal_width",
'petal_length','petal_width','species']
iris.dropna(how='all', inplace=True)
iris.head()
可视化数据
# Plotting data using seabornplt.style.use("ggplot")
plt.rcParams["figure.figsize"] = (12,8)
sns.scatterplot(x = iris.sepal_length, y=iris.sepal_width,
hue = iris.species, style=iris.species)
标准化数据
在应用 PCA 之前,变量将被标准化为平均值为 0,标准差为 1。这一点很重要,因为所有变量都经过原点(所有轴的值都是 0)并共享相同的方差。
def standardize_data(arr):
'''
This function standardize an array, its substracts mean value,
and then divide the standard deviation.
param 1: array
return: standardized array
'''
rows, columns = arr.shape
standardizedArray = np.zeros(shape=(rows, columns))
tempArray = np.zeros(rows)
for column in range(columns):
mean = np.mean(X[:,column])
std = np.std(X[:,column])
tempArray = np.empty(0)
for element in X[:,column]:
tempArray = np.append(tempArray, ((element - mean) / std))
standardizedArray[:,column] = tempArray
return standardizedArray
# Standardizing dataX = iris.iloc[:, 0:4].values
y = iris.species.valuesX = standardize_data(X)
计算特征向量和特征值
- 计算协方差矩阵
现在,我将通过将特征矩阵乘以其转置来找到数据集的协方差矩阵。它是衡量每个维度相对于彼此偏离平均值的程度。像相关矩阵一样,协方差矩阵包含关于变量对之间共享的方差的信息。
# Calculating the covariance matrixcovariance_matrix = np.cov(X.T)
2.协方差矩阵的特征分解
# Using np.linalg.eig functioneigen_values, eigen_vectors = np.linalg.eig(covariance_matrix)
print("Eigenvector: \n",eigen_vectors,"\n")
print("Eigenvalues: \n", eigen_values, "\n")
特征向量是主成分。第一个主成分是值为 0.52、-0.26、0.58 和 0.56 的第一列。第二个主成分是第二列,依此类推。每个特征向量将对应于一个特征值,每个特征向量可以根据其特征值进行缩放,特征值的大小表示数据的可变性有多少是由其特征向量解释的。
使用解释的方差选择主成分
我想看看这些成分中的每一个能解释多少数据差异。习惯上使用 95%的解释方差
# Calculating the explained variance on each of componentsvariance_explained = []
for i in eigen_values:
variance_explained.append((i/*sum*(eigen_values))*100)
print(variance_explained)
第一主成分解释了我们数据中 72.77%的方差,第二主成分解释了 23.03%的数据。
确定有多少组件
选择要保留的组件数量的一些指导原则:
- 保留特征值大于 1 的分量,因为它们会增值(因为它们包含的信息比单个变量更多)。这个规则倾向于保留比理想情况更多的组件
- 按照从高到低的顺序将特征值可视化,用一条线将它们连接起来。目视检查时,保持所有特征值落在直线斜率变化最剧烈的点(也称为“弯头”)上方的部件
- 包括方差截止值,我们只保留解释数据中至少 95%方差的成分
- Keep 归结于做 PCA 的原因。
# Identifying components that explain at least 95%cumulative_variance_explained = np.cumsum(variance_explained)
print(cumulative_variance_explained)
如果我用第一个特征,它会解释 72.77%的数据;如果我使用两个特征,我能够捕获 95.8%的数据。如果我使用所有特征,我将描述整个数据集。
# Visualizing the eigenvalues and finding the "elbow" in the graphicsns.lineplot(x = [1,2,3,4], y=cumulative_variance_explained)
plt.xlabel("Number of components")
plt.ylabel("Cumulative explained variance")
plt.title("Explained variance vs Number of components")
将数据投影到低维线性子空间
在最后一步中,我将计算原始数据集的 PCA 变换,得到原始标准化 X 和从特征分解中得到的特征向量的点积。
# Using two first components (because those explain more than 95%)projection_matrix = (eigen_vectors.T[:][:2]).T
print(projection_matrix)
# Getting the product of original standardized X and the eigenvectors X_pca = X.dot(projection_matrix)
print(X_pca)
现在,我可以像使用变量一样,在任何分析中使用组件。
结论
- PCA 变换是使用这些 NumPy 函数实现的:np.cov、np.linalg.eig、np.linalg.svd(它是获得特征值和特征向量的替代方案)、np.cumsum、np.mean、np.std、np.zeros、np.empty 和 np.dot
- 主成分分析的好处是分量比变量少,从而简化了数据空间,减轻了维数灾难
- 当数据是线性的时,PCA 也是最好的使用方法,因为它将数据投影到由特征向量构成的线性子空间上
- 使用主成分分析,它将把我们的数据投射到使沿轴的方差最大化的方向
- 当然,Scikit-learn 也有应用 PCA 的库
完整代码在我的 GitHub 资源库 。
原载于 2020 年 5 月 24 日 https://wendynavarrete.com**。
Python 中的 PDF 文本提取
如何使用 PyPDF2 和 PDFMiner 从 PDF 文件中拆分、保存和提取文本。
我不认为在为一篇关于从 pdf 文件中提取文本的文章撰写介绍段落时有多少创造性的空间。有一个 pdf,里面有文本,我们希望文本出来,我将向您展示如何使用 Python 来实现这一点。
在第一部分,我们将看看两个 Python 库,PyPDF2 和 PDFMiner。顾名思义,它们是专门为处理 pdf 文件而编写的库。我们将讨论我们需要的不同的类和方法。
然后,在第二部分,我们要做一个项目,就是把一个 708 页长的 pdf 文件拆分成单独的小文件,提取文本信息,清理,然后导出到易读的文本文件。关于这个项目的更多信息,请参考我的 GitHub repo 。
PyPDF2
第一步,安装软件包:
pip install PyPDF2
我们需要的第一个对象是一个 PdfFileReader :
reader = PyPDF2.PdfFileReader('Complete_Works_Lovecraft.pdf')
该参数是我们想要使用的一个pdf
文档的路径。使用这个reader
对象,您可以获得关于您的文档的许多一般信息。例如,reader.documentInfo
是包含以下格式的文档信息词典的属性:
{'/Author': 'H.P. Lovecraft',
'/Creator': 'Microsoft® Word 2010',
'/CreationDate': "D:20110729214233-04'00'",
'/ModDate': "D:20110729214233-04'00'",
'/Producer': 'Microsoft® Word 2010'}
还可以用reader.numPages
得到总页数。
也许最重要的方法是getPage(page_num)
,它将文件的一页作为单独的 PageObject 返回。注意,PageObjects 在一个列表中,所以该方法使用一个从零开始的索引。
我们不打算大量使用 PageObject 类,你可以考虑做的另外一件事是extractText
方法,它将页面的内容转换成一个字符串变量。例如,要获取 pdf 的第 7 页(记住,零索引)上的文本,您将首先从 PdfFileReader 创建一个 PageObject,并调用此方法:
reader.getPage(7-1).extractText()
然而,即使是官方文档也是这样描述这个方法的:“这对于一些 PDF 文件来说效果很好,但是对于其他文件来说效果很差,这取决于所使用的生成器。”这并不完全令人放心,根据我的经验,extractText
并没有正常工作,它遗漏了页面的第一行和最后一行。这是我在项目中使用另一个库 PDFMiner 的主要原因。
接下来我们需要一个 PdfFileWriter 对象。这个类没有参数,您可以像这样创建它:
writer = PyPDF2.PdfFileWriter()
对象将跟踪我们想要创建的pdf
文件。为了向要创建的文件添加页面,使用addPage
方法,该方法需要一个 PageObject 对象作为参数。例如,要从我们的输入 pdf 中添加某个页面:
my_page = reader.getPage(7)
writer.addPage(my_page)
最后,PdfFileWriter 对象有一个 write 方法,将内容保存在文件中。该方法需要一个参数,一个文件对象,这意味着简单地输入文件路径是行不通的。创建文件对象的一个简单方法是使用 Python 内置的open
方法:
output_filename = 'pages_we_want_to_save.pdf'with open(output_filename, 'wb') as output:
writer.write(output)
这些是我们将要使用的所有类和方法,参见 PyPDF2 文档了解更多功能的信息。
现在我们可以读写 pdf 文件,但是我们仍然需要一个重要的功能:将内容转换成文本文件。为此,我们需要使用一个不同的库,PDFMiner。
PDFMiner
我们将使用 pdfminer.six,它是原始 pdfminer 库的一个社区维护的分支。(自 2020 年起,PDFMiner 项目不再维护。)
首先,您需要安装它:
pip install pdfminer.six
与 PyPDF2 相比,PDFMiner 的范围要有限得多,它实际上只专注于从 PDF 文件的源信息中提取文本。文档也非常集中,其中有大约三个例子,我们将基本上使用指南中提供的代码。由于代码看起来工作正常,我觉得没有必要深入研究。
这就是我们现在在实际项目中所需要的!
Lovecraft 项目简介
只是为了给你一些我们为什么做这个项目的背景,我想对 T4 的作品做一个全面的 NLP 分析。如果你不知道他是谁,他是 20 世纪初的一位美国作家,以其怪异和宇宙恐怖小说而闻名,对现代流行文化有着巨大的影响。如果你想一窥他的写作风格,看看这篇文章,如果你想了解他性格中一些更有问题的方面,我推荐这篇。
最棒的是,他的作品在他去世 70 年后,在欧盟已经正式成为公共领域,在美国基本上也是公共领域,因为似乎没有人拥有版权。这就是为什么现在所有的东西都有克苏鲁,这是免费的知识产权。网上有很多来源可以收集他的作品,但有趣的是,它们不在古腾堡项目中。我发现其他一些集合有轻微的文字变化,一个例子是“经常”与“经常”在第一句墙外和睡眠,或不包含引号等。
最后,我发现 Arkham Archivist 是一个相对有据可查的选项(我也很喜欢这个名字),下载了 pdf,并开始分割文件和提取文本。
将理论付诸实践
在本节中,我们将结合目前所学的内容。我们所需要做的就是从阿克汉姆档案馆的大 pdf 文件开始。
我们将使用几个通用函数,我将它们保存在一个单独的data_func.py
文件中:
功能:
convert_pdf_to_string
:这是我们从 pdfminer.six 文档中复制的通用文本提取器代码,稍加修改,我们就可以将它用作一个函数;convert_title_to_filename
:一个函数,获取目录中出现的标题,并将其转换为文件的名称——当我开始工作时,我认为我们需要更多的调整;split_to_title_and_pagenum
:这个函数的用途在后面会变得更清楚,它基本上是用来判断目录中的一行是否实际上是一个标题-页码对,如果是,就以元组的形式返回。
下一步是设置环境,我们导入库(包括上面块中的函数),检查文档的一些属性。最重要的是,我们设置了将在整个项目中使用的 PyPDF2 PdfFileReader 对象:reader
。
现在我们可以开始处理文件了。看一下 pdf,似乎最好的办法是从目录中提取页码,然后用它们来分割文件。目录在 pdf 中的第 3 页和第 4 页,这意味着在 PageObjects 的 PdfFileReader 列表中的第 2 页和第 3 页。一旦我们在一个单独的文件中有了 pdf,我们就可以使用 pdfminer.six 代码来提取文本信息。(注意:我们也可以直接调整相关页面而不分割文件,但是我也想创建单独的 pdf 文件,并且有一个单独的目录文件也是有意义的。)
截至目前,text
变量如下所示:
'Table of Contents \n\nPreface ............................................................................................................................. 2 \nThe Tomb .......................................................................................................................... 5 \nDagon ............................................................................................................................. 12
...
诸如此类。几个简单的字符串替换了:
会将text
转换成更好的格式。进行这样的调整时需要小心,例如,它还会删除任何“.”一个标题可能有,但幸运的是,我们没有这样的标题。我们现在有了目录中的行列表:
['Table of Contents ',
'',
'Preface 2 ',
'The Tomb 5 ',
'Dagon 12 ',
...
我们希望将标题和页码收集到列表中。这是通过去掉空格字符,然后检查该行是否以数字结尾来完成的。我们需要小心,因为我们确实希望保留标题中的空格。
现在我们有了列表,我们可以将文件分割成更小的 pdf 文件,保存在单独的文件夹中:
如果我们查看单个 pdf 文件,我们希望进行三项额外的调整:
- 每个故事的标题后面的括号里都有写作的年份。这不是原文的一部分,我们想把它们排除在外。但是,我们确实希望将它们保存在一个列表中,以便在将来的分析中使用它们。
- 每个故事都有一个返回目录的链接,“返回目录”,它显然不是原文的一部分。我检查了一下,这个单词组合(5-gram,我们将在后面的 NLP 项目中看到)没有出现在任何原始文本中,所以我们可以删除它。
- 最后一个故事的结尾有一个“Fin ”,还有一堆空格。因为字符“f-i-n”最有可能出现在英文文本的某个地方,所以我最后只删除了最后一个字符串的末尾。
运行所有故事的代码可能需要一分钟。
最后,我们将原始标题、文件名、页码和写作年份保存在一个 CSV 文件中。
这样,我们就完成了,我们成功地清理了文本,并将它们保存在单独的文本文件中。
结论
我想主要的问题是:这样做项目值得吗?我不能在别的地方找到文本文件吗?正如我提到的,我找到了文本来源,但这似乎是最可靠的。好吧,但是简单地手动复制文本不是更快吗?我正在考虑那件事,但是我不这样认为。想想我们应用的所有细微差别,收集它写的年份,删除非核心文本,等等。
无论如何,我学到了一些新东西,希望你觉得有用。看到一个人在做这样一个相对简单的项目时会遇到多少小问题是很有趣的,我已经迫不及待地想开始写这篇文章了。我真的很好奇洛夫克拉夫特在他的作品中到底用了多少次“恐怖”这个词!
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
matepocs.medium.com](https://matepocs.medium.com/membership)
参考
[## 如何在 Python 中使用 PDF 真正的 Python
在这个循序渐进的教程中,您将学习如何在 Python 中使用 PDF。您将看到如何从…中提取元数据
realpython.com](https://realpython.com/pdf-python/) [## 面向 Nook 和 Kindle 的免费 H.P. Lovecraft 全集
在 2010 年 12 月初,我用一个来自澳大利亚古腾堡项目的文件创建了一个大多数 Lovecraft 的 EPUB
arkhamarchivist.com](https://arkhamarchivist.com/free-complete-lovecraft-ebook-nook-kindle/) [## PyPDF2 文档- PyPDF2 1.26.0 文档
编辑描述
pythonhosted.org](https://pythonhosted.org/PyPDF2/) [## pdfminer.six
Pdfminer.six 是一个 python 包,用于从 PDF 文档中提取信息。查看 github 上的源代码。这个…
pdfminersix.readthedocs.io](https://pdfminersix.readthedocs.io/en/latest/index.html)
使用 YOLOv3 解析 PDF 表格
用 python 解析 pdf 文件内容(包括表格)的最佳工具
PDF 文件或表格是 Adobe 开发的一种文件类型,目的是创建各种形式的内容。特别地,它允许关于其内容变化的一致的安全性。一个 PDF 文件可以包含不同类型的数据:文本、图像、媒体等等。它是一个标签结构的文件,这使得它很容易解析,就像一个 HTML 页面。
也就是说,出于结构的考虑,我们可以将 PDF 文件分为两类:
- 文本文件:包含可复制粘贴的文本
- 基于图像的文件:包含扫描文件等图像
在本文中,我们将通过主要的python libraries
功能来解析基于文本和基于图像的 PDF 文件,这些文件将被 OCR 化,然后作为基于文本的文件进行处理。我们还将在最后一章介绍如何使用对象检测算法 YOLOV3 来解析表格。
目录表
摘要如下:
- 基于图像的 pdf 文件 1.1。OCRMYPDF
- 基于文本的 pdf 文件 2.1。PyPDF2
2.2。PDF2IMG
2.4。卡梅洛特
2.5。卡米洛特与约洛夫 3
为了便于本文的说明,我们将使用这个 pdf 文件。
1.基于图像的 pdf 文件
1.1.OCRMYPDF
Ocrmypdf 是一个 python 包,它允许将基于图像的 pdf 转换为基于文本的 pdf,可以选择、复制和粘贴文本。
为了安装 ocrmypdf,您可以使用命令行对 macOS 和 Linux 使用brew
:
brew install ocrmypdf
安装软件包后,您可以通过运行以下命令行来 ocrise 您的 pdf 文件:
ocrmypdf input_file.pdf output_file.pdf
其中:
- ocrmypdf :路径变量
- input_file.pdf :基于图像的 pdf 文件
- output_file.pdf :输出文本文件
作者图片
一旦 pdf 转换成基于文本的格式,就可以使用下面详述的所有库进行处理。
有关ocrmypdf
的更多详情,请访问以下官方网站。
2.基于文本的 pdf 文件
在这一节中,我们将主要关注三个 python 库,它们允许提取基于文本的 pdf 文件的内容。
2.1.PyPDF2
PyPDF2 是一个 python 工具,它使我们能够解析 PDF 文件的基本信息,如作者、标题等。它还允许在假设有密码的情况下获取给定页面的文本,以及拆分页面和打开加密文件。
PyPDF2 可以使用pip
通过运行以下命令行来安装:
pip install PyPDF2
我们总结了以下 python 脚本中列出的所有功能:
- 读取 pdf 文件:
from PyPDF2 import PdfFileWriter, PdfFileReader
PDF_PATH = "boeing.pdf"
pdf_doc = PdfFileReader(open(PDF_PATH, "rb"))
- 提取文档信息:
print("---------------PDF's info---------------")
print(pdf_doc.documentInfo)
print("PDF is encrypted: " + str(pdf_doc.isEncrypted))
print("---------------Number of pages---------------")
print(pdf_doc.numPages)*>> ---------------PDF's info---------------
>> {'/Producer': 'WebFilings', '/Title': '2019 12 Dec 31 8K Press Release Exhibit 99.1', '/CreationDate': 'D:202001281616'}
>> PDF is encrypted: False
>> ---------------Number of pages---------------
>> 14*
- 逐页分割文件:
#indexation starts at 0
pdf_page_1 = pdf_doc.getPage(0)
pdf_page_4 = pdf_doc.getPage(3)
print(pdf_page_1)
print(pdf_page_4)*>> {'/Type': '/Page', '/Parent': IndirectObject(1, 0), '/MediaBox': [0, 0, 612, 792], '/Resources': IndirectObject(2, 0), '/Rotate': 0, '/Contents': IndirectObject(4, 0)}
>> {'/Type': '/Page', '/Parent': IndirectObject(1, 0), '/MediaBox': [0, 0, 612, 792], '/Resources': IndirectObject(2, 0), '/Rotate': 0, '/Contents': IndirectObject(10, 0)}*
- 从页面中提取文本:
text = pdf_page_1.extractText()
print(text[:500])*>> '1Boeing Reports Fourth-Quarter ResultsFourth Quarter 2019 Financial results continue to be significantly impacted by the 737 MAX grounding Revenue of $17.9 billion, GAAP loss per share of ($1.79) and core (non-GAAP)* loss per share of ($2.33) Full-Year 2019 Revenue of $76.6€billion, GAAP loss per share of ($1.12) and core (non-GAAP)* loss per share of ($3.47) Operating cash flow of ($2.4)€billion; cash and marketable securities of $10.0 billion Total backlog of $463 billion, including over 5,400'*
- 逐页合并文档:
new_pdf = PdfFileWriter()
new_pdf.addPage(pdf_page_1)
new_pdf.addPage(pdf_page_4)
new_pdf.write(open("new_pdf.pdf", "wb"))
print(new_pdf)*>> <PyPDF2.pdf.PdfFileWriter object at 0x11e23cb1****0****>*
- 裁剪页面:
print("Upper Left: ", pdf_page_1.cropBox.getUpperLeft())
print("Lower Right: ", pdf_page_1.cropBox.getLowerRight())
x1, y1 = 0, 550
x2, y2 = 612, 320
cropped_page = pdf_page_1
cropped_page.cropBox.upperLeft = (x1, y1)
cropped_page.cropBox.lowerRight = (x2, y2)
cropped_pdf = PdfFileWriter()
cropped_pdf.addPage(cropped_page)
cropped_pdf.write(open("cropped.pdf", "wb"))
作者图片
- 加密和解密 PDF 文件:
PASSWORD = "password_123"
encrypted_pdf = PdfFileWriter()
encrypted_pdf.addPage(pdf_page_1)
encrypted_pdf.encrypt(PASSWORD)
encrypted_pdf.write(open("encrypted_pdf.pdf", "wb"))
read_encrypted_pdf = PdfFileReader(open("encrypted_pdf.pdf", "rb"))
print(read_encrypted_pdf.isEncrypted)
if read_encrypted_pdf.isEncrypted:
read_encrypted_pdf.decrypt(PASSWORD)
print(read_encrypted_pdf.documentInfo)*>> True
>> {'/Producer': 'PyPDF2'}*
作者图片
关于PyPDF2
的更多信息,请访问官方网站。
2.2.PDF2IMG
PDF2IMG 是一个 python 库,允许将 PDF 页面转换成可以处理的图像,例如,通过计算机视觉算法。
通过运行以下命令行,可以使用pip
安装 PDF2IMG:
pip install pdf2image
我们可以将第一页和最后一页设置为从 pdf 文件转换成图像。
from pdf2image import convert_from_path
import matplotlib.pyplot as pltpage=0
img_page = convert_from_path(PDF_PATH, first_page=page, last_page=page+1, output_folder="./", fmt="jpg")print(img_page)*>> <PIL.PpmImagePlugin.PpmImageFile image mode=RGB size=1700x2200 at 0x11DF397D****0****>*
作者图片
2.3.卡默洛特
Camelot 是一个专门解析 pdf 页面表格的 python 库。可以通过运行下面的命令行使用pip
来安装它:
pip install camelot-py[cv]
解析的输出是一个pandas dataframe
,这对数据处理非常有用。
import camelot
output_camelot = camelot.read_pdf(
filepath="output_ocr.pdf", pages=str(0), flavor="stream"
)
print(output_camelot)
table = output_camelot[0]
print(table)
print(table.parsing_report)*>> TableList n=****1****>
>> <Table shape=(18, 8)>
>> {'accuracy': 93.06, 'whitespace': 40.28, 'order': 1, 'page': 0}*
当 pdf 页面包含文本时,Camelot 的输出将是一个数据框,第一列中包含文本,随后是所需的表格。通过一些基本的处理,我们可以将它提取如下:
作者图片
卡梅洛特提供两种口味格子和流,我建议使用stream
,因为它对表格结构更灵活。
2.4.卡米洛特混有约洛夫 3
Camelot 提供了通过变量table_areas="x1,y1,x2,y2"
指定要处理的区域的选项,其中(x1,y1)是 PDF 坐标空间的左上,( x2,y2)是右下。填写后,解析的结果会显著增强。
解释基本想法
自动解析表的一种方法是训练一种算法,该算法能够返回环绕表的边界框的坐标,如下面的管道中所详述的:
作者图片
如果原始的 pdf 页面是基于图像的,我们可以使用ocrmypdf
转换成基于文本的页面,以便能够在表格中获取文本。然后,我们执行以下操作:
- 使用
pdf2img
将 pdf 页面转换成图像页面 - 使用经过训练的算法来检测表的区域。
- 使用图像尺寸标准化边界框,这使得能够使用通过
PyPDF2
获得的 pdf 尺寸获得 pdf 空间中的区域。 - 将区域反馈给
camelot
并获得相应的熊猫数据帧。
检测 pdf 图像中的表格时,我们扩展边界框以保证其完全包含,如下所示:
作者图片
表格检测
允许检测表格的算法是 yolov3,我建议你阅读我以前的关于对象检测的文章。
我们微调算法,检测表格,重新训练所有架构。为此,我们执行以下步骤:
- 使用
[Makesense](https://www.makesense.ai/)
工具创建一个培训数据库,该工具能够以 YOLO 格式进行标记和导出:
作者图片
- 训练 a
[yolov3](/object-detection-face-recognition-algorithms-146fec385205)
[repository](https://github.com/ultralytics/yolov3)
被修改以适应我们在 AWS EC2 上的目的,我们得到以下结果:
作者图片
插图
检测结果如下所示:
作者图片
结论
当混合标准 python 库和深度学习算法时,有可能显著增强 PDF 文档的解析。事实上,按照相同的步骤,我们可以训练 YOLOV3 算法来检测 pdf 页面中的any other object
,例如可以从图像页面中提取的图形和图像。
你可以在我的 GitHub 里查看我的项目。
不要犹豫,检查我以前的文章处理:
原载于 2020 年 4 月 30 日 https://www.ismailmebsout.com。
皮尔逊和斯皮尔曼等级相关系数—解释
随机变量之间的关系。
相关系数是发现两个随机变量之间关系的统计度量。两个随机变量之间的相关性可以用来比较两者之间的关系。通过观察相关系数,可以测量关系的强度。
相关系数的值范围从-1 到+1。
- 接近+1 的值表示高线性关系,随着一个随机变量的增加,第二个随机变量也增加。
- 接近-1 的值表示高线性关系,随着一个随机变量的增加,第二个随机变量减少。
- 接近或等于 0 的值表示两个随机变量之间没有关系。
一些先决条件—协方差:
协方差是一种用于确定两个随机变量各自的均值相差多少的度量。它受到尺度变化的影响。协方差系数的值介于-∞和+∞之间。
Notation,
X, Y: Two random variables
X_bar: mean of random variable X
Y_bar: mean of random variable Y
n: length of random variable X, Y
这里身高与体重的协方差> 0,即 114.24,这意味着随着身高的增加,体重也增加。
因此,协方差根据两个变量与其平均值的偏差来比较它们。
协方差有一个限制,即它的值在-∞和+∞之间,因此
皮尔逊相关系数(PCC):
皮尔逊相关是衡量两个随机变量之间相关程度的系数。系数值的范围在+1 到-1 之间。皮尔逊相关是每个随机变量的标准差对协方差的归一化。
Notation,
X, Y: Two random variables
COV(): covariance
SD: standard deviation
PCC 的局限性:
- 两个变量都需要正态分布
- 变量需要线性和同方差
斯皮尔曼等级相关系数(SRCC):
SRCC 涵盖了 PCC 的一些局限性。它不带有任何关于数据分布的假设。SRCC 是一种测试,用于通过给每个随机变量的值分配等级并计算 PCC 来测量两个变量之间的关联程度。
(图片由作者提供)
给定两个随机变量 X,y。计算每个随机变量的秩,使得最小值的秩为 1。然后应用秩(X)和秩(Y)上的皮尔逊相关系数来计算 SRCC。
SRCC 的范围在-1 到+1 之间,适用于单调递增或递减的函数。
降维方法概述-相关,主成分分析,t-SNE,自动编码器及其在…
towardsdatascience.com](/4-ways-to-reduce-dimensionality-of-data-8f82e6565a07)
结论:
SRCC 克服了 PCC 的一些缺点,因此它应该比 PCC 用于计算两个随机变量之间的关系。上面讨论的两个系数只有在两个随机变量都连续时才起作用。
感谢您的阅读
PEDRA——无人机强化学习应用的可编程引擎
3D 环境中无人机的 Python 平台
跳转到代码: PEDRA GitHub 库
PEDRA 是什么?
PEDRA 是用于无人机强化学习(RL)应用的可编程引擎。该引擎是用 Python 开发的,并且是模块式可编程的。PEDRA 主要针对面向目标的无人机 RL 问题,但也可以扩展到其他问题,如 SLAM 等。该引擎使用 AirSim 与虚幻游戏引擎进行交互,以创建完整的平台。下图显示了引擎的完整框图。虚幻引擎用于为接受训练的无人机创建 3D 逼真环境。可以添加不同级别的细节,以使环境看起来尽可能真实或符合要求。PEDRA 配备了一个可供用户选择的 3D 现实环境列表。一旦选择了环境,它就使用 AirSim 与 PEDRA 连接。AirSim 是微软开发的一个开源插件,它将虚幻引擎与 Python 相接口。它提供基本的 python 功能,控制无人机的感官输入和控制信号。PEDRA 建立在 AirSim 提供的低级 python 模块上,为无人机 RL 应用程序创建更高级别的 python 模块。
PEDRA 工作流程
PEDRA 的完整工作流程如下图所示。引擎从配置文件(.cfg)。这个配置文件用于定义问题和解决问题的算法。它是算法特定的,用于定义算法相关参数。目前支持的问题是基于相机的自主导航,支持的算法是单无人机香草 RL,单无人机 PER/DDQN 基 RL。更多的问题和相关算法正在被添加。PEDRA 最重要的特性是高级 python 模块,可以用作构建模块来实现面向无人机应用的多种算法。用户可以从上述算法中选择,也可以使用这些构建模块创建自己的算法。如果用户想要定义他们自己的问题和相关算法,可以使用这些构件。一旦设定了这些要求,模拟就可以开始了。PyGame 屏幕可用于控制模拟参数,如暂停模拟、修改算法或训练参数、覆盖配置文件以及保存模拟的当前状态等。PEDRA 生成许多输出文件。日志文件跟踪每次迭代的模拟状态,列出有用的算法参数。这在排除模拟故障时特别有用。Tensorboard 可用于在运行时可视化训练图。这些图对于监控训练参数和在需要时使用 PyGame 屏幕更改输入参数特别有用。
安装 PEDRA
当前版本的 PEDRA 支持 Windows,需要 python3。建议为这个项目创建一个新的虚拟环境并安装依赖项。可以采取以下步骤下载 PEDRA 入门
1.克隆存储库
为了使事情变得简单和容易,PEDRA 配备了两个版本。
- PEDRA: 单机无人机支援:
- D-PEDRA: 分布式多架无人机支援
每个版本都是存储库中的一个分支,可以按如下方式下载
# PEDRA Single Drone
git clone — single-branch — branch PEDRA [https://github.com/aqeelanwar/PEDRA.git](https://github.com/aqeelanwar/PEDRA.git)
# Distributed PEDRA Multiple Drones
git clone — single-branch — branch D-PEDRA [https://github.com/aqeelanwar/PEDRA.git](https://github.com/aqeelanwar/PEDRA.git)
2.安装所需的软件包
提供的 requirements.txt 文件可以用来安装所有需要的包。使用以下命令
采用 NVIDIA GPU 的系统
cd PEDRA
pip install –r requirements_gpu.txt
没有 NVIDIA GPU 的系统
cd PEDRA
pip install –r requirements_cpu.txt
这将在激活的 python 环境中安装所需的包。
3.安装 Epic 虚幻引擎
所提供的模拟环境是使用虚幻游戏引擎创建的。为了运行这些环境,您需要在机器上安装虚幻引擎。您可以按照下面链接中的指导在您的平台上安装虚幻引擎。建议安装虚幻引擎 4.18.3 版。
跑步 PEDRA
一旦安装了所需的包和软件,就可以执行以下步骤来运行代码
1.下载模拟环境
您可以使用虚幻引擎手动创建自己的环境(如果您计划创建自己的环境,请参见下面的常见问题解答来安装 AirSim 插件),或者您可以从下面的链接下载其中一个环境。
以下环境可供下载
室内环境:
- 室内长环境
- 室内扭转环境
- 室内通风环境
- 室内技术环境
- 室内金字塔环境
- 室内冰箱环境
- 室内燃气轮机环境
- 室内复杂环境
- 室内上下环境
- 室内云环境
室外环境:
- 户外庭院
- 户外森林
- 户外老城
关于环境的更多细节可以在这里找到。
上面的链接将帮助您下载 64 位 windows 环境的打包版本。提取并保存在 unreal_env 文件夹中。
# Generic
|-- PEDRA
| |-- unreal_envs
| | |-- <downloaded-environment-folder>
# Example
|-- PEDRA
| |-- unreal_envs
| | |-- indoor_cloud
| | |-- outdoor_forest
| |
2.编辑配置文件
有两种类型的配置文件可用于控制一般仿真参数和算法特定参数,它们可在 configs 文件夹中找到
2a。模拟配置:
|-- PEDRA
| |-- configs
| | |-- config.cfg
该配置文件用于设置高级仿真参数。参数及其解释的完整列表如下所示。
通用参数【通用参数】:
摄像机参数【Camera _ params】:
2b。特定于算法的配置:
# Example
|-- PEDRA
| |-- configs
| | |-- DeepQLearning.cfg
| | |-- DeepREINFORCE.cfg
根据在主配置文件(config.cfg)的 general_param 类别中选择的算法,需要为用户提供的参数编辑特定于算法的配置文件。更多细节可以在这里找到
3.运行 Python 代码
一旦根据用户需要编辑了配置文件,就可以使用 main.py 文件开始模拟
cd PEDRA
python main.py
运行 main.py 会执行以下步骤
- 尝试加载配置文件
- 尝试生成指定环境参数所需的 settings.json 文件
- 尝试启动选定的 3D 环境
- 尝试为用户界面初始化 PyGame 屏幕
- 尝试开始算法
为了加速算法,环境渲染被关闭。关于如何与环境图形交互的详细文档可以在这里看到。为了查看无人机是否在环境中移动,请按环境屏幕上的“Z”键。包含无人机的平面图将出现在右上角。
特征
1.可用无人机代理:
PEDRA 配备了 3 架无人机
- 阿德龙
- 吉马维奇
- DJIPhantom
下面可以看到这些无人机的图像。
不同的动作空间可以与这些无人机中的每一个相关联。
2.支持的模式:
配置文件可用于选择模拟需要运行的模式。
- train :表示训练模式,作为待执行算法的输入标志
- 推断:表示推断模式,作为待执行算法的输入标志。通过设置以下参数,可将自定义砝码加载到网络中
custom_load_path: True
custom_load_path: <path_to_weights>
- move_around :当模式设置为 move_around 时,模拟以自由模式启动环境。在这种模式下,键盘可用于在环境中导航。这种模式可以帮助用户了解环境动态。键盘按键 a、w、s、d、左、右、上、下可用于导航。这也有助于确定无人机的初始位置。更多详情此处
3.使用 tensorboard 查看学习参数
在模拟过程中,可以在张量板上查看张量流参数,如ε、学习率、平均 Q 值、损耗和返回。张量板日志文件的路径取决于配置文件中设置的环境类型、环境名称和训练类型,由下式给出
custom_load_path: True
custom_load_path: <path_to_weights>
一旦确定了日志文件的存储位置,就可以在终端上使用以下命令来激活 tensorboard。
custom_load_path: True
custom_load_path: <path_to_weights>
终端将显示可以在任何浏览器上打开的本地 URL,并且 tensorboard 显示器将在运行时显示 DRL 参数。
4.使用 PyGame 屏幕的运行时控件
可以使用 PyGame 屏幕定义和访问特定算法的控件。更多信息可在这里找到
5.输出图表
该模拟实时更新两个图形。第一张图是无人机的高度变化,而另一张图是映射到环境平面图上的无人机轨迹。轨迹图还报告了无人机在坠毁前行驶的总距离。
通过利用环境提供的平面布置图,可以添加更多特定于算法的图形。
GitHub:
GitHub 复位器— PEDRA
github.com](https://github.com/aqeelanwar/PEDRA)
网址:
3D 环境中基于 python 的无人机平台
icsrl.ece.gatech.edu](http://icsrl.ece.gatech.edu/pedra/)
如果这篇文章对你有帮助,欢迎鼓掌、分享和回复。如果你想了解更多关于机器学习和数据科学的知识,请关注我@Aqeel an war或者在LinkedIn上与我联系。
从峰值到峰值:时间序列预测,以预测餐包的需求
这个项目是由 Aditi Khandelwal,Amrita Dutta,Aneesh Goel 和 Simran Kalera 完成的,他们参加了哥伦比亚大学的 MS 商业分析项目。
窥视过去(来源:剪贴画)
解决了“晚饭吃什么”这个长期存在的问题套餐配送服务为消费者提供了一种在家做饭的便捷方式,而不必进行膳食计划和食品杂货店购物。餐包递送是一个 15 亿美元的市场,并且还在增长。据报道,每四个美国人中就有一个订购了套餐。
让你的利润达到顶峰(来源: Iconfinder
我们的目标是通过使用机器学习的历史数据来预测每周需求。我们的目标是开发一个模型,帮助减少套餐服务的“损失”。这是通过量化货币损失实现的,每周可节省数千美元。
数据字典
首先,我们有一个流行的餐包服务的 3 个数据集:
- 每周需求数据表示从第 1 周到第 145 周的每周订单
- 履行中心包含关于每个 center_id 的数据
- 膳食数据包括订购膳食的类型和类别
第一步包括合并三个数据集并寻找缺失值。在时间序列中,丢失的数据可能被隐藏,因为数据可能在时间步长(1 周)上不一致,并可能在构建模型时导致问题。数据被分组为每个膳食 id-中心 id 组合。其中一些组合不是每周都订购/提供,而是通过将这些周的需求视为 0,并将价格视为该组合的平均价格来估算。我们的假设是,这顿饭是在失踪的几周内提供的,但是没有人买。
在研究了餐包市场后,人们意识到大部分成本来自易腐商品。对于一个企业来说,站在本周的立场上了解下周的需求是非常理想的。这将帮助他们订购/安排新的库存,并为下周的订单管理物流。
来源: Giphy
特征工程
在执行基本的探索性数据分析后,价格和需求列被转换成对数标度,以得到正态分布。该数据有许多分类列,因此设计了捕捉这些变量的每周平均需求的特性。下面显示一个: Avg_orders_cat_week 。
制定了反映不同类别每周价格波动的比率。例如,比率价格/avg_price_week_category 指示第 1 周的膳食 1062 的价格比同一周所有饮料的平均价格低 0.977 。这里的想法是,价格低的饭菜可能有高需求。
我们提出的第二种特征是超前和滞后特征,它们是时间序列预测的核心。
一个显而易见的问题是,我们的数据滞后了多少时间步?
需求的自相关图显示滞后的最佳数量是 2(如果值在圆锥之外,则相关性在统计上是显著的,否则可能是偶然的)
在选择了最佳滞后之后,我们创建了超前-滞后特征并为建模设置了数据。下图中的每一行都是进入模型的内容。假设我们处于第 4 周,我们希望预测第 5 周的需求,即标签( lead )。我们使用了第 4 周的几个特性,第 3 周的几个特性和第 5 周的一些特性(餐费、提供的折扣等,假设它会在一周前固定下来)。
一些特点是:
- 第 4 周的需求(需求)
- 第 3 周的需求( demand_lag1 )
- 前几周的平均需求(扩大 _ 平均
- 第 4 周和第 3 周订单的加权平均值( weighted_average_2w
- 第 4 周、第 3 周和第 2 周订单的加权平均值( weighted_average_3w
- 第 4 周提供的折扣( perc_diff )
- 第 5 周提供的折扣( perc_diff_lead1 )。
数据被分成训练集、验证集和测试集。在去除空值之后,训练数据是从第 3 周到第 142 周;第 144 周验证,第 145 周测试。下面的代码片段显示了不同型号的性能。
随机森林是选择的模型,它给出了均方误差和 R 平方的可比结果,并且可以进一步调整。
调整最大深度参数后,我们在整个训练集和验证集上重新训练模型,并在测试集上获得以下结果:
- 均方差: 0.31
- r 平方: 0.89
特征重要性图显示滞后特征是下周需求的最重要预测因素,这是直观的。
来源: giphy
言归正传!
该行业亏损的主要原因是易腐食品的保质期有限。如果需求预测不准确,可能会导致订单预测过高或过低。
假设我们的随机森林回归器预测了 5 个订单,而观察到的实际销售额是 6,我们对需求的预测值低了 1,因此错过了一个订单,我们称之为“订单损失”。同样,如果我们的模型预测 5 个订单,实际销售额是 4,这是一个过度预测的情况,会导致我们所说的“库存损失”,并会因变质易腐品而出现。
我们做了几个假设看看左边展示的这个商业模式。参考该图,
库存损失将是我们超额预测的情况下,由于库存成本而导致的损失。另一方面,订单损失是由于低于预测而导致的损失,我们无法满足需求,因此损失了利润。
如果一个公司没有预测模型会怎样?
基线比较:如果一家公司根本没有任何预测模型,那么我们假设他们会取第 1 周到第 144 周订单的平均值,并预测下一周的订单。
如果基准模型预测需求为 4,而实际需求为 7,我们将会得到-3 的基准损失,这是我们利润的 3 倍。如果我们的集合模型预测需求为 8,那么这将导致 1 倍的库存损失。然后,通过对所有膳食 id 和中心 id 的损失进行累积求和,计算总基线损失和预测损失。
我们的模型真的有助于预测需求并节省一些钱吗?
资料来源:吉菲
基线损失:
386,015 美元
集合模型损失:
279,384 美元
该分析为公司节省了每周 106,631 美元!
额外的用例:测量价格变化的需求弹性。
现在我们已经达到了尽量减少损失的目标,我们探索的另一个用例是:
如果成本增加 25%,我们的模型能预测需求的变化吗?
在一个理想的世界里,我们想要一个模型,它给出一个完美的预测,不会给我们带来损失,但是这样一个理想的场景是不存在的。考虑到我们的模型的预测是准确的,我们运行我们的预训练模型,结帐价格增加了 25%。这使得需求每周减少 46,000 个订单,这很直观,因为这些客户可能都是对价格敏感的客户。
同样,如果我们推出 50%的黑色星期五促销,我们预计每周需求将增加 40,524 个订单。
似乎我们的模型比我们想象的有更多定量和定性的答案。
我们研究了餐包市场;应用机器学习为公司带来利润,并展示了这种模式如何为这一独特的行业带来利润。
你可以在我们的 GitHub Repo 上访问我们的代码:【https://github.com/aditi310896/Demand-Forecasting
来源: hellofresh
同行评审数据科学项目
使用同行审查使您的工作更加防错
同行评审是任何创造性活动的重要组成部分。它用于研究——包括学术界内部和外部——以确保结果的正确性、对科学方法的坚持和产出的质量。在工程中,它用于提供外部审查,并在技术开发过程的早期发现代价高昂的错误。它在任何地方都被用来改善决策。
我们这些在技术行业工作的人都熟悉同行评审的一个特殊且非常重要的实例——代码评审过程。如果你曾经收到过评论,你应该知道,尽管有点不舒服——因为我们的作品总是受到审查——但它可能非常有价值。这一过程迫使我们做好准备,结果,甚至在审查之前就发现了许多需要改进的地方。这通常会提出一些小而重要的问题,这些问题逃过了我们的关注,但对于一个局外人来说却很容易发现,有时它可以提供非常深刻的见解。
因此,这篇文章的目标是为数据科学项目提供一个执行类似功能的流程布局建议。它是我个人关于小规模数据科学项目应该如何运行的观点的一部分,我在以前的博客文章中已经提到过这个话题:初创公司的数据科学项目流程。您可以在下面找到该工作流程的图表。
编辑:关于实施这种方法一年来的收获,请参见我的博客文章:回顾:初创公司数据科学同行评审的一年
这个框架也是基于我作为首席数据科学家、团队领导以及最后作为独立顾问的经历,见证了我咨询过的各种公司所遵循的工作模式。我也依赖于我的许多领导数据科学家团队的朋友的经验。
图 1:数据科学项目流程(来源:作者)
根据我对项目流程的看法,我建议两个不同的同行评审过程,因为我觉得最需要同行审查的两个阶段是:研究阶段和模型开发阶段。
因此,目标是为两个阶段的同行评审过程提供一个总体框架,该框架将:
- 正式化流程并提供结构。
- 鼓励数据科学家提问并提供同行评审。
- 降低代价高昂的错误风险。
我将讨论这两个过程的动机、目标和结构,并附带一个独特的问题清单。
与项目流程类似,这些流程是由应用数据科学领域的小团队编写的。然而,更大的团队甚至研究小组可能会发现下面的一些想法很有用。
演职员表:模型开发评审清单是 Philip Tannor 的创造,他是一位令人敬畏的数据科学家,也是一个更好的人。
数据科学研究阶段回顾
为了唤起你的记忆,我们称之为“研究阶段”,在这个过程中,我们在数据探索和文献回顾之间反复执行(可以包括回顾现有的代码和实现)。这一阶段还包括规划可能的方法空间,以构建和解决手头的问题,并形成我们对最佳前进方式的建议,通过列出每种选择固有的优势和风险来补充这一建议。
图 2:研究阶段的三个检查(来源:作者)
请注意,在此阶段执行三种类型的检查,以验证方法的自然影响选择:
- 技术有效性检查,通常与支持项目的数据/软件工程师一起完成,旨在确保建议的方法可以成功部署并得到工程支持。
- 研究评审,同行帮助数据科学家验证其流程和建议。我们一会儿将深入研究这个问题。
- 范围和 KPI 有效性检查,由产品负责人完成,以确保推荐的方法保持在项目范围内,并且似乎满足项目的 KPI。
动机
我认为,数据科学家在项目的研究阶段获得同行评审的主要动机是防止他们在项目的早期阶段选择错误的方法或方向。这些错误——在 DS 工作流程中被称为方法失败——制造起来非常昂贵,并且它们在项目的剩余生命周期中阻碍了项目。
如果幸运的话,我们会在几次模型开发迭代后发现方法失败,并且我们会失去数据科学家几周的宝贵工作。当使用我们选择跟踪和优化的指标来衡量时,这是方法失败导致性能下降的情况。
然而,在某些情况下,由方法失败导致的性能差异要微妙得多,在使用大量生产数据验证模型性能之前,甚至在用户/客户对该行为的反馈时,可能极难识别,导致只有在模型投入生产后才发现失败。并且成本明显更高:数据科学家、工程师和产品人几个月的工作,客户满意度下降,潜在的流失甚至收益减少。
这就是为什么模型基准不能是我们希望在过程和决策中发现方法错误的唯一方法;我们必须明确地假设它们可能通过这些手段逃脱检测,并试图通过检查过程本身、其逻辑和支持它们的主张和数据,从方法上发现它们。
目标
显然,这里的主要目标是在早期捕捉代价高昂的错误,如上所述,通过明确地将过程的核心方面置于检查之下,同时还对几个捕获对象执行基本的健全性检查。
然而,两个额外的子目标可以在这里陈述:首先,提高数据科学家在即将到来的产品/业务评审过程中解释和维护其决策的能力。第二,更好地准备向团队的其他成员展示研究阶段的成果,这在大多数数据科学团队/小组中是一种极其常见和重要的做法。
结构
准备评审的三个步骤是:
- 接受审查的数据科学家准备一份他所经历的研究过程的演示文稿(不一定是幻灯片)。
- 接受审查的数据科学家安排了一次长时间的会议(至少 60 分钟;上次花了我们两个小时)和评审。
- 评审者(在会议前)检查了清单。
接受审查的数据科学家在演讲中试图触及以下七点:
-
提醒:项目范围和产品需求。
总是从这里开始,因为评审的一个要点是评估遵循的过程和做出的选择如何服务于项目范围和需求。 -
初始指导方针和 KPI。
在许多情况下,这将是负责审查的 DS 第一次看到将产品需求转化为技术 KPI 的过程,这也是向同行展示这一过程的好机会。涵盖问题特征和约束。 -
做出的假设。我们都会对数据格式、可用性、可接受的错误和常见的产品使用模式做出强有力的假设。让他们明白。
-
使用的数据,以及如何探索。
-
达成的可能方法/解决方案列表。
研究阶段的第一个主要成果:解决方案前景图,详细说明每个方案的优缺点,包括假设,如果相关的话,还可能描绘出起源和它们之间的关系。 -
选定的前进方法和理由。
您的推荐。这是这个阶段最显著的成果,但是记住,要绘制一条路线,你必须首先有一张地图。解决方案前景提供的背景应该使您能够向评审者解释选择,同时排除备选方案。 -
哪些可能的失败会导致替代选择?
我相信这个研究阶段经常被忽视的产品的重要性不亚于你的推荐。你现在用应急计划来支持它,明确地分享如何对冲和解释一个研究驱动的项目的内在风险。
最后,以下是审查会议的建议结构:
- 审查中的 DS:研究阶段演示
- 审核人:一般反馈
- 审核人:查看检查表(见下文)
- 审核人:批准或拒绝
- 一起:结果动作点
清单
该列表重点关注在研究阶段应该解决的问题。我把它分成十类:
- 数据属性
- 接近假设
- 过去的经验
- 目标对准
- 履行
- 缩放比例
- 合成/断裂能力
- 信息要求
- 领域适应
- 噪声/偏差/缺失数据弹性
让我们检查一下问题。记住,这只是我想出来的一个初步建议,并不能全面涵盖需要考虑的一切。
数据属性
关于初始数据集:
- 是如何产生的?是怎么采样的?更新了吗?
例如,从现有的五个客户端中的每一个客户端统一抽取上月数据的 10%。 - 这引入了什么噪声、采样偏差和缺失数据?
例如,其中一个客户两周前才集成了我们的服务,在数据集中引入了他的数据的下采样偏差。 - 你能修改采样/生成以减少或消除噪声吗?抽样偏差?缺失数据?
例如,要么将欠采样的客户端向上采样两倍,要么对所有客户端仅使用最近两周的数据。 - 你能独立于一种方法明确地模拟噪声吗?
- 如果数据集被标记,它是如何被标记的?
- 这引入了什么标签偏见?可以测量吗?标签可能来自半专注的用户。使用专家/分析师手动标记一个非常小的(但有代表性的)集合可能是容易处理的,并且将使我们能够测量这个集合上的标记偏差/误差。
- 你能修改或增加标签过程来补偿现有的偏见吗?
- 初始数据集在生产、结构和模式方面与预期的输入数据有多相似?
例如,某些项目的内容在生产中动态变化。或者可能是不同的字段丢失了,这取决于创建的时间或源域,并且是后来完成的或推断的。 - 生产数据的初始数据集有多大代表性?
例如,客户端之间的数据分布不断变化。或者可能它是在春季的两个月中被采样的,但是当冬季开始时,模型会上升。或者它可能是在集成主要的客户端/服务/数据源之前收集的。 - 你希望的最好的训练数据集是什么?非常明确地定义它。
-估计:性能会提高多少?
-生成的可能性和成本如何?
例如,使用三个标注器标记 20,000 个帖子的情感,并给出模式/平均分作为每个帖子的标签。这将需要 400 个工时,预计花费 2000 或 3000 美元,并预计将精确度提高至少 5%(这个数字通常很难提供)。
接近假设
- 每种方法对数据/数据生成过程/正在研究的现象做了什么假设?
- 对你的问题做这些假设合理吗?到什么程度?
- 你如何期望适用性与这些假设的违反相关联?例如,10%的违规→ 10%的性能下降?
- 这些假设可以独立验证吗?
- 是否有违反这些假设的边缘/角落案例?
示例:要确定机票价格的变化是否会推动您的服务使用量激增,您可能需要使用一些经典的因果推断统计方法来分析两个值的时间序列。或者,您可以使用 ARIMA 这样的通用模型来预测下个月的机票价格。
许多这样的方法不仅——有时是隐含的——假设输入时间序列是平稳随机过程的实现,而且它们既没有提供时间序列平稳程度的度量,也没有提供时间序列违反这一假设的程度。最后,当这个假设在任何程度上被违反时,它们没有提供错误的上限——或者说犯错误的概率。
因此,这是这些方法的一个显著缺点,应该明确地提出和说明。在这种情况下,平稳性实际上可以独立地对进行测试,而不考虑所使用的方法,因此在做出选择之前可以确定某些方法的有效性。
以下是一些方法做出的非常强且通常隐含的假设的例子:
- 每个特征遵循高斯分布。
- 时间序列是平稳过程的实现。
- 特征是严格独立的(例如朴素贝叶斯假设这一点)。
- 被测系统中不同部件测量值的可分性。
过往经历
- 你有什么应用这种方法的经验,无论是在一般情况下还是在类似的问题上?
- 您是否发现了任何已发表的(例如在博客帖子或文章中)将此方法应用于类似问题的成功/失败案例?
- 你有没有向同龄人请教他们的经验?
- 从上面可以学到什么教训?
- 如果这个项目是一个迭代和改进现有解决方案的尝试:到目前为止使用了什么解决方案来解决这个问题?他们的优势是什么?他们遭遇了什么问题?
物镜校准
对于基于优化的监督学习方法:
- 拟合模型参数时可以使用哪些损失函数?它们与项目 KPI 有什么关系?
- 哪些指标可用于模型选择/超参数优化?它们与项目 KPI 有什么关系?
- 应该添加哪些硬约束来反映 KPI?
对于无监督学习方法:
- 方法优化什么措施?
- 它与 KPI 有什么关系?
例如,最大化平均词向量的可分性在多大程度上可以近似地找到一组捕捉到关于某个主题的不同观点的文章? - 有哪些很好地满足了度量标准但却不满足 KPI 的边缘案例?
对于无监督表示学习:
- 为什么假任务(例如预测 Word2Vec 中一个单词的邻居)有望促进输入表示的学习,这将有利于试图执行实际任务的模型?
实施
- 在您的生产环境中是否有当前使用的语言的方法实现?
- 实现是最新的吗?一贯支持?广泛使用?
- 与贵公司类似的公司/组织是否成功运用了这一具体实施方案?和你类似的问题?
缩放
- 计算/训练时间如何与数据点数量成比例?
- 与特征的数量?
- 存储如何随数据点/功能扩展?
合成/断开能力
- 如果需要的话,不同领域(类别/客户/国家)的模型能以合理的方式组合吗?
- 或者,他们的结果能以一种有意义的方式整合吗?
- 一个通用模型可以被分解成每个领域的模型吗?可以识别和利用这种模型的每个域相关的内部组件吗?
图 3:模型可组合性可能是一个需求,并且有不同的形式(来源:作者)
例:假设我们正在开发一个主题动态播放列表生成系统,作为音乐流媒体服务的一部分。这意味着我们希望围绕“新独立”、“英国新浪潮”、“女权主义音乐”或“俗气情歌”等主题自动生成和更新播放列表。为了增加复杂性,我们可能还希望允许主题组合的播放列表,如“女权主义新浪潮”或“新阴郁独立”。或者,我们希望能够为听众个性化每个播放列表。最终,如果我们有一个模型可以对包含在主题播放列表中的歌曲进行排序,另一个个性化模型可以根据特定用户喜欢歌曲的可能性对歌曲进行排序,我们可能希望能够将这两个模型的结果结合起来。
不同的方法可以以不同的方式支持这些场景。例如,如果我们将每个人/主题建模为所有歌曲的概率分布,其中播放列表是通过重复采样(没有重复)即时生成的,则任何数量的这种模型都可以容易地组合成单个概率分布,然后可以使用相同的过程来生成适应的播放列表。然而,如果我们使用一些使用分数的 ML 模型对歌曲进行排名,我们可能必须设计一种方法来使不同模型的分数具有可比性,或者使用通用方法来整合同一组项目上的几个排名。
信息需求
- 每种方法在多大程度上依赖于可用信息量?少量信息对性能的预期影响是什么?
- 这与当前可用的信息量是否一致?未来的可用性?
例如,如果不能依赖预先训练的模型或单词嵌入,从头开始训练基于神经网络的文本分类序列模型通常需要大量信息。例如,如果我们正在为代码文件构建一个脚本或模块分类器,这可能正是我们所处的情况。在这种情况下,假设每种编程语言代表一个独立的领域,神经网络模型可能无法很好地适用于非常罕见的编程语言,或者那些我们很少有标签的语言。
冷启动&域适应
- 新客户/类别/国家/地区/域预计多快能达到每种方法的最低信息要求?
- 至少作为一个起点,一个通用模型能为他们带来好的表现吗?
- 有没有办法让一个已经适应的模型适应一个新的领域?这有望比从头开始培训产生更好的绩效吗?这能比从头开始训练更快/更便宜吗?
举例:假设我们正在为我们的在线市场中的产品建立一个推荐系统,这个系统被许多商家使用,每天都有新的商家加入并开始销售他们的产品。使用基于神经网络的模型来学习商家和/或其产品在表示它们的一些数据上的嵌入,可以通过将这些新商家映射到嵌入空间中类似的现有商家附近,然后利用已经学习到的关于不同购物者从现有商家购买特定产品的可能性的知识,来对此类新商家表现得令人惊讶地好。
噪声/偏差/缺失数据弹性
这种方法能处理数据中的噪音吗?怎么会?
- 它具体建模了吗?
- 如果是这样,考虑:噪声模型在多大程度上适用于您的问题中的噪声生成过程?
- 对于异常/预测/因果关系/动态:该方法是否模拟了系统中的外生变量?
对于监督和半监督方法:
- 每种方法对标签偏见的敏感程度如何?
- 在你的案例中,有偏见的模型意味着什么?
- 一般的偏差修正技术适用于这种方法吗?
对于所有方法:
- 每种方法如何处理丢失的数据,如果有的话?
- 在每种情况下,随着丢失数据的增加,性能预计会下降到什么程度?
数据科学模型开发阶段回顾
再次回忆一下,我所说的模型开发阶段是数据科学项目的一部分,在这个阶段完成了大量的数据处理(包括特征生成,如果使用的话),实际的模型被应用于实际的数据——在受监督的场景中,它们被训练或拟合——并且使用一些预定的度量标准进行基准测试,通常是在预留的测试/验证集上。
您可以将整个事情看作是一个大的迭代——不管检查了多少方法——但是我更愿意将它看作是几个迭代,每个迭代致力于一个方法。这些要么并行执行,要么按照我们在研究阶段结束时概述的排序顺序执行,其中方法失败(估计模型子性能是由于当前方法对问题的基本不适用性或不匹配)导致新的模型开发迭代超过下一个最佳方法。
图 4:模型开发阶段的两个检查(来源:作者)
与所有其他阶段类似,最后的检查点是与负责的产品经理共同讨论,以验证要部署的模型实际上满足项目 KPI。然而,在此之前,另一个审查是由一个同行。
动机和目标
正如在研究综述中,这里的动机是模型开发阶段的错误也可能是昂贵的。这些错误几乎总是在生产中被发现,并且可能需要主动的模型监控来捕捉。否则,它们会在产品化阶段的后期被捕获,同时需要一个开发人员/数据工程师,并暴露关于工程能力或生产环境的隐含假设之间的差距;在研究阶段结束时进行的技术有效性检查是为了减少这些错误。然而,由于它们中的许多是(1)技术性的,(2)基于实现的和(3)特定于模型的,它们通常只能在这个阶段期间和之后被检查,因此模型开发评审也处理它们。
因此,目标是相同的:首先,为模型开发阶段提供一个结构化的审查过程,这将通过正式地将它合并到项目流程中来增加同行审查。第二,通过检查准备好的问题和议题列表来降低代价高昂的错误的风险,这些问题和议题是根据必须解决它们的其他数据科学家的宝贵经验来检查的。
结构
在准备评估时,有三个简单的步骤:
- 接受审查的数据科学家准备模型开发过程的演示文稿(不一定是幻灯片)。
- 接受审查的数据科学家安排一次彻底的会议(至少 60 分钟。)与审核数据的科学家。
- 审核 DS(在会议前)检查清单。
接受审查的数据科学家应在演示中涵盖以下几点:
- 提醒:项目范围和产品需求
- 研究阶段后选择的方法
- 模型选择的度量+软硬约束
- 使用的数据、预处理和特征工程
- 模型检查,训练制度,超参数优化。
- 选定的模型、备选方案以及每种方案的优缺点。
- 下一步:自动化、产品化、性能优化、监控等。
此处建议审查会议采用相同的结构:
- 审阅 DS :模型开发阶段演示
- 审核人:总体反馈
- 审核人:查看清单(见下文)
- 审核人:批准或拒绝
- 一起:结果动作点
清单
同样, Philip Tannor 创建了一个问题清单,在评审模型开发阶段时使用,分为以下几类:
- 数据假设
- 预处理
- 渗漏
- 因果关系
- 损失/评估指标
- 过度拟合
- 运行时间
- 愚蠢的虫子
- 琐碎的问题
在这里复制这个列表是没有意义的,特别是因为 Philip 计划维护他的列表,所以去看看他的博客吧。
最后的话
类似于我建议的初创公司数据科学项目流程,这只是一个早期草案。我们正在我的一个客户团队中测试 DS 同行评审的这种方法,我保证会报告结果。此外,我希望听到其他团队尝试实施这种方法或相关方法的反馈——我相信你们中的许多人都经历过类似的过程,并提出了自己的方法。
和往常一样,我把这个放在这里是为了听听我错过或做错的事情,并得到你诚实的反馈。通过你在我的个人网站底部找到的任何联系方式联系我。此外,请随时联系我,让我成为你的顾问。😃
编辑:关于实施这种方法一年来的收获,请参见我的博客文章:回顾:初创公司数据科学同行评审的一年
观察黑盒内部——如何欺骗神经网络
在本教程中,我将向您展示如何使用梯度上升来找出如何错误分类的输入。
使用梯度上升来计算如何将输入分类为 5。(所有图片均为作者本人,版权所有)。
神经网络因为是黑盒而声名狼藉。虽然理解他们的决策肯定需要创造力,但他们真的不像人们想让你相信的那样不透明。
在本教程中,我将向您展示如何使用反向传播来更改输入,以便根据您的喜好对其进行分类。
使用这个按钮跟随。
(这部作品是与阿尔弗雷多·坎齐阿尼在一个即将到来的视频之前共同创作的)
人类是黑匣子
让我们考虑一下人类的情况。如果我给你看下面的输入:
很有可能你不知道这是 5 还是 6。事实上,我相信我甚至可以说服你这个可能也是一个 8。
现在,如果你问一个人,他们必须做些什么才能让一些东西变得更像 5,你可能会这样做:
如果我想让你把这个打成 8 分,你可以这样做:
现在,这个问题的答案不容易用几个 if 语句或者看几个系数来解释(是的,我在看你回归)。不幸的是,对于某些类型的输入(图像、声音、视频等),可解释性肯定变得更加困难但并非不可能。
询问神经网络
神经网络将如何回答我在上面提出的同样的问题?为了回答这个问题,我们可以用梯度上升来做。
下面是神经网络如何认为我们需要修改输入,使其更接近 5。
由此得出两个有趣的结果。首先,黑色区域是我们需要去除像素密度的地方。其次,黄色区域是它认为我们需要增加更多像素密度的地方。
我们可以通过给原始图像添加渐变来在渐变方向上前进一步。我们当然可以一遍又一遍地重复这个过程,最终将输入转化为我们所希望的预测。
你可以看到图像左下方的黑色斑块与人类可能认为要做的事情非常相似。
人类在左上角添加黑色。网络显示同样的情况
让输入看起来更像 8 怎么样?这是网络认为你必须改变输入的方式。
值得注意的是,在左下方有一团黑色,中间有一团明亮。如果我们将此与输入相加,我们会得到以下结果:
在这种情况下,我并不特别相信我们已经把这个 5 变成了 8。然而,我们少做了一个 5,并且使用右边的图像而不是左边的图像来说服你这是一个 8 的论点肯定会更容易获胜。
渐变是你的向导
在回归分析中,我们通过系数来了解我们学到了什么。在随机森林中,我们可以查看决策节点。
在神经网络中,这归结于我们在使用梯度方面有多有创造力。为了对这个数字进行分类,我们生成了一个可能预测的分布。
这就是我们所说的向前传球。
在向前传递期间,我们计算输出的概率分布
代码看起来是这样的(使用 colab 跟随):
现在,假设我们想要欺骗网络预测输入 x 为“5”,那么方法是给它一个图像(x),计算图像的预测,然后最大化预测标签“5”的概率。
为此,我们可以使用梯度上升来计算第 6 个索引(即:label = 5) ( p )处的预测相对于输入 x 的梯度。
为了在代码中做到这一点,我们将输入 x 作为参数馈送给神经网络,选择第 6 个预测(因为我们有标签:0,1,2,3,4,5,…),第 6 个索引意味着标签“5”。
这看起来像是:
“5”相对于输入的预测梯度。
在代码中:
当我们打电话时。backward()发生的过程可以通过前面的动画形象化。
既然我们已经计算了梯度,我们可以将它们可视化并绘制出来:
上面的梯度看起来像随机噪声,因为网络还没有被训练…但是,一旦我们训练了网络,梯度将提供更多信息:
通过回调实现自动化
这是一个非常有用的工具,有助于说明你的网络在训练时发生了什么。在这种情况下,我们希望自动化这一过程,以便在培训中自动进行。
为此,我们将使用 PyTorch Lightning 来实现我们的神经网络:
自动绘制我们在这里描述的内容的复杂代码可以抽象成 Lightning 中的回调。回调是一个小程序,在您可能关心的培训部分调用。
在这种情况下,当处理一个训练批次时,我们希望生成这些图像,以防某些输入被混淆。
但是…我们用 pytorch-lightning-bolts 让它变得更简单,你可以简单地安装它
pip install pytorch-lightning-bolts
并将回调导入到训练代码中
把所有的放在一起
最后,当逻辑“混乱”时,我们可以训练我们的模型并自动生成图像
tensorboard 会自动生成这样的图像:
摘要
总之:您学习了如何使用 PyTorch 查看黑盒内部,学习了直觉,在 PyTorch Lightning 中编写了一个回调函数,并自动获得 Tensorboard 实例来绘制可疑的预测
用 PyTorch 闪电和 PyTorch 闪电自己试试。
(这篇文章是在一个即将到来的视频之前写的,在这个视频中,我(威廉)和阿尔弗雷多·坎齐安将向你展示如何从头开始编码)。
PEGASUS: Google 最先进的抽象摘要模型
谷歌人工智能如何生成人类级别的摘要
苏丹欧阳在 Unsplash 上的照片
总结的能力评估一个人对一篇给定的文章或一种语言的理解。
也许对一个人智力的最好测试是他做总结的能力
—李顿·斯特雷奇
因此,摘要在自然语言处理中是一个相当重要的概念。在这篇文章中,我已经介绍了作为一个整体的摘要和抽象摘要以及它的实现。如果你有兴趣了解这项任务的简要背景,可以考虑读一读;飞马模型是在变形金刚架构上训练的。
在这篇文章中,我们将讨论谷歌人工智能最近提出的一篇论文,“ PEGASUS:使用提取的间隙句子进行抽象概括的预训练,该论文预计将在 ICML 2020 上发表。
PEGASUS:用提取的间隔句进行抽象摘要的预训练
像任何其他序列转换任务一样,PEGASUS 也实施 seq2seq 体系结构。然而,这种架构的新颖之处在于其自我监督的预训练目标。
自我监督学习是深度学习的新亮点。它基本上消除了数据对标记样本的依赖,并使大量未探索、未标记的数据可用于训练。
基于转换器的模型与自我监督的预训练(例如,伯特、 GPT-2 、罗伯塔、 XLNet 、艾伯特、 T5 、伊莱克特拉)的组合,已经被证明在整体语言建模任务中非常有效。
间断句生成(GSG):自动监督的摘要目标
谷歌人工智能博客在 PEGASUS 进行的自我监督预培训
这个目标背后的主要思想是假设预训练自我监督目标越接近最终的下游任务,微调性能越好
因此,在 PEGASUS 中,完整的句子被从文档中删除(即它们被“屏蔽”),并且模型被训练来预测这些句子,如图所示。作者承认,这项任务似乎几乎不可能完成,事实上对人类来说也是如此。但是这种训练对于具有原始文档实例的句子的生成引起了更高的理解感;从而支持他们的假设。这项任务被称为间隙句子生成(GSG)。
除此之外,作者声称从文档中选择最重要的句子来屏蔽效果最好。这是通过根据称为 ROUGE (通常用于评估摘要任务中摘要的质量)的度量来找到与完整文档最相似的句子来完成的。
掩蔽语言模型(MLM)
虽然飞马座的主要贡献是 GSG (上一节讨论过),但它的基础架构由一个编码器和一个解码器组成;因此,将编码器预先训练为屏蔽语言模型是有意义的。
语言建模 v/s Masked 语言建模 by Google AI 博客
在这项任务中,我们随机屏蔽序列中的单词,并使用序列中的其他单词来预测这些被屏蔽的单词。GSG 任务可以被理解为一个文档级的 MLM,并且就是从这个概念中派生出来的。
因此,正如 BERT 论文中所建议的,序列中 15%的单词被随机屏蔽,模型被训练来预测这些被屏蔽的单词。
综合训练
前面几节中讨论的两种方法都被合并,并且以组合的方式训练转换器。
MLM(左)+ GSG(右)一起在飞马训练来自论文
GSG 和 MLM 作为预训练目标同时应用于此示例。本来有三句话。一个句子用[MASK1]屏蔽,用作目标生成文本(GSG)。其他两个句子保留在输入中,但是一些单词被[MASK2] (MLM)随机屏蔽。
— 飞马纸业
结果
该模型在 12 个公共摘要数据集上进行了微调。在其中 6 个数据集上,它已经超过了之前的技术水平,令人惊讶的是,它只在很少的样本上进行训练。
微调
谷歌人工智能博客对选定的 4 个数据集(虚线是未经预训练的完全监督模型的结果)的各种 ROUGE 指标
可以清楚地看到,在最少 1000 个训练样本的情况下,PEGASUS 在这些数据集上已经超越并达到了最先进的水平。
人的素质总结
谷歌人工智能博客进行的人类评级测试
PEGASUS 还在 3 个数据集上取得了人类水平的结果。评估是通过对人工摘要和模型生成的摘要进行评级来完成的,而不知道哪一个是哪一个。
“我们用 3 个不同的数据集进行了实验,结果发现,与我们的模型相比,人类评分者并不总是更喜欢人类摘要”
清点船只
这是飞马座取得的另一个有趣的结果:
来自 Xsum 数据集的一篇文章提出了 4 艘船的名字,即。英国皇家海军舰艇坎伯兰号、坎伯敦号、查塔姆号和康沃尔号。该模型正确地将其抽象为、【四艘皇家海军护卫舰】、、,尽管这里没有提到样本中的数字“四”。
考虑到这是一种侥幸,作者通过从名单中增加或删除名字来测试这一点。如果有 2-5 个名字,该模型可以正确地提取数字。然而,它将 6 艘船误算为“7 艘”,这表明它只能提取列表中的一小部分名称。
演示可以在这里找到。
有趣的事实:该模型仅使用 T5 的 5%的参数数量就取得了比 T5 等同类模型更好的结果。
结论
我们已经讨论了 Google 的抽象摘要模型的工作状态。我们还看到了在一个与下游任务相对相似的任务上进行预训练如何极大地增强了模型在微调上的性能。这为比一般情况更具体地模拟自我监督预训练目标提供了可能性。
代码和检查点是开源的,可以在这里找到。
参考
[## PEGASUS:用提取的间隔句进行抽象摘要的预训练
最近的工作预训练变压器与自我监督的目标对大型文本语料库显示了巨大的成功…
arxiv.org](https://arxiv.org/abs/1912.08777) [## PEGASUS:抽象文本摘要的最新模型
学生的任务通常是阅读一份文件并编写一份摘要(例如,读书报告)来展示…
ai.googleblog.com](https://ai.googleblog.com/2020/06/pegasus-state-of-art-model-for.html) [## 使用变压器的抽象文本摘要
对谷歌 Transformer 模型的详尽解释;从理论到实施
medium.com](https://medium.com/swlh/abstractive-text-summarization-using-transformers-3e774cc42453)
强化学习中折扣因子的惩罚
强化学习领域用于许多机器人问题,具有独特的机制,奖励应该通过行动来积累。但是,这些动作之间的时间呢?
作者形象
这篇文章讨论了我发现影响很大的一个关键参数:折扣因子。讨论了基于时间的惩罚以获得更好的性能,其中折扣因子被相应地修改。
我假设如果你看到这篇文章,你已经熟悉 RL 术语了。如果不是这样,那么在你继续之前,我强烈推荐这些提供了很好背景的博客: Intro1 和 Intro2 。
折扣因子在 RL 中的作用是什么?
贴现因子𝛾是一个实值∈ [0,1],关心代理人在过去、现在和未来获得的回报。换句话说,它将奖励与时间域联系起来。让我们来探讨以下两种情况:
- 如果 𝛾 = 0,代理只关心他的第一个奖励。
- 如果 𝛾 = 1,代理关心所有未来的奖励。
一般来说,设计师应该预先定义场景情节的折扣系数。这可能会引起许多稳定性问题,并且可能在没有达到预期目标的情况下结束。然而,通过探索一些参数,许多问题可以用收敛的解决方案来解决。要进一步了解折扣系数和为机器人应用选择折扣系数的经验法则,我推荐阅读: resource3 。
为什么要处罚?
一旦设计者选择了折扣因子,它对于整个场景都是统一的,这对于连续-离散问题(以及更多,但让我们专注于此)来说不是最优的情况。机器人动力学是一个连续的过程,我们通过各种嘈杂的传感器观察,并以离散的方式处理其信息(毕竟是计算机……)。因此,我们通过使用离散的工具来解决一个连续的问题。因为这涉及到数值误差。此外,各种传感器被噪声污染,增加了固有误差。最后,我们假设的动态模型(例如我们定义的状态)也受到不确定性的影响,并且包括额外的误差。
因此,通过假设统一的折扣因子,我们假设这些误差源的统一行为,这些误差源是非统一行为。对这些问题的补偿可以通过惩罚折扣因子并相应地权衡所获得的回报来实现。
关于取样时间的处罚
下面的例子解释了一种针对采样时间(定义为两次连续测量之间经过的时间)惩罚折扣因子的常用方法:
作者形象
由于采样间隔很小,在极限情况下,折扣为 1(由于或 Rivlin 的校正),当采样间隔很大,以致两次连续测量之间经过很长时间时,采样间隔相应改变。请记住,折扣因子介于 0 和 1 之间,因此大的采样间隔转换为小的折扣因子(反之亦然)。更新折扣系数的公式只是一个演示该想法的建议,因为可以采用许多其他形式。
一个算法例子
考虑一个算法交易场景,其中投资者(代理人)在交易市场(环境)中控制他的对冲策略(行动),其中股票价格(状态)随时间变化。如果最近一次投资已经过去了很长时间,回报就不能保持原样,因为在此期间可能会发生很多变化。因此,修改的折扣因子可能会导致更好的性能,因为它关心事件之间经过的时间。
作者形象
就这样…希望你喜欢读这篇文章!如有任何问题/讨论,请随时联系我。
巴拉克
www.barakor.com
【https://www.linkedin.com/in/barakor/
潘德雷肯四:多智能体强化学习的训练管道
在过去的一年里,我制作了各种版本的神经网络机器人来玩游戏《命运大令》(FGO),大致称为“潘德雷肯计划”。围绕潘德雷肯项目的工作范围从用一系列神经网络提取特征以获得关于当前游戏状态的信息,到我最近增加的三个神经网络来控制游戏屏幕上活跃的三个角色。这三个和另外一个用于挑选动作卡的机器人是四个强化学习(RL)代理,它们组成了我当前版本的项目,潘德雷肯四。
我最近的一篇介绍潘德雷肯四人组的帖子涵盖了我为了能够训练我的新特工而必须进行的总体添加和升级,以及一些结果。然而,我想回顾一下我发现有用的培训的一些方面,以及在继续改进我最初的潘德雷肯四管道后学到的一些经验教训。
- 从 DQN 转向基于政策梯度的培训
- 处理无效移动
- 游戏平衡和适当地挑战代理
纯策略梯度机器人,在多次训练中,一个相当一致的事情是机器人会使用 Ishtar(中间角色)的第二技能来立即清除第一波。对于机器人来说,我认为这是一个更稳定的策略,至少让他们有机会进入后期游戏,而不是在他们可能活着也可能不活着的时候保存技能。
让代理学习他们自己的游戏方式
我对 RL 的第一次真正尝试是为我最初的潘德雷肯 Alter 机器人建立我的定制环境,这是一个基于 DQN 的代理,其目标是在任何给定的回合挑选命令卡。基于 DQN 的培训方法的基本流程如下:
- 代理(在这种情况下是网络)被告知游戏的当前状态。这可能是一款雅达利乒乓游戏的像素,也可能是你选择的任何代表。我的 FGO·潘德雷肯 Alter bot 是 5 张牌中的一张。
- 代理从动作空间中选择一个动作。在 Pong 的例子中,Andrej Karpathy 把它作为上升的概率。在我的 FGO 游戏中,是 60 种可能的卡槽组合中哪一种最好(有 60 种方法从 5 张卡中选择 3 张)。然而,应该注意的是,在强化学习中,存在探索与利用的概念。本质上是说,有时应该随机选择一个动作,而不是简单地做代理认为最好的事情。这有助于代理探索和发现额外的奖励,否则如果它只是利用它知道的奖励,它将不会发现。
- 行动被归入环境,任何奖励被收集,环境进入下一个状态、框架或回合。机械地,我这样做是通过将奖励添加到网络输出的卡槽组合中。对于正的奖励,该类别在输出数组中的值增加,并且网络将再次看到给定该输入,该特定类别是有益的。
- 代理根据其收到的奖励进行更新。在奖励被用于修改输出阵列之后,网络以修改后的输出阵列为目标在初始输入状态上被训练。这有助于巩固好的选择,同时也考虑到坏的选择。
- 冲洗并重复。
这个过程是有效的,也是合理的,但我发现在我的定制环境中,这意味着我不得不限制代理根据我的观点来玩,因为没有分数或内置的奖励,我可以用它来分配个人行为的价值。所以发生的事情是,我必须奖励机器人我认为好的东西。例如,有了卡牌机器人,它将组合 3 张同类型的卡牌组合,因为有与此相关联的奖金,或者与潘德雷肯四中的角色机器人相关联,它将在合理的时间使用技能。虽然这些奖励可以有效地教会机器人有用的行为…但它并没有真正让它们探索新的游戏风格,它把我的信念印在我的代理身上,而不是让它们自己学习。
因此,在我为潘德雷肯四中进行的下一轮培训中,我的目标之一是成功升级到更多的政策梯度培训方法。而基于 DQN 的训练方法在单个回合分配奖励,政策梯度类型方法的最纯粹形式是基于游戏的输赢分配奖励,例如+1 或-1。
从概念上讲,这对我来说一直是一个相当混乱的概念,因为一个代理人可能在赢和输的时候采取相同的行动,但下一次奖励是积极的,然后是消极的。这是真的,结果证明是好的,因为这个想法是,如果在这种情况下,平均来说,这个移动是一个好的移动,你应该赢的次数多于你输的次数,这应该给这个移动一个净正值。
3 场比赛中一个代理人的策略梯度示例:
3 个示例游戏的图表每个圆圈是一个游戏状态,箭头是代理人在该回合选择的动作。底部是三个示例呈现的游戏状态。
在上面的例子中有三场比赛,2 胜 1 负。基本上,我们所做的是对赢的游戏中发生的所有行为给予积极的奖励,对输的游戏中的行为给予消极的奖励。因此,一些结果是,在第 5 回合,两个获胜回合都使用了技能 3 (sk3 ),因此这一步棋在这个子回合中可能会得到相当积极的回报。而在第一回合使用技能 2 是一个只会在失败的游戏中发生的动作,所以它是不利的。一个更有趣的可能是在 1 胜 1 负的第 4 回合使用技能 1。在这个子集之后,它将有 1 个不鼓励的例子和 1 个鼓励的例子。还需要更多的游戏来证明这是否是一个好的举措。
对我来说,这种方法是高度可行的,并产生良好的结果,这有点不可思议。从大数定律的角度来看,这开始变得更有意义了。如果我们平均进行成千上万场游戏,更好的棋步会比更差的棋步出现在更多的胜局中,所以我们最终会让代理人学习如何做出获胜的棋步。
在我之前的潘德雷肯四管道中,我有一个政策梯度管道,我在游戏结束时分配奖励,但我也对我认为好的行为给予中间奖励。所以我仍然把我的一些观点印在代理人身上。因此,本着尽可能将自己从管道中移除的精神,我希望转向一种纯粹的政策梯度方法,在这种方法中,我只根据输赢来分配奖励。为了达到这一点,我发现比我以前做的更多的探索有助于让机器人发现更多的游戏方式。
当我转向使用策略梯度,在游戏完成后应用奖励时,我的另一个想法是将游戏聚合在一起,以创建更大的“数据集”来对代理进行大规模更新,而不是像以前那样一轮一轮地进行更新。我认为这有利于训练,也有利于利用我更多的计算资源。
在我的旧管道中,当我在每回合的基础上接受培训时,我总是发现令人讨厌的是,我真的没有从 GPU 上的培训中受益,因为我认为该设置中的大多数计算都是从 GPU 发送和接收的。所以现在我基本上把几百个游戏收集在一起作为一个“数据集”,然后利用我的 GPU 进行大批量训练。
处理无效移动
在我以前做过的强化学习项目中,我为我的代理保留了所有有效动作的动作空间。主要是因为我不知道如何优雅地处理无效的举动。然而,对于最近的潘德雷肯四的迭代,我必须弄清楚如何为我的代理人实现这一点,以处理技能在使用后冷却,不再可供机器人玩。
在这一点上,我只允许我的代理人每回合选择一个动作,当前的角色集每个都有 4 个可能的移动。
pass
skill_1
skill_2
skill_3
这一套 4 招是因为其中一个机器人拥有可以对其他角色施展的技能,他们的每一招都是针对自己或整个团队的。选择具有这些移动类型的角色是我在原型化我的机器人管道时为了简单而做出的选择。
在未来,我想增加更多的角色,让他们的动作更加多样化,并允许机器人每回合玩不止一个动作。
在处理无效移动的第一次迭代中,我遵循了堆栈溢出的建议:
“忽略无效动作就好了。”-堆栈溢出
这或多或少仍然是我处理无效移动的方式,但它变得更加微妙了。当我训练我的第一轮潘德雷肯四机器人时,我将网络的最后一层设置为线性激活,当技能冷却时,我会将其设置为低值,如阵列中的最低值。我发现,有时这会导致一种竞相逐底的效应,输出会变得非常负。
为了避免这些大的负输出,我决定在网络的最后一层使用类似 sigmoid 的东西,或者更理想的是 softmax 激活,这至少会将每个节点的可能值限制为等于 0 和 1 之间的某个值。这两次激活意味着我可以把技能的冷却时间设置为 0,而不是一些越来越大的负数。在 sigmoid 和 softmax 之间,softmax 激活是我真正想使用的,但不确定如何应用奖励和零化无效移动,同时仍然创建有效的概率分布。所以我用 sigmoids 做了一些测试。
使用 sigmoid 激活,很容易将它们的值保持在 0 和 1 之间。如果奖励使价值高于或低于这些值,那么你可以把它设置回 1 或 0。因为 sigmoid 将每个节点视为独立的,所以您不必担心重建有效的概率分布。对于我的问题,虽然这种方式建模感觉不真诚,因为动作并不是真正相互独立的,因为我只允许一个机器人每回合采取一个动作(又名 softmax)。
因此,虽然这个系统机械地工作,但对这个问题来说感觉是错误的。这让我回到了如何在这里恰当地使用 softmax 激活的绘图板。
使用 softmax,网络的输出是某种有效的概率分布,我们将具有最高值的动作作为该回合的动作。对一个行为的奖励也意味着其他行为会被抑制,反之亦然。所以对我来说,这在心理上是最有意义的,但我必须弄清楚它的机制,因为修改输出数组的值会破坏这种良好的概率分布。
虽然这花费了我更多的时间,但我最终发现我可以将预测数组的无效索引清零,从选择的移动中增加或减去奖励,然后通过将数组相加并将每个索引除以数组的总和来重新计算概率分布…这是获胜的基本数学!见下文:
Game State: Skill 3 has been used and reward of +1Original: [.2, .5, .3] # sample probabilities for networkModified: [.2, 1.5, 0] # added reward to skill 2 and zeroed skill 3Final: [.117, .882, 0] # .2/1.7 and 1.5/1.7
虽然这对于环境来说并不是一个巨大的变化,但这有助于使代理人的奖励结构和目标在我的大脑中更加具体,并帮助我解决这个问题的其他部分。总的来说,对我来说这是一件好事,因为它有助于处理其他有趣的 RL 问题,其中可能会有大量的无效移动
机器人在最后一轮中放弃了他们剩余的技能。有趣的是,左边的第一个机器人之前使用了一个技能来增加它的致命伤害,这一次使用了一个技能来大量增加它的伤害,而机器人 3 使用了一个技能来增加致命一击的可能性。然后,第一个机器人受到很大的致命一击,结束这一轮。
游戏平衡和培训各种代理
在我开始训练潘德雷肯四人组之前,我从来没有认真考虑过如何平衡游戏环境。我发现,为了让机器人学习有用的行为,我必须非常仔细地考虑我把它们放在什么环境中。
我有几个版本的定制 FGO 环境。我一年前做的第一个可以粗略地认为是 RL 代理人和敌人之间的单轮战斗,他们只是轮流打击对方,直到一个人死亡…我最近的一个有 3 波敌人,代理人必须在代理人的生命值下降到 0 之前击败所有三波。第一个环境大约有 120 点生命值,每回合造成 1 到 3 点伤害。这种设置大致反映了标准的 FGO 农业水平。代理人大约有 30 HP。
在我的潘德雷肯四环境升级后,我开始在一个总 HP 和伤害相似的环境中训练所有 4 个代理,我发现代理有时只是不玩技能,或者其他时候立即玩他们所有的技能而不用担心任何事情。
这两者都不是很有趣,因为它们并没有真正解决任何问题。但是他们胜率并不可怕。在我最初的帖子中,我说过
4 个机器人的随机猜测胜率约为 38%,可能在 20-30K 游戏后达到 72%左右的峰值。在做了一些额外的工作和大量的实验后,我的成功率达到了 84%左右。
这 72%的胜率实际上是 3 个角色的代理人做出了不感兴趣的选择,而拾牌机器人学习并基本上艰难地携带 3 个角色的代理人通过环境。让我在当时达到 84%,并在当前版本中接近 88 %- 90%胜率的部分,实际上是我的训练协议的改变。
我发现的是,我训练选卡员代理(不同的角色有不同的卡片组,因此选卡机器人必须学习玩的卡片组根据角色而不同),然后一旦选卡员代理被训练,我就对各个代理进行一轮训练。这种两步走的方法帮助我进行了最初的开发,并隔离了学习的内容。我认为有助于隔离的另一件事是,如果选卡代理在角色代理学习的同时也在学习,那么角色代理在他们的策略梯度训练中得到的反馈就不那么一致,因为团队的很多损失都是由被选卡造成的。因此,将管道分成几部分也有助于确保角色代理对他们采取的行动获得一致的反馈。
两阶段培训流程:
- 拾牌代理将在一个版本的游戏环境中接受训练。这主要是为了让它学会如何玩一副给定的牌,它是在 DQN 管道中训练的。
- 然后,我把经过训练的拣卡员和三个随机初始化的角色代理放入一个更难的游戏环境中。
我提到最初的游戏环境大约有 120HP,分布在 3 波中,每回合造成 1-3 点伤害。我调整了环境,增加了血量和伤害。我的想法是,由于机器人能够在没有真正尝试原始游戏设置的情况下通过,如果我让游戏变得更难,机器人将无法顺利通过,需要真正开始学习有用的信息才能获胜。查看更恶劣环境的示例:
Round1: 40 HP 1-3 damage
Round2: 60 HP, 2-5 damage
Round3: 80 HP, 4-6 damage
因此,与之前环境中的 120 马力不同,机器人需要额外的 60 马力来对抗,如果它们长时间被困在后面的波中,它们可能会死亡。
在这种更艰难的环境下,机器人的表现看起来并不出色。他们在训练有素的拣卡员和随机初始化的角色机器人的帮助下,开始时胜率约为 35%(4 个随机机器人的胜率约为 3%),在大约 100K 场游戏后,胜率约为 54%。这个结果并不是我们必须关心的,因为我可以将这些机器人放回到更接近 FGO 农业水平的原始游戏环境中,它们有 88-90%的几率获胜。
机器人保存了第三波的伤害 buffs,并删除它们来帮助清除更难的最终波内容。
结束语
我真的没有很多机会同时训练多个神经网络,它们都需要学习和合作才能成功。大多数情况下,如果涉及多个网络,它会以合奏的形式出现,在那里一切都可以如此,所以我在这里必须做的大量基础工作是消除我的心理模型,即我希望所有这些如何运行,并将其隔离开来,以便在给定的时间点具有最少的移动部分。这不是最独特的经验教训,但却是不时重新学习的好经验。
就我目前的培训渠道而言,我看到的是可靠的网络学习策略,但可能不是最有趣的。例如,许多训练在第一波或第二波开始时使用伊师塔的第二技能,因为它让团队立即清除那一波,并使团队受到的早期伤害最小化。
和上面一样的 gif
纯策略梯度机器人,在多次训练中,一个相当一致的事情是机器人会使用 Ishtar(中间角色)的第二技能来立即清除第一波。对于机器人来说,我认为这是一个更稳定的策略,至少让他们有机会进入后期游戏,而不是在他们可能活着也可能不活着的时候保存技能。
当我玩的时候,我倾向于把这些技能留到第三轮。然而,我认为这是因为作为一名玩家,我非常清楚我不太可能在我所玩的任务的第三轮之前死去。机器人真的没有这种不死的精神保证。所以对他们来说,如果他们不尽早利用它,他们可能永远没有机会使用他们的技能。这也可能是缺乏基于我的训练协议、环境、建模等的内置于机器人的长期规划。因此,对于机器人团队来说,在数千场比赛中,使用 Ishtar 的第二技能来清除早期波浪而不是保存它似乎是机器人玩的一种可靠方式,因为它至少给了他们到达终点的机会,而不是冒着死在路上的风险。
所以现在我已经为这些机器人解决了更多的训练过程,我在一个更好的位置开始为不同的角色训练一些额外的代理,并致力于融入更多有趣的团队组成。
企鹅数据集概述— iris 替代方案
一个简单的类似 iris 的数据集,用于教授数据科学概念
如果有一个数据集是数据科学家/数据分析师在学习或指导某人时使用最多的——它要么是iris
(更多 R 用户),要么是titanic
(更多 Python 用户)。
78%的数据营折扣
不使用iris
数据集仅仅是因为它容易访问。它也可以用来演示许多数据科学概念,如相关性、回归、分类。这篇文章的目的是向您介绍penguins
数据集,并让您从几个代码片段开始,这样您就可以脱颖而出了!
最近,由于罗纳德·费雪过去的优生学经历,社区里有越来越多的人希望远离 [iris](https://www.garrickadenbuie.com/blog/lets-move-on-from-iris/)
。
此时,我们有幸得到了另一个类似于iris
的关于penguins
的数据集。感谢 Allison Horst 在 CC-0 许可下将其打包为 R 包[palmerpenguins](https://github.com/allisonhorst/palmerpenguins)
。
YouTube—https://www.youtube.com/watch?v=4zUmlZg9Dd4
视频漫游
请订阅 频道 如果有用请留下反馈。很高兴收到你的来信!
装置
palmerpenguins
尚未发布到 CRAN,因此您可以从 Github 安装它:
remotes::install_github("allisonhorst/palmerpenguins")
访问数据
成功安装后,您会发现有两个数据集附加到包中— penguins
和penguins_raw
。你可以查看他们的帮助页面(?penguins_raw
)来了解更多关于各自的数据集。
加载库
library(tidyverse)
library(palmerpenguins)
penguins
数据集的元一瞥
penguins
数据集有 7 列 344 行
names(penguins)## [1] "species" "island" "bill_length_mm"
## [4] "bill_depth_mm" "flipper_length_mm" "body_mass_g"
## [7] "sex"
在这 7 列中,3 列是分类列(species
、island
、sex
),其余是数字列。
glimpse(penguins)## Rows: 344
## Columns: 7
## $ species <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Ade…
## $ island <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgers…
## $ bill_length_mm <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1,…
## $ bill_depth_mm <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1,…
## $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 18…
## $ body_mass_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475,…
## $ sex <fct> male, female, female, NA, female, male, female, mal…
企鹅数据列定义
种类表示企鹅种类的一个因素(阿德利企鹅、下巴颏企鹅和巴布亚企鹅)
表示南极洲帕尔默群岛中岛屿的一个因素
表示钞票长度的数字(毫米)
表示票据深度的数字(毫米)
脚蹼长度毫米表示脚蹼长度的整数(毫米)
身体质量 g 表示身体质量(克)的整数
性别表示企鹅性别的因素(雌性,雄性)
缺少值
penguins
比iris
好的一点是,它有missing values
NA
。用于教育目的时,在场是一件非常重要的事情!
penguins %>%
#group_by(species) %>%
select(everything()) %>%
summarise_all(funs(sum(is.na(.)))) %>%
pivot_longer(cols = 1:7, names_to = 'columns', values_to = 'NA_count') %>%
arrange(desc(NA_count)) %>%
ggplot(aes(y = columns, x = NA_count)) + geom_col(fill = 'darkorange') +
geom_label(aes(label = NA_count)) +
# scale_fill_manual(values = c("darkorange","purple","cyan4")) +
theme_minimal() +
labs(title = 'Penguins - NA Count')## Warning: `funs()` is deprecated as of dplyr 0.8.0.
## Please use a list of either functions or lambdas:
##
## # Simple named list:
## list(mean = mean, median = median)
##
## # Auto named with `tibble::lst()`:
## tibble::lst(mean, median)
##
## # Using lambdas
## list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
简单散点图
像iris
一样,你可以简单地用 base-R 的plot()
做一个散点图
plot(penguins)
条形图
在这个柱状图中,我们可以看到企鹅数据集中每个物种的数量
penguins %>%
count(species) %>%
ggplot() + geom_col(aes(x = species, y = n, fill = species)) +
geom_label(aes(x = species, y = n, label = n)) +
scale_fill_manual(values = c("darkorange","purple","cyan4")) +
theme_minimal() +
labs(title = 'Penguins Species & Count')
每个物种的条形图
在这个柱状图中,我们可以看到每种性别的物种分布(用分面图)
penguins %>%
drop_na() %>%
count(sex, species) %>%
ggplot() + geom_col(aes(x = species, y = n, fill = species)) +
geom_label(aes(x = species, y = n, label = n)) +
scale_fill_manual(values = c("darkorange","purple","cyan4")) +
facet_wrap(~sex) +
theme_minimal() +
labs(title = 'Penguins Species ~ Gender')
相关矩阵
penguins %>%
select_if(is.numeric) %>%
drop_na() %>%
cor()## bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## bill_length_mm 1.0000000 -0.2350529 0.6561813 0.5951098
## bill_depth_mm -0.2350529 1.0000000 -0.5838512 -0.4719156
## flipper_length_mm 0.6561813 -0.5838512 1.0000000 0.8712018
## body_mass_g 0.5951098 -0.4719156 0.8712018 1.0000000
散点图—企鹅大小与物种的关系
在这个散点图中,我们将尝试可视化每个物种的flipper_length_mm
和body_mass_g
之间的关系。
library(tidyverse)
ggplot(data = penguins,
aes(x = flipper_length_mm,
y = body_mass_g)) +
geom_point(aes(color = species,
shape = species),
size = 3,
alpha = 0.8) +
#theme_minimal() +
scale_color_manual(values = c("darkorange","purple","cyan4")) +
labs(title = "Penguin size, Palmer Station LTER",
subtitle = "Flipper length and body mass for Adelie, Chinstrap and Gentoo Penguins",
x = "Flipper length (mm)",
y = "Body mass (g)",
color = "Penguin species",
shape = "Penguin species") +
theme_minimal()
散点图—企鹅大小与岛屿的关系
library(tidyverse)
ggplot(data = penguins,
aes(x = flipper_length_mm,
y = body_mass_g)) +
geom_point(aes(color = island,
shape = species),
size = 3,
alpha = 0.8) +
#theme_minimal() +
scale_color_manual(values = c("darkorange","purple","cyan4")) +
labs(title = "Penguin size, Palmer Station LTER",
subtitle = "Flipper length and body mass for each island",
x = "Flipper length (mm)",
y = "Body mass (g)",
color = "Penguin island",
shape = "Penguin species") +
theme_minimal()
摘要
正如您在上面看到的,来自palmerpenguins
包的penguins
数据集是iris
数据集的完美替代。它可以很好地用于教授数据科学概念关联、回归、分类,也可以用于教授数据可视化。如果你写了很多数据科学文章,并且总是需要挑选一个非常通用的数据集,penguins
是你应该探索的一个选项。
参考
citation('palmerpenguins')##
## To cite palmerpenguins in publications use:
##
## Gorman KB, Williams TD, Fraser WR (2014) Ecological Sexual Dimorphism
## and Environmental Variability within a Community of Antarctic
## Penguins (Genus Pygoscelis). PLoS ONE 9(3): e90081.
## https://doi.org/10.1371/journal.pone.0090081
##
## A BibTeX entry for LaTeX users is
##
## @Article{,
## title = {Ecological Sexual Dimorphism and Environmental Variability within a Community of Antarctic Penguins (Genus Pygoscelis)},
## author = {Gorman KB and Williams TD and Fraser WR},
## journal = {PLoS ONE},
## year = {2014},
## volume = {9(3)},
## number = {e90081},
## pages = {-13},
## url = {https://doi.org/10.1371/journal.pone.0090081},
## }
最初发布于https://www . programmingwithr . com/penguins-dataset-overview-iris-alternative-in-r/
宾夕法尼亚州自行车与机动车的碰撞
骑自行车的人和想骑自行车的人经常担心受伤的危险。然而,我们对自行车事故和伤害了解多少呢?在本文中,我研究了 30,000 起自行车碰撞事故,以了解发生的碰撞类型(例如,追尾与角度),并使用逻辑模型来了解使碰撞更加严重的因素。我将这些结果与国家运输安全委员会最近的一项研究进行了比较,我发现该研究过于强调自行车设施,如独立的自行车道,而不太强调用户行为的作用。
伊恩·瓦莱里奥在 Unsplash 上拍摄的照片
项目背景
尽管每个州和美国交通部的国家公路交通安全管理局都保存着警方报告的道路交通事故数据库,但关于骑自行车的数据是有限的,因此有时会产生误导。大多数骑自行车的伤害都被排除在这些数据库之外,因为只有涉及交通工具中的机动车辆的碰撞才是可报告的。骑自行车的人与行人、动物或其他骑自行车的人之间的碰撞,或单车碰撞(摔倒或与固定物体的碰撞)被排除在外,即使医院数据显示它们占骑自行车者急诊的大多数。此外,与汽车驾驶员相比,骑自行车的人在事故报告表或事故数据库中通常受到不同的对待,这意味着关于事故情况和操作者行为的关键字段没有被报告、没有被编码或没有被清楚地编码。
通常我们真正想知道的是与不同地点、道路设计或操作者行为相关的碰撞风险。但是风险是结果除以暴露程度,而且很少有公共来源估计不同情况下骑自行车的数量。例如,我们需要知道白天和天黑后骑自行车出行的比例,以了解夜间骑自行车的相对风险。
尽管有这些限制,如果我们找到包含关键属性的数据源,我们可以从碰撞报告中了解很多关于自行车与机动车碰撞的信息。我们可以看看车祸发生率:BMVCs 发生在哪里,在什么情况下发生?一般来说,我们可以假设交通事故的分布主要是自行车使用分布的函数。即使流行率不能提供关于个人撞车风险的信息,但它确实给了我们关于集体问题的位置和环境的信息。
我们还可以看看影响受伤严重程度的因素,假设发生了撞车事故。这与伤害事故发生的建模不是一回事,但它可以帮助我们理解是什么因素使事故更加严重。
我的大型项目利用了美国各州和美国交通部/NHTSA 的几个可用的车祸数据库。当前的分析使用单一来源,宾夕法尼亚州报告的车祸,通过位置类型(限速和城市或农村)、交叉路口或中间街区、碰撞方式和其他特征来查看车祸发生率,并创建伤害严重度模型。
这项研究的最终目的是指导改善骑自行车者安全的对策的实施。这个话题在 2019 年 12 月发布的国家运输安全委员会自行车报告中有所涉及。我将我对碰撞类型、位置和伤害严重程度的研究结果与 NTSB 报告中的一些数据和模型进行了比较,特别是他们对基础设施的建议,如独立的自行车道。
数据
这项分析的数据来自 PennDOT,它为宾夕法尼亚州提供了 20 年的年度车祸数据。我下载了 1999 年到 2019 年的年度档案。(2001 年的档案不见了,多次要求 PennDOT 提供这些档案都没有成功。)PennDOT 为每一年提供了几个文件,代表关系数据库中由关键字段链接的单独的表。这些表包括碰撞、人员、车辆和道路。不同于几乎所有其他州的车祸数据库,骑自行车的人和机动车的人一样被编码。自行车包含在车辆文件中(如 VEH_TYPE =“自行车”或“其他骑自行车的人”)。
我使用的最终数据集包含 29,726 名骑自行车的人和 30,090 名开车的人,涉及 29,489 起车祸。
撞车流行率
位置特征
宾夕法尼亚州 80%以上的自行车-机动车碰撞(BMVCs)发生在城市地区。三分之二发生在限速 25 英里或更低的地方。虽然低速道路占主导地位,但大约四分之一的 BMVCs 发生在限速 30-35 英里/小时的地方,主要是在城市地区。只有 10%的 BMVCs 发生在限速 40 英里/小时或以上的地方,其中约 40%发生在农村地区。
按限速和城市/农村划分的车祸发生率:所有车祸
如果我们只考虑导致严重或致命伤害的撞车事故,这种情况会发生显著变化。这些严重或致命的 BMVCs 中只有一半是在限速为 25 英里/小时或更低的街道上,27%的限速为 30-35 英里/小时,另外 27%的限速为 40 英里/小时或更高。与所有撞车事故的分布相比,农村地区也不成比例。致命的撞车事故更倾向于高速街道和农村地区:44%的致命 bmvc 发生在农村地区(占所有 bmvc 的 19%),66%发生在限速 30 英里/小时或更高的地方(占所有 bmvc 的 32%)。
按限速和城市/农村划分的车祸发生率:严重和致命伤害车祸
道路系统中撞车的位置呢?宾夕法尼亚州的数据编码区分了发生在交叉路口(多条道路共享的区域)内的碰撞和与交叉路口相关的碰撞(通常发生在实际交叉路口 100 英尺内的与转弯和交叉运动相关的碰撞)。我综合了交叉路口和与交叉路口相关的车祸。车道,即使是有交通信号的大型商业车道,也不算十字路口。我使用了一个单独的特征来识别车道(该类别还包括停车场碰撞)。从功能上来说,车道和十字路口是一样的。
如下图所示,28%的 BMVCs 发生在交叉口之间(“中间阻断”)。其余的 72%发生在十字路口(或与十字路口相关的)或车道上。到目前为止,四向交叉是最常见的,但丁字交叉也是很重要的一部分。
按交叉口类型/中间街区划分的碰撞发生率
如果我们只考虑造成严重或致命伤害的撞车事故,40%是中撞事故。就致命车祸而言,50%发生在中段。很明显,中段碰撞更有可能是严重的。然而,大多数严重的车祸发生在十字路口或车道上,就死亡事故而言,这两类地点的车祸发生率相当。
碰撞类型
如下图所示,近 70%的 BMVCs 是角度碰撞。接下来最常见的是侧撞和追尾。迎面和侧撞-相反方向的碰撞类型意味着骑自行车的人或开车的人在道路的错误一侧。
碰撞方式:所有碰撞
仅考虑下图所示的严重和致命伤害 BMVCs,我们发现角度碰撞仍然占主导地位。追尾事故的重要性增加了,但只占总数的 11%。正面碰撞的重要性也增加了。仅考虑致命伤害,追尾碰撞的重要性上升到 18%,但大多数,58%,仍然是角度碰撞。
碰撞方式:严重和致命伤害碰撞
大多数角度 bmvc(83%)发生在十字路口或车道,大多数追尾 bmvc(64%)发生在中间块。然而,只有一小部分中间块碰撞是追尾碰撞:41%是角度碰撞(包括骑自行车的人在十字路口之间过街的情况),27%是同方向侧撞,14%是追尾,7%是正面碰撞,4%是相反方向侧撞,其余 7%是其他或未知情况。追尾和同方向侧撞碰撞在严重和致命bmvc 中所占比例较高(分别为 23%和 17%),但角度碰撞占这些中间块 bmvc 的 36%。
追尾和横炮碰撞的发生率
NTSB 自行车安全报告建议将自行车道分隔作为减少严重碰撞的主要方法,认为它们可以“几乎消除从后面撞上,超车和侧撞事故”然而,在宾夕法尼亚州,追尾和侧撞仅占严重 BMVCs 事故的 18%和致命 BMVCs 事故的 27%。此外,单独的自行车道通常安装在自行车密度最高的地区:限速为 30 英里/小时或更低的城市街道。根据自行车人库存的数据,截至 2020 年 10 月,宾夕法尼亚州所有现有和拟议的独立自行车道都在费城、匹兹堡和兰开斯特。这些道路上张贴的限速是每小时 25 英里,只有一条道路是每小时 30 英里。在这些低速城市道路上,基础设施针对的碰撞类型在问题中所占的比例甚至更小:只有 12%的严重 BMVCs 和 11%的致命 BMVCs。
在该数据集中的 325 起骑自行车死亡事故中,只有 11 起是在低速(< =30 英里/小时)城市道路上发生的追尾或侧撞事故,其中驾车者是撞击方。其中,五起发生在夜间,当时主要因素是能见度,特别是因为许多骑自行车的人在夜间不使用灯(宾夕法尼亚州的数据记录了使用后反射器,但没有使用尾灯)。在白天发生的六起事故中,有三起涉及危险驾驶的犯罪行为:
- 2006 年 3 月,一名法定盲人司机在州立学院的波尔斯堡路漂移到路肩,杀死了一名骑自行车的人,他也是一名大学教授。
- 2015 年 7 月,一名 21 岁的无证司机快速加速,超过了停在她面前车道上的一辆汽车,费城第二大街的每个方向都有一条狭窄的车道。经过那辆车后,她撞上了停在那辆车前面的一个骑自行车的人。她逃离了现场,拿走了汽车牌照,但是被抓住并被指控谋杀。
- 2015 年 10 月,在匹兹堡的福布斯大道上,一名吸食大麻的驾车者撞上了一辆停下来的汽车,然后又撞上了一名停下来的自行车手,后者是一名大学教授。逃离现场的司机被判 5-10 年徒刑。
其他三起事故似乎不是我们通常认为的追尾事故:
- 2000 年 6 月,一名骑自行车的人在费城的 Frankford 大道和 Ashburner 街的交叉口被杀,这是一个信号控制的 T 型交叉口。一名损伤情况不明的司机在左车道行驶,与一辆停下的汽车追尾。两辆机动车中的一辆撞上了在右车道骑自行车的人的左侧。
- 2003 年 11 月,在费城东豪厄尔街附近的托雷斯代尔大道,一名骑自行车的人在路边停车场旁边的自行车道上被杀害。一辆运动未知的客车卷入了一场“非碰撞”——可能是从停车或开门开始移动。骑自行车的人摔倒在一辆同方向行驶的重型卡车下面。
- 2009 年 8 月,一名骑自行车的人在匹兹堡 Meyran 大道和 Luisa 街的交叉口被一辆大卡车撞死,这是两条狭窄街道的 4 向交叉路口。骑自行车的人的位置、运动和行进方向是未知的。逃离现场的司机被编码为“分心”
车辆和操作员特征
BMVCs 涉及哪些类型的机动车辆?大约三分之二是乘用车,30%是轻型卡车(SUV、货车、皮卡和其他小型卡车)。只有 1%是大型卡车,另外 1%是公共汽车(主要是过境公共汽车)。当我们看严重和致命伤害时,小型卡车的份额从 9%上升到 14%,大型卡车的份额从 1%上升到 4%。仅考虑致命 BMVCs,小型卡车份额为 15%,大型卡车份额为 7%,客车份额为 2%。
机动车类型
不到 2%的司机受到 BMVCs 的影响(主要是酒精或药物)。当我们看到严重或致命的伤害事故时,这一数字上升到 6%,仅致命事故就上升到 15%。总体而言,只有 1.4%的司机超速行驶,但 4%是严重或致命事故,7%是致命事故。
伤害严重度模型
下表显示了该数据集中大约 30,000 名骑自行车的人在撞车事故中的受伤严重程度。由于这些都不是医疗记录,受伤的程度往往是不确定的。将近一半被编码为“可能受伤”,另外 19%被编码为“严重程度未知的受伤”超过 1%的人受到致命伤害,5.5%的人疑似受到严重但非致命的伤害。
我决定做一个简单的二元模型,将严重或致命伤害的结果与所有其他结果(无伤害或轻微、可能或未知伤害)进行比较。我决定不采用多级模型,原因如下:
- 二元模型更容易解释
- 对致命和严重但非致命的伤害进行单独分类将是有益的,但只有 325 起死亡事故,这使得更难找到具有统计意义的结果
- 在那些不严重或不致命的案例中,有许多是“可能的”或“未知的”伤害严重程度,增加了类别的不确定性。
- 选择的模型与 2019 年 12 月发布的国家运输安全委员会自行车报告中创建的模型相当。
我使用 statsmodels python 库创建了一个逻辑回归模型,以严重或致命伤害(编码为 1)或任何其他伤害状态(编码为 0)为因变量。对每个系数求幂提供了与该特征相关的概率。所有的特征都是分类的,其中一个类别被排除在模型之外,所以优势表示与被排除或“参考”类别相比的优势比率。比值比为 1 意味着建模类别和参考类别之间没有差异。比值比大于 1 意味着与参考相比,建模的类别具有正面影响,而比值比小于 1 意味着它具有负面影响。我计算了与每个估计比值比相关的 95%置信区间:如果区间包括 1,则没有足够的信息来确定是否有积极或消极的影响。
尽管效果的方向(积极、消极或无)很容易理解,但优势比的大小没有直观的解释。然而,对于不常见的因素,优势比是相对风险的合理近似值,这确实有一个简单的解释:例如,相对风险为 2,意味着该因素的风险是参考的两倍。使用完整数据集的第一个模型的优势比及其置信区间绘制如下。
模型 1:完整的数据集
位置特征
- 与城市地区相比,农村地区的优势比为 2.0,这意味着严重或致命伤害的风险高于城市地区,即使控制了模型中的所有其他特征。
- 严重受伤的风险随着张贴的速度限制的提高而增加。最高级别,50+英里/小时,与 25 英里/小时或更低的参考级别相比,OR 值为 2.1。
- 与交叉口相比,中间街区位置的 OR 为 1.5。
车型
- 涉及公共汽车的 OR 值为 2.4。
- 我把小卡车的参与作为一个特征,以及小卡车和中间街区位置之间的相互作用。代表不在街区中部(即在十字路口)的小型卡车碰撞的部分的 OR 值为 1.8。
- 与小型卡车一样,大型卡车和中间块之间的相互作用也包括在内。同样,中间街区部分并不显著,但卡车/交叉口效应的 OR 值为 5.3,是模型中最高的特征。
照明条件
参照类别是日光。相比之下:
- 黑暗条件下(大部分是明亮的,但也有一些未知的照明)的优势比为 1.5
- 黑暗和无光条件下的 OR 值为 1.8
- 黎明时分的撞车事故的 OR 值为 2.4
- 没有证据表明黄昏时的撞车事故比白天更严重。
碰撞类型
与角度碰撞(目前最常见的类型)相比:
- 侧撞(同方向)碰撞不太可能是严重的(或 0.5)
- 侧撞(相反方向)碰撞也不太可能是严重的,但这种影响在统计学上并不显著
- 追尾事故更可能是严重的,OR 值为 1.2
- 正面碰撞更可能是严重的,OR 值为 1.5
- 所有其他类型的碰撞都没有统计学上的显著影响,部分原因可能是因为它们很少发生。
骑自行车的人被撞(而不是撞击)的事故更可能是严重的,OR 值为 1.2。
操作员行为
- 与酒精相关的车祸更可能是严重的(或 2.3 倍)。
- 同样,与超速相关的撞车事故的 OR 值为 2.4。
- 最大的影响是司机使用药物的车祸(or 为 3.4)。
- 与预期相反,骑自行车的人戴头盔更有可能受重伤(或 1.2 倍)。这个结果可能是由于糟糕的编码:12%的骑自行车的人戴着头盔,41%的人没有戴头盔,剩下的 48%的人不知道头盔的用途。因此,参考组(包括“否”和“未知”)可能有许多头盔使用者。
- 与我的预期相反,肇事逃逸事故与严重受伤的几率较低有关。
虽然没有在 OR 图中显示,但是模型也包括了骑车人的年龄和性别。女性骑自行车者受重伤的可能性略低(OR 值为 0.8)。只有三个年龄组在统计上与其他年龄组不同:20-29 岁的人不太可能受到严重伤害(或 0.7),60-69 岁(或 1.5)和 70 岁及以上(或 1.7)的骑自行车者更有可能受到严重或致命的伤害。
城市区域模型
包括 NTSB 自行车研究在内,碰撞对策的大部分重点是在城市地区安装自行车专用基础设施。州数据中三分之二(66%)的 BMVCs 发生在限速 30 英里/小时或更低的城市地区。我为全州范围内的车祸建立了一个独立的模型。包括的特征是相同的,除了我必须排除城市/乡村和速度限制,它们在这个数据子集中没有变化。
模式 2:城市地区张贴限速< = 30 英里/小时
以下是与第一个模型相比的不同之处:
位置特征
- 中段或略有下降,从 1.5 降至 1.4
车型
- 公共汽车或从 2.4 增加到 3.1。
- 小型货车/交叉路口或从 1.8 提高到 1.9。
- 大型货车/交叉路口或从 5.3 增加到 6.6。
照明条件
- 黑暗条件或从 1.5 下降到 1.2。
- 黑暗和无光条件或从 1.8 降至 1.7。
- 黎明时分的撞车和白天不再有什么不同。
碰撞类型
- 侧撞(反方向)碰撞的新的有效 OR 为 0.5
- 追尾碰撞与角度碰撞的严重程度不再不同
- 正面碰撞的优势比略有下降,从 1.5 降至 1.4
操作员行为
- 与酒精相关的车祸的优势比从 2.3 上升到了 2.7。
- 与超速有关的车祸的优势比从 2.4 下降到 1.8。
- 涉毒撞车的 OR 从 3.4 下降到 2.1。
- 骑自行车的人使用头盔没有显著影响。
- 肇事逃逸事故没有显著影响。
女性和老年骑自行车的人与男性骑自行车的人相比,受伤的几率不再有统计学差异。
模型结果的讨论
伤害严重程度模型让我们估计控制模型中其他特征的各种特征的独立效果。结果表明,与城市地区相比,由于车速限制较高,农村地区发生严重伤害的几率会增加。中部碰撞位置也增加了严重程度。在黑暗的时间里,特别是在没有灯光的道路上和黎明时分,撞车会增加严重的风险。涉及公共汽车和卡车的碰撞更可能是严重的,尤其是在大型卡车在十字路口碰撞的情况下。驾驶障碍对严重程度也有很大的影响。
正面碰撞和追尾比其他类型的碰撞更可能严重,但追尾只是轻微的。与最常见的角度碰撞相比,骑自行车的人和开车的人同向行驶的横炮碰撞不太可能严重或致命。
使用城市低速道路碰撞的模型在几个重要方面有所不同。在市区,追尾不太可能比角度碰撞更严重。司机超速,司机吸毒,黑暗是严重性的不太强的预测因素,但仍然是最重要的。司机饮酒成为某种程度上更强的严重程度的预测因素。最重要的预测因素是车辆类型,这在所有类别中都得到了加强——公共汽车、小型卡车和大型卡车。就卡车而言,增加的风险仅针对交叉路口的碰撞。
我将这些结果与 NTSB 自行车报告中的伤害严重程度进行了比较,如下表所示。农村的影响实际上是一样的。然而,中块碰撞位置的影响(与十字路口或车道相比)较低,更高速度限制的影响也较低,最显著的是最高速度类别。造成这些差异的一个可能原因是,我的模型中包含了许多其他因素,但 NTSB 模型却忽略了这些因素。然而,当我仅使用这些特征重新评估模型时,限速类别的 ors 略有增加(增加到 1.5、2.2 和 2.2,仍远低于 NTSB ORs),农村 OR 没有变化,中街区 OR 减少到 1.3。因此,NTSB 模型中遗漏的变量并不是导致结果不同的主要原因。
与 NTSB 模式对比
NTSB 模型使用了来自四个州的 2017 年数据:宾夕法尼亚州、明尼苏达州、得克萨斯州和华盛顿州。NTSB 报告这四个州当年共有 6,661 辆 BMVCs。我的数据显示,宾夕法尼亚州占 1150(17%),得克萨斯州占 3345(51%)。NTSB 的数据偏向得克萨斯州,在得克萨斯州按速度限制的 BMVCs 的分布与宾夕法尼亚州非常不同,如下图所示。得克萨斯州骑自行车的人在街道上使用的速度限制要高得多,这可能是 NTSB 模型与当前模型相比速度效应差异的原因。
结论
与大多数其他美国碰撞数据源不同,宾夕法尼亚州数据库提供了自行车与机动车碰撞(BMVCs)方式的相关信息。近 70%的 BMVCs 是角度碰撞,其中大多数发生在十字路口或车道上——大多数转弯和交叉运动都发生在那里。追尾事故只占总数的 8%。这些数字表明,解决导致角度碰撞的因素将对减少 BMVCs 产生最大的影响。
不幸的是,宾夕法尼亚州的数据缺乏关于骑自行车者行为的关键信息,这些信息可以帮助我们确定角度碰撞的原因:具体来说,碰撞前骑自行车者的位置和方向——在道路上或人行道上,以及是否有交通。这个数据集也没有告诉我们哪一方违反了通行权规则。我将使用其他状态数据库分别处理这些主题。
NTSB 报告主要关注作为潜在对策的道路设计,并强调中间块碰撞比交叉碰撞更有可能导致严重伤害,并假设大多数中间块碰撞可以通过在道路上设置物理屏障来避免。然而,佩恩。数据显示,41%的中间块碰撞实际上是角度碰撞,而不是 NTSB 提出的独立自行车道对策的目标追尾和侧撞碰撞。另外 11%涉及错误方式的操作者。
我的受伤严重程度模型证实了 NTSB 的发现,即中块碰撞更可能是严重的,但在 NTSB 模型中,风险是 1.5 倍,而不是 2 倍。此外,我评估了具体的碰撞类型(追尾和侧撞),这可能会受到 NTSB 提出的自行车道分离的影响。与角度碰撞相比,追尾碰撞更有可能导致严重或致命的伤害,但不是在低速的城市道路上,因为大多数独立的自行车道都是在低速的城市道路上安装的。横炮碰撞比角度碰撞严重得多。
我发现伤害严重程度的风险随着张贴的速度限制的增加而增加,尽管没有 NTSB 模型中那么明显。这种差异可能是由 NTSB 数据中德克萨斯州撞车事故的高比例,以及发生自行车撞车事故的德克萨斯州道路上更高的张贴速度所解释的。在有高速公路的城市地区,有几种选择可以减少骑自行车的风险,例如:道路饮食、环形路和其他旨在减缓交通的道路重新设计,以及提供通过低速街道的替代、连接路线。
我发现 NTSB 模型中忽略了其他几个因素,这些因素在确定伤害严重程度时与街区中心位置同等重要或更重要:卡车或公共汽车卷入,尤其是在十字路口;驾驶障碍;和黑暗。所有这些因素都可以通过非基础设施措施来解决,例如:让骑自行车的人意识到在十字路口和车道上跟在卡车和公共汽车后面而不是旁边的重要性;减少酒后和吸毒后驾车和超速的发生率;以及在夜间增加自行车上的灯的使用。减少逆行自行车可以减少最严重的正面碰撞。所有这些措施除了降低碰撞严重程度之外,几乎肯定会降低碰撞频率。
这个项目的 python 笔记本和数据可以在 Github 上找到。
人们对澳大利亚总理的看法
使用 Twitter 数据进行情感分析
这张照片是我在访问堪培拉时拍的
"斯科特·莫里森的假期不是问题,他对森林大火缺乏领导才是问题所在!"
凯瑟琳·墨菲
“澳大利亚对冠状病毒感到恐惧和困惑。在这个严峻的时刻,斯科特·莫里森是我们需要的领袖吗??"
理查德·弗拉纳根
斯科特·莫里森(ScoMo)是澳大利亚第 30 任总理,也是自 2018 年以来的自由党领袖。我不支持任何政党,但是,在我呆在澳大利亚期间,我从来没有听到过关于我们总理的一句好话,我确信大多数人不喜欢他。
我很想知道大多数人对 ScoMo 的看法。但是在哪里可以找到这样的信息呢?我们可以利用一个资源来获取数据。现在是 推特T3。
Twitter 是一个微博客系统,允许你发送和接收被称为 tweets 的简短帖子。推文可以长达 140 个字符,可以包括相关网站和资源的链接。这是一个社交媒体平台,每天有 1 . 45 亿活跃用户和 5 亿条推文。
现在我们知道在哪里可以找到数据。但是,
- 如何从 twitter 中提取数据?
- 如何根据这些数据检查人们的意见?
这就是我这篇文章要讨论的内容。
首先我们需要对 情感分析 有一个基本的概念。
什么是情感分析?
本质上,它是确定一系列词语背后的情感基调的过程,用于理解在线提及中表达的态度、观点和情绪。
好!等够了。让我们深入研究代码,
点击这里查看我在 GitHub 上的完整项目。
1.提取数据
要提取 twitter 数据,您需要有一个 Twitter API。我将向您展示如何创建一个。
- 申请 Twitter 开发者账户并获得批准。
- 创建一个 Twitter 开发者应用
- 生成您的应用程序的 API 密钥和用户访问令牌。
有三种类型的搜索 API,这里我们使用的是标准搜索 API。
让我们看看如何在代码中使用它
导入库
- tweepy——使用 Twitter APIs。这是这个库的文档。
- CSV-操作 CSV(逗号分隔值)文件。
import tweepy as tw
import csv
设置 API 密钥和访问令牌
consumer_key = "xxxxxxxxxxxxx"
consumer_secret = "xxxxxxxxxxxxxx"
access_token = "xxxxxxxxxxxxx"
access_token_secret = "xxxxxxxxxxxxxx"
创建到 Twitter API 的连接
auth = tw.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tw.API(auth,wait_on_rate_limit=True)
创建并打开一个 CSV 文件以追加数据
file = 'data/raw/scomo.csv'csvFile = open(file, 'w+', newline="",encoding="utf-8")csvWriter = csv.writer(csvFile)csvWriter.writerow(["user_id","tweet_created_time","tweet_text","tweet_length","user_created_at","user_followers_count","user_friends_count", "twitter_source","tweet_likes","tweet_retweets"])
文件头包括我决定从一条推文中提取的十个特征。
提取数据并附加到 CSV 文件
tweets = tw.Cursor(api.search,q=search_string,count=200, lang="en",since=date_since)for tweet in tweets.items():
csvWriter.writerow([tweet.id_str,
tweet.created_at,
tweet.text.encode("utf-8"),
len(tweet.text),
tweet.author.created_at,
tweet.author.followers_count,
tweet.author.friends_count,
tweet.source,
tweet.favorite_count,
tweet.retweet_count
])
这里是 extracted_data.csv 文件。
我在 2014 年 1 月 20 日和 2014 年 9 月 20 日之间收集了 43554 条推文。我用了所有的推文进行分析。Twitter 标准 API 只允许我们回溯十天。如果 tweet 的数量与关键字高度相关,我们将能够收集更多的 tweet。
2.预处理数据
当我们处理数据时,这一步至关重要。提取的数据以原始格式存在,这对于分析是不可行的。这就是为什么我们需要清理数据,然后转换成一个干净的数据集。这里我使用以下两个库来完成任务。
预处理器 —预处理器是用 Python 编写的 tweet 数据的预处理库。这个库使得清理、解析或标记 tweets 变得容易。
re — re 是 Python 的正则表达式(RegEx)库,它负责解析字符串并有效地修改它们,而不必显式地遍历组成特定字符串的字符。
导入库
#python libraries
import pandas as pd
import numpy as np#Regular expression and clean data
import preprocessor as p
import re#processing textual data
from textblob import TextBlob
from textblob import Word#nltk library for stopwords
import nltk
from nltk.corpus import stopwords
让我们将提取的数据从 extracted_data.csv 读取到 Panda 数据帧中。
raw_data = pd.read_csv('data/raw/scomo.csv', usecols=['tweet_text'])
数据集的前 20 行
让我们继续数据清理过程。
1.删除网址,提及,标签,保留字,表情符号,笑脸和数字
raw_data['tweet_text'] = np.array([ p.clean(tweet) for tweet in raw_data['tweet_text'] ])
预处理程序库中的 clean()函数删除一条推文中的 URL、标签、提及、保留字(RT、FAV)、表情符号、表情符号和数字。
执行 clean()函数后的数据
2.删除标点符号
标点符号在分析时没有帮助。最好从文本中删除它们,就像我们使用 clean()函数删除它们一样。
raw_data['tweet_text'] = np.array([ re.sub(r'(@[A-Za-z0-9]+)|([^0)-9A-Za-z \t])|(\w+:\/\/\S+)', "", tweet) for tweet in raw_data['tweet_text'] ])
3.删除第一个字母“b”
正如你在下面看到的,我们可以在每一行前面找到字母‘b’,它没有任何用处。
我们可以通过调用下面的代码行来删除它们。
raw_data['tweet_text'] = np.array([ tweet[1:] for tweet in raw_data['tweet_text'] ])
4.小写字母盘
将所有字母转换成小写,使它们更精确。
raw_data['tweet_text'] = raw_data['tweet_text'].apply(lambda x: " ".join(x.lower() for x in x.split()))
5.删除停用词
停用词是英语中常用的词(如“the”、“a”、“an”、“in”)。它们不会给上下文增加价值。我们不希望这些单词占用我们的数据库空间,或者占用宝贵的处理时间。
stop_words = stopwords.words('english')raw_data['tweet_text'] = raw_data['tweet_text'].apply(lambda x: " ".join(x for x in x.split() if x not in stop_words))
6.生僻字去除
有时,您需要删除独特的单词,如名称、品牌、产品名称和一些干扰字符,如 HTML 省略符号。这被认为是一个罕见的词删除。
freq = pd.Series(' '.join(raw_data['tweet_text']).split()).value_counts()[-1000:]
freq = list(freq.index)
raw_data['tweet_text'].apply(lambda x: " ".join(x for x in x.split() if x not in freq))
7.词汇化
词汇化是一个寻求分析一个词的预期意义而不是它的基本形式的过程。
例如;
- 这个男孩的汽车是不同的颜色。
- 男孩可以是不同的颜色。
raw_data['tweet_text'] = raw_data['tweet_text'].apply(lambda x: " ".join([Word(word).lemmatize() for word in x.split()]))
清理数据后,推文如下…
干净的文本
将 tweet length、like 和 retweets 列连接到最终数据集
data = pd.read_csv('data/raw/scomo.csv', usecols=['tweet_length','tweet_likes','tweet_retweets'])processed_df = pd.concat([raw_data, data], axis=1)
将处理后的数据帧作为新的 CSV 文件写入
processed_df.to_csv('data/processed/processed_data.csv',index=False)
3.分析并可视化结果
由 Carlos Muza 在 Unsplash 上拍摄的照片
这里我们已经到了工作的最后部分。
我已经设法以比原始版本更有意义的方式清理了推文文本,这将有助于识别文本的情绪。
如何进行情感分析
我们使用text blob库
TextBlob 是一个用于处理文本数据的 Python (2 和 3)库。它提供了一个简单的 API,用于处理常见的自然语言处理(NLP)任务,如词性标注、名词短语提取、情感分析、分类、翻译等。
导入库
import pandas as pd
import numpy as np#sentiment analysis
from textblob import TextBlob
from textblob import Blobber
from textblob.sentiments import NaiveBayesAnalyzer#for visualizations
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
将 processed_data.csv 读入数据帧
data = pd.read_csv('data/processed/processed_data.csv')
用于分析的已清理数据集
找到极性和主观性
极性,又称倾向性,是句子中表达的情感。它可以是积极的、消极的或中性的。主观性是指文本是一篇必须在上下文中分析的信息性文章。
极性是一个位于[-1,1]范围内的浮点数,其中 1 表示肯定陈述,而-1 表示否定陈述。主观句一般指个人观点、情感或判断,而客观句指事实信息。主观性也是一个位于[0,1]范围内的浮点数。
textblob 的情感函数返回两个属性,极性,和主观性。
data['polarity'] = np.array([ TextBlob(str(tweet)).sentiment.polarity for tweet in data['tweet_text']])data['subjectivity'] = np.array([ TextBlob(str(tweet)).sentiment.subjectivity for tweet in data['tweet_text']])
计算的极性和主观性被添加到数据集中。
具有极性和主观性的数据集
让我们看看一些统计和图形可视化的分析
1.生成数据帧的统计数据
让我们看看数据帧的数值的百分位数、平均值和标准差等统计数据是什么。我们可以用描述熊猫的功能。
2.创建并可视化相关矩阵
相关矩阵是显示变量之间相关系数的表格。表中的每个单元格显示了两个变量之间的相关性。
相关矩阵
我们将创建一个热图来可视化这种关联。热图是以二维形式表示数据的一种方式。数据值在图表中用颜色表示。
plt.figure(figsize=(10,6))
sns.heatmap(data.corr(), annot=True)
plt.title('data correlations')
根据热图,
- 推文点赞和转发显示最高相关度
- 极性和情绪显示第二高的相关性
3.极性、主观性和微博长度的分布
我们将分别为两个字段创建一个 distplot 图,以查看数据集中的值是如何分布的。
3.1 极性分布
plt.figure(figsize=(10,4))
plt.title('Distribution of polarity')
sns.distplot(data['polarity'].dropna(), kde=False, bins=40)
极性分布
3.2 主观性分布
plt.figure(figsize=(10,4))
plt.title('Subjectivity of polarity')
sns.distplot(data['subjectivity'].dropna(), kde=False, bins=40)
主观性分布
3.3 推文长度分布
plt.figure(figsize=(10,4))
plt.title('Distribution of tweet length')
sns.distplot(data['tweet_length'].dropna(), kde=False, bins=40)
推文长度分布
3.4 极性和主观性的组合分布
我们可以创建一个 jointplot 图来显示两个变量之间的关系。
sns.jointplot(x='polarity',y='subjectivity',data=data,kind='scatter')
4.决定情绪
正如我上面提到的,我们可以通过使用极性值来发现情绪。如果,
- 极性< 0: negative sentiment
- polarity = 0: neutral sentiment
- polarity > 0:积极情绪
让我们创建一个函数来计算情绪。
def select_sentiment(value):
if value>0:
return 1
elif value==0:
return 0
else:
return -1
产生情感价值。
data['sentiment'] = np.array([select_sentiment(p) for p in data['polarity']])
带有情感值的数据集
5.想象这种情绪
5.1 每个类别中出现的次数
我们可以使用 计数图 来显示使用条形的每个分类箱中的观察计数。
plt.figure(figsize=(6,4))
plt.title('Number of occurence of each category')
sns.countplot(x='sentiment',data=data,)
情绪的计数图
根据图表,出现次数最多的是在空档类别。
5.2 基于情感的推文长度
我们可以使用 条形图 来显示数据中分类特征的聚合数据。我们可以观察一条推文的长度在每个类别中的指向。
plt.figure(figsize=(6,4))
plt.title('Tweet length based on sentiment')
sns.barplot(x='sentiment',y='tweet_length',data=data)
基于情感的推文长度柱状图
5.3 基于情感的推文长度分布
我们可以用一个 箱线图 来显示分类数据的分布。该框显示数据集的四分位数,而触须延伸以显示分布的其余部分。
plt.figure(figsize=(10,7))
plt.title('Distribution of tweet length based on sentiment')
sns.boxplot(x="sentiment", y="tweet_length", data=data)
基于情感的推文长度分布箱线图
5.3 基于情感类别的百分比
使用以下代码计算每个情感类别的百分比。
pos_tweets= [ tweet for index, tweet in enumerate(data['sentiment']) if data['sentiment'][index] == 1]
nue_tweets= [ tweet for index, tweet in enumerate(data['sentiment']) if data['sentiment'][index] == 0]
neg_tweets= [ tweet for index, tweet in enumerate(data['sentiment']) if data['sentiment'][index] == -1]pos_percentage = len(pos_tweets)*100/len(data)
nue_percentage = len(nue_tweets)*100/len(data)
neg_percentage = len(neg_tweets)*100/len(data)
根据这些值绘制一个饼图。
fig = plt.figure()
percentage = pd.Series([pos_percentage,nue_percentage,neg_percentage],
index=['positive','nuetral','negative'],
name='Sentiment analysis')
percentage.plot.pie(fontsize=9, autopct='%.2f%%', figsize=(8,8));
plt.figure()
情感类别饼图
6.决赛成绩
分析的结果如下。调查显示,50.19%的人对 ScoMo 持中立态度(无论是负面还是正面),32.36%的人持正面态度,17.45%的人持负面态度。
结论
根据分析,更多的人(50%)对 ScoMo 持中立态度。如果我们能够收集更长时间的数据,而不是十天,这个结果是可以改变的。此外,世界当前局势和最近发生的事件等事实会影响推文的行为。
进一步扩展
我只用了推文的文字。诸如日期/时间、地理位置等其他属性可用于进一步分析。此外,我们可以观察哪些是经常使用的单词,并寻找模式和训练模型以供将来使用。
感谢您的阅读!
照片由 Riz Mooney 在 Unsplash 上拍摄
感知机学习及其在 Python 中的实现
本博客涵盖以下主题:
- 什么是感知器?
- 算法。
- 实施。
- 局限性。
什么是感知器?
生物神经元示意图
感知器的概念类似于大脑的基本处理单元——神经元的工作原理。神经元由树突、细胞体携带的许多输入信号和轴突携带的一个输出信号组成。当细胞达到特定阈值时,神经元会发出一个动作信号。这个动作要么发生,要么不发生;神经元的“部分”放电是不存在的。
类似地,感知器有许多输入(通常称为特征),这些输入被送入一个线性单元,产生一个二进制输出。因此,感知器可以应用于解决二元分类问题,其中样本将被识别为属于预定义的两个类别之一。
该算法
感知器示意图
由于感知器是二元分类器(0/1),我们可以将它们的计算定义如下:
让我们回忆一下,两个长度为 n (1≤i≤n)的向量的点积是
w。x = ∑ᵢ wᵢ。xᵢ
函数 f(x)=b+w。 x 是权重和特征向量的线性组合。因此,感知器是一种线性分类器——一种使用线性预测函数进行预测的算法。
权重表示 x 中每个特征 xᵢ对模型行为的有效性。要素 xᵢ的权重 wᵢ越高,其对输出的影响就越大。另一方面,偏差“b”就像线性方程中的截距。这是一个帮助模型以最适合数据的方式进行调整的常数。偏置项假设输入特征系数 x₀=1.为虚数
可以使用以下算法来训练该模型:
1\. set b = w = 0
2\. for N iterations, or until weights do not change
(a) for each training example xᵏ with label yᵏ
i. if yᵏ — f(xᵏ) = 0, continue
ii. else, update wᵢ, △wᵢ = (yᵏ — f(xᵏ)) xᵢ
履行
我们考虑用于实现感知器的数据集是虹膜花数据集。该数据集包含 4 个描述花的特征,并将它们分类为属于 3 个类中的一个。我们去除属于类‘Iris-virginica’的数据集的最后 50 行,并且仅使用 2 个类‘Iris-setosa’和‘Iris-versicolor ’,因为这些类是线性可分的,并且算法通过最终找到最优权重而收敛到局部最小值。
可视化包含其中两个要素的数据集,我们可以看到,通过在它们之间画一条直线,可以清楚地将数据集分开。
我们的目标是编写一个算法,找到那条线,并正确分类所有这些数据点。
现在我们实现上面提到的算法,看看它是如何工作的。我们有 4 个特征,因此每个特征有 4 个权重。请记住,我们定义了一个偏差项 w₀,它假设 x₀=1 总共有 5 个权重。
我们将迭代次数定义为 10 次。这是超参数之一,与算法学习的 w 等系统参数相反。在每次迭代中,该算法计算所有数据点的类别(0 或 1 ),并随着每次错误分类更新权重。
如果样本被错误分类,则权重通过向相反方向移动的增量来更新。所以如果再对样本进行分类,结果是“错的少”。我们将任何≤0 的标签归类为‘0’(Iris-setosa),其他任何标签归类为‘1’(Iris-versicolor)。
现在,让我们画出每次迭代中错误分类样本的数量。我们可以看到,该算法在第 4 次迭代中收敛。即,所有样本在第四次通过数据时被正确分类。
感知器的一个属性是,如果数据集是线性可分的,那么算法保证收敛到某个点!
限制
- 单层感知器只有在数据集是线性可分的情况下才能工作。
- 该算法仅用于二元分类问题。然而,我们可以通过为每个类引入一个感知器来扩展该算法以解决多类分类问题。即,每个感知器产生 0 或 1,表示样本是否属于该类。
如果你想过来打个招呼,请在LinkedIn|Twitter|Github上与我联系。
1尤金·查尔尼亚克,《深度学习导论》(2018)。
[2] R.A. Fisher,分类问题中多重测量的使用 (1936)。
感知器:解释、实现和可视化示例
理解神经网络的构建模块
科林·伯伦斯在皮克斯贝拍摄的图片
感知器是人工神经网络的构建模块,它是我们大脑中生物神经元的简化模型。感知器是最简单的神经网络,仅由一个神经元组成。感知器算法是弗兰克·罗森布拉特在 1958 年发明的。
下面是一个生物神经元的图示:
图片由用户:Dhp1080 / CC BY-SA 在维基共享
神经元的大部分输入信号是通过树突接收的。其他神经元与这些树突之间大约有 1000 到 10000 个连接。来自连接的信号称为突触,通过树突传播到细胞体。细胞体中的电位增加,一旦达到阈值,神经元就会沿着轴突发送一个尖峰,通过轴突末端连接到大约 100 个其他神经元。
感知器是真实神经元的简化模型,试图通过以下过程来模拟它:它获取输入信号,让我们称它们为 x1,x2,…,xn,计算这些输入的加权和 z ,然后将其通过阈值函数ϕ并输出结果。
但是将 w0 作为阈值与将 w0 作为偏差加到总和上并且取而代之以阈值 0 是一样的。也就是说,我们考虑一个总是被设置为 1 的附加输入信号 x0。
这里代表一个感知器:
为了使用向量符号,我们可以将所有输入 x0,x1,…,xn 和所有权重 w0,w1,…,wn 放入向量 x 和 w ,当它们的点积为正时输出 1,否则输出-1。
这是一个仅使用两个输入 x1 和 x2 的几何表示,因此我们可以在二维空间中绘制它:
正如你在上面看到的,有两个输入的感知器的决策边界是一条线。如果有 3 个输入,决策边界将是一个 2D 平面。一般来说,如果我们有 n 个输入,则决策边界将是一个称为超平面的 n-1 维对象,该超平面将我们的 n 维特征空间分成两个部分:一个部分中的点被分类为正,一个部分中的点被分类为负(按照惯例,我们将把正好在决策边界上的点视为负)。因此,感知器是一个二元分类器,其权重是线性的。
在上图中,w’表示不含偏差项 w0 的权重向量。w’具有垂直于决策边界并指向正分类点的性质。该向量确定了判定边界的斜率,并且偏置项 w0 确定了判定边界沿着w’轴的偏移。
到目前为止,我们讨论了感知器如何根据输入信号及其权重做出决策。但是感知器实际上是如何学习的呢?如何找到正确的一组参数 w0,w1,…,wn 以便进行良好的分类?
感知器算法是一种基于以下简单更新规则的迭代算法:
其中 y 是我们当前数据点 x 的标签(-1 或+1 ),而 w 是权重向量。
我们的更新规则说了什么?点积 x⋅w 只是感知器基于当前权重的预测(其符号与预测标签的符号相同)。只有当真实标签 y 不同于预测标签ϕ(x⋅w).时,表达式 y(x⋅w 才能小于或等于 0 因此,如果真实标签和预测标签不匹配,那么我们更新我们的权重:w = w+yx;否则,我们就听其自然。
那么,为什么 w = w + yx 更新规则有效呢?它试图将 if 条件下的 y(x⋅w 的值推向 0 的正侧,从而正确地对 x 进行分类。如果数据集是线性可分的,通过对每个点进行一定次数的迭代来执行这种更新规则,权重将最终收敛到每个点都被正确分类的状态。让我们通过在更新后重新评估 if 条件来看看更新规则的效果:
也就是说,在特定数据点的权重更新之后,if 条件中的表达式应该更接近正的,从而被正确分类。
完整的感知器算法伪代码如下:
现在让我们用 Python 来实现它
现在,我们将使用 numpy 作为矩阵向量运算的外部库,用 python 从头开始实现感知器算法。我们将把它实现为一个类,它的接口类似于 Sci-kit Learn 等常见机器学习包中的其他分类器。我们将为这个类实现 3 个方法:.fit()
、.predict()
和.score()
。
.fit()
方法将用于训练感知器。它期望 2D numpy 数组 X 作为第一个参数。该数组的行是来自数据集的样本,列是特征。第二个参数, y ,应该是一个 1D numpy 数组,它包含了 X 中每一行数据的标签。第三个参数, n_iter ,是算法运行的迭代次数。
**def** fit(self, X, y, n_iter**=**100):
n_samples **=** X.shape[0]
n_features **=** X.shape[1]
*# Add 1 for the bias term*
self.weights **=** np.zeros((n_features**+**1,))
*# Add column of 1s*
X **=** np.concatenate([X, np.ones((n_samples, 1))], axis**=**1)
**for** i **in** range(n_iter):
**for** j **in** range(n_samples):
**if** y[j]*****np.dot(self.weights, X[j, :]) **<=** 0:
self.weights **+=** y[j]*****X[j, :]
.predict()
方法将用于预测新数据的标签。它首先检查权重对象属性是否存在,如果不存在,这意味着感知器还没有被训练,我们显示一个警告消息并返回。该方法需要一个参数, X ,其形状与.fit()
方法中的相同。然后我们在 X 和权重之间做一个矩阵乘法,把它们映射到-1 或者+1。我们使用np.vectorize()
将该映射应用于矩阵乘法的结果向量中的所有元素。
**def** predict(self, X):
**if** **not** hasattr(self, 'weights'):
print('The model is not trained yet!')
**return**
n_samples **=** X.shape[0]
*# Add column of 1s*
X **=** np.concatenate([X, np.ones((n_samples, 1))], axis**=**1)
y **=** np.matmul(X, self.weights)
y **=** np.vectorize(**lambda** val: 1 **if** val **>** 0 **else** **-**1)(y)
**return** y
.score()
方法计算并返回预测的准确性。它期望输入矩阵 X 和标签向量 y 作为参数。
**def** score(self, X, y):
pred_y **=** self.predict(X)
**return** np.mean(y **==** pred_y)
以下是完整代码:
几个例子
我现在想做的是展示几个决策边界如何收敛到一个解的可视化例子。
为此,我将使用 Sci-kit Learn 的datasets.make_classification()
和datasets.make_circles()
函数创建几个包含 200 个样本的双特征分类数据集。这是用于创建接下来的 2 个数据集的代码:
X, y **=** make_classification(
n_features**=**2,
n_classes**=**2,
n_samples**=**200,
n_redundant**=**0,
n_clusters_per_class**=**1
)
最后一个数据集:
X, y **=** make_circles(n_samples**=**200, noise**=**0.03, factor**=**0.7)
对于每个例子,我将把数据分成 150 个用于训练,50 个用于测试。左侧将显示训练集,右侧将显示测试集。当决策边界收敛到一个解时,它将显示在两侧。但是决策边界将仅基于左边的数据(训练集)来更新。
示例 1 —线性可分离
我将展示的第一个数据集是线性可分的数据集。下面是完整数据集的图像:
这是一个简单的数据集,我们的感知器算法将在通过训练集进行 2 次迭代后收敛到一个解决方案。因此,每个数据点的动画帧都会发生变化。绿点是算法中当前测试的点。
在这个数据集上,该算法正确地对训练和测试示例进行了分类。
示例 2 —噪声数据集
如果数据集不是线性可分的呢?如果正面和反面的例子像下图这样混淆了怎么办?
感知器算法不能正确地分类所有的例子,但是它会试图找到一条线来最好地区分它们。在这个例子中,我们的感知机得到了一个 88% 的测试准确率。下面的动画帧在所有训练示例的每次迭代后都会更新。
示例 3 —非线性数据集
下面的数据集呢?
它是可分的,但显然不是线性的。所以你可能认为感知器不适合这个任务。但是关于感知器的事情是,它的决策边界在权重方面是线性的,不一定在输入方面。我们可以增加我们的输入向量 x ,使它们包含原始输入的非线性函数。例如,除了原始输入 x1 和 x2 之外,我们还可以添加 x1 的平方、x1 乘以 x2 和 x2 的平方。
下面的polynomial_features(X, p)
函数能够将输入矩阵 X 转换成一个矩阵,该矩阵包含作为特征的次数为 p 的多项式的所有项。它利用了polynom()
函数,该函数计算代表要相乘以获得 p 阶项的列的索引列表。
**def** polynom(indices_list, indices, a, b, p):
indices **=** [*****indices]
**if** p **==** 0:
indices_list.append(indices)
**return**
**for** i **in** range(a, b):
indices.append(i)
polynom(indices_list, indices, i, b, p**-**1)
indices **=** indices[0:**-**1]
**def** polynomial_features(X, p):
n, d **=** X.shape
features **=** []
**for** i **in** range(1, p**+**1):
l **=** []
polynom(l, [], 0, d, i)
**for** indices **in** l:
x **=** np.ones((n,))
**for** idx **in** indices:
x **=** x ***** X[:, idx]
features.append(x)
**return** np.stack(features, axis**=**1)
对于我们的示例,我们将在 X 矩阵中添加 2 度项作为新特性。
X **=** polynomial_features(X, 2)
现在,让我们看看在使用转换后的数据集进行训练的过程中会发生什么:
注意,为了绘图,我们只使用原始输入,以保持 2D。在扩充的特征空间中,即现在的 5D 中,决策边界仍然是线性的。但是当我们绘制投影到原始特征空间上决策边界时,它具有非线性形状。
通过这种方法,我们的感知器算法能够正确地分类训练和测试示例,而无需对算法本身进行任何修改。我们只改变了数据集。
使用这种特征增强方法,我们能够通过使用原本只是线性的算法来模拟数据中非常复杂的模式。
但是,这种方法不是很有效。想象一下,如果我们有 1000 个输入要素,并且我们想用最多 10 次多项式项来扩充它,会发生什么情况。幸运的是,使用一种叫做内核的东西可以避免这个问题。但这是另一篇文章的主题,我不想让这篇文章太长。
我希望这些信息对你有用,感谢你的阅读!
这篇文章也贴在我自己的网站这里。随便看看吧!
完全保护隐私的人工智能
数据隐私
它是什么,我们如何实现它?
数据隐私被称为“未来十年最重要的问题”,并由于像欧盟一般数据保护条例(GDPR)和加州消费者隐私法(CCPA)这样的立法而占据了中心位置。公司、开发者和研究人员正在争先恐后地跟上需求。特别是,“设计隐私”是 GDPR 不可或缺的一部分,在这十年里可能只会越来越受欢迎。当使用隐私保护技术时,立法突然变得不那么令人生畏了,因为确保数据安全是维护用户信任的核心。
数据隐私是训练和测试人工智能模型的核心问题,特别是那些根据敏感数据进行训练和推断的模型。然而,据我们所知,还没有关于拥有完全保护隐私的人工智能意味着什么的指南出版。我们介绍了实现完全保护隐私的人工智能所需的四个支柱,并讨论了可以帮助解决每个支柱的各种技术。我们用快速增长的隐私保护机器学习子领域中相对新的研究来支持我们的说法。
完美隐私保护人工智能的四大支柱
完美隐私保护人工智能的四大支柱
在我们的研究中,我们确定了保护隐私的机器学习的四个支柱。这些是:
- 训练数据隐私:保证恶意参与者无法对训练数据进行逆向工程。
- 输入隐私:保证用户的输入数据不会被其他方观察到,包括模型创建者。
- 输出隐私:保证一个模型的输出除了被推断数据的用户之外,任何人都不可见。
- 模型隐私:保证模型不被恶意方窃取。
1–3 处理保护数据创建者,而 4 意味着保护模型创建者。
培训数据隐私
虽然从明文(未加密的技术术语)输入和输出数据中收集有关训练数据和模型权重的信息可能会稍微困难一些,但最近的研究表明,重建训练数据和逆向工程模型并不像人们希望的那样是一个巨大的挑战。
证据
在1中,Carlini 和 Wagner 计算了生成序列模型(例如,字符语言模型)可以多快地记住训练集中的稀有信息。Carlini 和 Wagner 在 Penn Treebank 上训练了一个字符语言模型,其中插入了一次“秘密”:“随机数是 oooooooooooo ”,其中 oooooooo 是一个(假的)社会安全号码。他们展示了隐藏在他们的宾夕法尼亚树库数据集(PTD)中的秘密是如何暴露的。他们在 5%的 PTD 上训练一个字符语言模型,并计算网络的记忆量。当测试集损失最低时,记忆达到峰值。这与秘密暴露的高峰期相吻合。
度量
那么,我们如何量化一个秘密被模型输出逆向工程的可能性呢?1开发了一个称为暴露的指标:
给定一只金丝雀 s[r],一个带参数θ的模型,随机空间 R,曝光度 s[r]为
等级是给定模型对输入的困惑时,真实秘密(或金丝雀)在所有可能秘密中的索引。索引越小,序列出现在训练数据中的可能性越大,因此目标是最小化秘密的暴露,这是 Carlini 和 Wagner 所做的事情。通过使用差分私有梯度下降来实现(参见下面的 解决方案 )。
在[2]中给出了另一个暴露度量,其中作者计算了通过不安全通道发送的私有数据的潜在表示会泄露多少信息。虽然本文更多地属于输入数据隐私分析的范畴,但仍然值得查看提议的指标,以将其与1中提出的指标进行比较。事实上,他们提出了两个隐私度量标准。一个用于人口统计变量,如情感分析和博客文章主题分类,一个用于命名实体,如新闻主题分类。他们的隐私指标是:
- 人口统计变量:“1X,其中 X 是攻击者对性别和年龄预测的平均准确度,”
- 命名实体:" 1F,其中 F 是在 z 中的一组二进制变量上计算的 F 分数,其指示输入示例中命名实体的存在,"其中" z 是包含在[自然语言文本]中的私有信息的向量。"
在查看证据时,重要的是要记住,人工智能(保护隐私的人工智能)的这个子领域是全新的,因此可能有很多潜在的利用尚未分析,甚至尚未想到。
解决方案
对于训练数据记忆的问题,提出了两种主要的解决方案,它们不仅保证了隐私,还提高了机器学习模型的可推广性:
- 差分隐私随机梯度下降(DPSGD) [3,4]:虽然差分隐私最初是为了允许人们在不透露数据集中任何个人的任何个人信息的情况下对数据集进行归纳,但该理论已被用于在深度学习系统中保护训练数据隐私。
关于在机器学习中使用差分隐私的详细讨论,请阅读这篇采访Parinaz sob Hani 博士,他是加拿大领先的风险投资公司之一 Georgian Partners 的机器学习主管。 - Papernot 的 PATE[5]:paper not 教授创建了 PATE,作为 DPSGD 的更直观的替代方案。PATE 可以被认为是一种集成方法&通过在数据集的 iid 子集上训练多个模型来工作。根据推断,如果大多数模型同意输出,那么输出不会揭示关于训练数据的任何私人信息,因此可以共享。
输入和输出隐私
输入用户数据和从该数据推断出的结果模型输出不应该对除用户之外的任何方可见,以符合完全保护隐私的 AI 的四个支柱。保护用户数据隐私不仅有利于用户本身,也有利于处理潜在敏感信息的公司。隐私与安全密不可分。拥有适当的安全性意味着数据泄露的可能性大大降低,从而实现理想的场景:不会失去用户的信任,也不会因为数据管理不当而被罚款。
证据
这对于确保私人数据不会:
- 被误用(例如,NYT 中报告的位置跟踪)
- 由于黑客攻击或其他原因落入他人之手
- 习惯于用户没有预料到或明确同意的任务(例如,亚马逊承认员工监听 Alexa 对话)。
虽然标准做法是在传输过程中对数据进行加密,并且(如果公司有责任的话)静态数据也要加密,但是当数据被解密以进行处理时,就很容易受到攻击。
方案
- 同态加密:同态加密允许对加密数据进行非多项式运算。对于机器学习,这意味着训练和推理可以直接在加密数据上进行。同态加密已经成功地应用于随机森林、朴素贝叶斯和逻辑回归[6]。[7]设计了对加密数据进行分类的低次多项式算法。最近,深度学习模型已经适应了加密领域[8,9,10]。
参见这篇文章了解同态加密的介绍。 - 安全多方计算(MPC):MPC 背后的想法是,不信任对方的两方或多方可以将他们的输入转换为“无意义的”,这些输入被发送到一个函数中,该函数的输出只有在使用正确数量的输入时才有意义。在其他应用中,MPC 已用于使用不同医院拥有的基因组数据进行基因组诊断[11],以及用于分类 MNIST 图像的线性回归、逻辑回归和神经网络[12]。[11]是一个很好的例子,说明了在隐私得到保证的情况下,通过访问敏感数据可以取得什么样的进步。由于缺乏训练分类和生成模型所需的数据,有许多任务无法用机器学习来完成。不是因为数据不在那里,而是因为信息的敏感性意味着它不能被共享,有时甚至不能被收集,包括医疗数据或甚至特定于说话人的元数据,这可能有助于改进自动语音识别系统(例如,年龄组、位置、第一语言)。
这里有一篇关于 MPC 的精彩介绍。你可以在这里找到 Morten Dahl 博士的私人深度学习用 MPC 教程。 - 联合学习:联合学习基本上是设备上的机器学习。只有在与差分专用训练(参见上一节中的 DPSGD)和用于安全模型聚合的 MPC[13]相结合时,它才真正成为专用的,因此用于训练模型的数据不能从单个电话输出的权重更新进行反向工程。在实践中,谷歌已经在 Gboard 上部署了联合学习(见他们的博客文章),苹果在 CoreML3 中引入了联合学习支持。
模型隐私
人工智能模型可能是公司的面包和黄油,其中许多通过 API 或最近通过可下载软件向开发人员提供预测能力。模型隐私是必须考虑的四个支柱中的最后一个,也是用户和公司利益的核心。如果竞争对手可以轻松复制他们的模型(这种行为不容易调查),公司就没有动力提供有趣的产品,也没有动力花钱改善人工智能能力。
证据
机器学习模型构成了许多公司的核心产品&知识产权,因此模型被盗是一种严重的威胁,可能会产生重大的负面商业影响。一个模型可以被直接窃取,也可以根据它的输出进行逆向工程[14]。
解决方案
- 在将差分隐私应用于模型输出以防止模型反转攻击方面已经做了一些工作。差分隐私通常意味着损害模型准确性;然而,[15]提出了一种不牺牲准确性来换取隐私的方法。
- 同态加密不仅可以用于保护输入和输出隐私,如果选择在云中加密模型,还可以保护模型隐私。然而,这带来了巨大的计算成本,并且不能防止模型反演攻击。
满足所有四大支柱
从前面几节可以看出,没有一种一揽子技术可以解决所有隐私问题。相反,为了拥有完全保护隐私的人工智能(这是研究界和工业界尚未实现的),人们必须结合各种技术:
- 同态加密+差分隐私
- 安全多方计算+差分隐私
- 联邦学习+差分隐私+安全多方计算
- 同态加密+补丁
- 安全多方计算+ PATE
- 联邦学习+ PATE +同态加密
其他组合也存在,包括一些替代技术,还没有强大的数学保证;即,(1)安全区域(例如,英特尔 SGX ),允许在甚至系统内核都无法访问的情况下执行计算,(2)数据去识别,以及(3)数据合成。
目前,完美保护隐私的人工智能仍然是一个研究问题,但有一些工具可以解决一些最紧迫的隐私需求。
保护隐私的机器学习工具
感谢
非常感谢 Pieter Luitjens 和 Siavash Kazemian 博士对本文早期草稿的反馈,以及 Gerald Penn 教授对本文的贡献。
参考
1 Carlini,Nicholas,等,秘密分享者:评估和测试神经网络中的非故意记忆(2019) ,第 28 届 USENIX 安全研讨会(USENIX 安全 19)。
[2]科沃克斯、马希民、沙希·纳拉扬和谢伊·b·科恩,文本的隐私保护神经表征 (2018),arXiv 预印本 arXiv:1808.09408。
3 Song,Shuang,Kamalika Chaudhuri,Anand D. Sarwate,带差分私有更新的随机梯度下降 (2013) , IEEE 全球信号与信息处理会议。
[4] Wu,x .、Li,f .、Kumar,a .、Chaudhuri,k .、Jha,s .和 Naughton,j ., Bolt-on 差分隐私用于可扩展的基于随机梯度下降的分析 (2017),载于 2017 年 ACM 数据管理国际会议论文集,1307–1322。ACM。
[5] Papernot,Nicolas,等著,带 PATE 的可扩展私人学习 (2018),arXiv 预印本 arXiv:1802.08908 (2018)。
[6]阿斯莱特,路易·JM,佩德罗·m·埃斯佩兰萨,克里斯·c·霍尔姆斯,加密统计机器学习:新的隐私保护方法 (2015),arXiv 预印本 arXiv:1508.06845。
[7]格雷佩尔,托雷等,加密数据上的机器学习 (2012),ICISC 2012,LNCS 7839。
[8]赫萨米法尔德、埃桑、哈桑·塔卡比和迈赫迪·哈塞米, CryptoDL:加密数据上的深度神经网络 (2017),arXiv 预印本 arXiv:1711.05189。
[9] Hesamifard,Ehsan 等人,隐私保护机器学习即服务 (2018),《隐私增强技术论文集》。
[10] Gilad-Bachrach,Ran 等, CryptoNets:将神经网络应用于高吞吐量和高精度的加密数据 (2016),机器学习国际会议。
[11] Jagadeesh,Karthik A .等人,在不暴露患者基因组的情况下得出基因组诊断 (2017),科学 357.6352。
[12]莫哈塞尔,佩曼,张予鹏, SecureML:一个可扩展的隐私保护机器学习的系统 (2017),2017 IEEE 安全与隐私研讨会(SP)。
[13] Bonawitz,Keith,等人,保护隐私的机器学习的实用安全聚合 (2017),2017 年 ACM SIGSAC 计算机与通信安全会议论文集。
[14] Tramèr,Florian 等人,通过预测 API 窃取机器学习模型 (2016),第 25 届 USENIX 安全研讨会(USENIX 安全 16)。
[15]王,岳,,,吴,差分隐私和模型反演攻击下的回归模型拟合 (2015),第 24 届国际人工智能联合大会。
完美的 Pythonic Python 你绝对应该知道的东西
像 Python 禅师一样编写简单而优雅的代码
图片来自壁纸 flare
M 我们大多数人在编码时都有一些定位功能。一旦我们习惯了以某种方式编码,我们倾向于一遍又一遍地使用相同的函数,即使可能有更好的方式。而且,按照 Python 的禅理,应该只有一种——最好只有一种——显而易见的方法来做这件事!
比我愿意承认的更多的时候,我也掉进了这个陷阱。例如,太习惯于循环,太频繁地使用打印而不是日志,等等。这绝对不是蟒蛇的方式。您可以随时更新关于 Pythonic 的知识,即编写清晰、简洁且易于维护的代码,更多内容请点击此处:
如果你问 Python 程序员他们最喜欢 Python 的什么,他们往往会引用它的高可读性。的确,一个…
docs.python-guide.org](https://docs.python-guide.org/writing/style/)
经过一段时间,以下 7 个功能成为我最喜欢使用的功能,因为它们的简单和内在的优雅。如果你到现在还没有,你应该尽可能地使用它们。
1。f 弦
Python 3.6 提出了一种格式化字符串的绝妙方法,称为 string(格式化字符串文字)。在 Python 3 中的 f-strings 之前,我们可以通过以下两种方式中的任何一种来实现:
# Method_1 Using %s>> name = "Leonardo"
>> surname = "Da Vinci"
>> Occupation_1 = "Artist"
>> Occupation_2 = "Inventor"
>> Occupation_3 = "Scientist"
>> Occupation_4 = "Mathematician"
>> Occupation_5 = "Philosopher">> print ("%s %s was an %s, %s, %s, %s and %s." % (name, surname, Occupation_1, Occupation_2, Occupation_3, Occupation_4, Occupation_5))'Leonardo Da Vinci was an Artist, Inventor, Scientist, Mathematician and Philosopher.'
正如我们所看到的,代码的可读性完全被破坏了。下面的 str.format()就是这种情况。
# Method_2 Using str.format()>> print (("{name} {surname} was an {Occupation_1}, {Occupation_1}, {Occupation_1}, {Occupation_1} and {Occupation_1}.").format(name = name, surname = surname, Occupation_1 = Occupation_1, Occupation_2 = Occupation_2, Occupation_3 = Occupation_3, Occupation_4 = Occupation_4, Occupation_5 = Occupation_5))'Leonardo Da Vinci was an Artist, Inventor, Scientist, Mathematician and Philosopher.'
这一点也不比第一个好。幸运的是,我们有 Python 3 f-strings 来拯救我们。他们是这样工作的:
>> f'{name} {surname} was an {Occupation_1}, {Occupation_2}, {Occupation_3}, {Occupation_4} and {Occupation_5}.''Leonardo Da Vinci was an Artist, Inventor, Scientist, Mathematician and Philosopher.'
就这么简单!好家伙,我们有可读性。最重要的是,因为 f 字符串是在运行时计算的,所以你可以做很多事情,包括其他函数和方法。请看这里:
>> f'{2*3}'
>> f'{name.lower()}''6'
'leonardo'
我知道有些保守的 python 专家会引用 Python 的禅,说 f 字符串是不符合 Python 的,因为应该只有一种显而易见的方法来做事。在这种情况下,有两个。但是实用性胜过纯粹性和可读性。我现在发现了一个显而易见的方法,那就是 f 弦!
2。清单和字典(打包、拆包)
列表和字典是 python 中最常用的两个对象,尤其是当您对数据争论和分析感兴趣的时候。它们也很难使用。以精益的方式使用它们是极其重要的。在这种情况下,我们释放*和**的力量,作为与创建函数的列表和字典相关的重要参数。
>> def some_function (a,b,c,d):
print (a,b,c,d)>> some_list = [1,2,3,4]
>> some_dictionary = {'a':1, 'b':2, 'c':13, 'd':14}
现在,我们来看看上面的解包是如何工作的:
# Unpacking list>> some_function(some_list)TypeError: some_function() missing 3 required positional arguments: 'b', 'c', and 'd'
如您所见,它自然地将列表视为一个位置参数。为了正确地做到这一点,您可以在列表前使用*号,瞧:
# Unpacking list>> some_function(*some_list)1 2 3 4
同样,对于字典来说,是**。
# Unpacking dictionary>> some_function(**some_dictionary)1 2 13 14
必须注意的是,字典的键是与函数相匹配的,如果键不同,你会得到一个错误。
# Unpacking dictionary>> some_dictionary_2 = {'x':1, 'y':2, 'z':13, 'w':14}
>> some_function(**some_dictionary_2)TypeError: some_function() got an unexpected keyword argument 'x'
让我们看看包装是如何工作的。对于列表,我们可以使用带*的包参数。同样,对于字典,我们使用**来打包关键字参数(kwargs)。
# List Packing>> def some_list_packing(*****args):
args **=** list(args)
args[0] **=** 'I am about to'
args[1] **=** 'pack lists' some_function(*args) # use the previous function unpacking>> some_list_packing('I am packing','','','')I am about to pack lists# Dictionary Packing>>> def some_dictionary_packing(**kwargs):>>> for key in kwargs: print(f'{key} = {kwargs[key]}')
# See how I used the f-string there?>>> some_dictionary_packing(a= "I", b= "am", c= "about", d= "to write..")
a = I
b = am
c = about
d = to write..
正如你所注意到的,列表和字典的打包和解包非常容易,可以大大简化你的代码!
3。动态新型
我知道这听起来像是最新的流行时尚,但在 Python 中它更酷。您知道 Python 允许您随时使用“类型”创建类吗?这是如何做到的,它比你想象的更棒:
>>> NewClass = type("NewClass", (object,), {"intro": "This is an awesome new class"})>>> n = NewClass()
>>> n.intro"This is an awesome new class"
这与以下内容完全相同:
>>> class NewClass(object):
intro = "This is an awesome new class"
>>> n = NewClass()
>>> n.intro"This is an awesome new class"
当然,为了让您更容易理解动态类的强大功能,我简化了这里的示例。使用这个功能,您可以轻松地创建高级类。
4。试试除了
我无法数清有多少次感谢 python 之神创造了这一切。它测试一个代码块,并允许我们以简单有效的方式处理错误!
# use the unpacking lists function again for the example>> def some_function (a,b,c,d):
print (a,b,c,d)a_list = [1,2,3,4]>> try:
some_function (a_list)
except:
print(“Put * before the input list in the function”) Put * before the input list in the function
当您的代码变得更大时,您应该更频繁地使用它和日志功能。在任何情况下,切勿执行以下操作:
>> try:
some_function (a_list)
except:
pass
蟒蛇神永远不会原谅你的!
5。可变默认值
这是一个漂亮的小技巧,它的好处比看起来要多,如果没有正确遵循,出错的可能性也比你最初想象的要大。下面是一个代码示例,可以让您明白我的意思:
>>> def something(x=[]):
x.append(1)
print (x)
>>> something()
[1]>>> something()
[1, 1]>>> something()
[1, 1, 1]
你看,每次使用这个函数时,列表都会被追加。相反,你应该使用一个缺省值来表示“未指明”,并替换为你希望作为缺省值的可变变量:
>>> def something(x= None):
if x is None:
x= []
x.append(1)
print (x)>>> something()
[1]
>>> something()
[1]
相信我,事先知道如何使用可变的默认参数将为您节省大量调试时间!
6。价值交换
我依稀记得当我学习 Java 时,我们被介绍交换值的基础是创建第三个临时变量。
a = 1,b =2。想交换价值观吗?创建一个临时变量 c .然后 c = a,a = b,b = c .多烦啊!下面是它在 Java 中的样子:
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
// a and b are copies of the original values.
}
好吧,我们有 python 元组来拯救。当我们可以将它们互换位置时,为什么要创建第三个变量呢!
>> a = 10
>> b = 5
>> a, b = b, a >> print(a)
5>> print(b)
10
这没什么大不了的,但有时,从长远来看,了解简单的基础知识会对我们有很大帮助。
7。用大括号替换空格?
是的,伙计们,你们没听错!我们都属于 Java 阵营,我们知道你讨厌空格分隔块,喜欢那些花括号。最后,还有一个办法:
from __future__ import braces
一旦运行了上面的代码,您会得到
SyntaxError: not a chance
逮到你了!那永远不会发生。这么长的吸盘!
图片来自 Pixabay
请原谅我用复活节彩蛋来描绘和开始这一小节,我控制不住自己。
但是 __ 未来 _ _ 模块比你想象的更有用。这次是真的!它有助于兼容最新版本的 python。以此为例:
如果您使用的是 Python 2.7
a = 1000print a>> 1000
现在,您可以从最新的 Python 版本中导入打印功能。
>> from __future__ import print_function>> print aSyntaxError: Missing parentheses in call to ‘print’. Did you mean print(a)?>> print (a)1000
未来模块包括一系列功能。你可以在这里查看:
[## 未来- pysheeet
未来语句告诉解释器编译一些语义作为将来可用的语义…
www.pythonsheets.com](https://www.pythonsheets.com/notes/python-future.html#:~:text=future%20is%20a,import%20to%20current%20Python%20interpreter.&text=Future%20statements%20not%20only%20change,_Feature%20into%20the%20current%20program.)
就是这样,伙计们!上面的清单只包含了 Python 中许多优秀功能中的一小部分。我希望你发现它们是有用的,如果你应用了它们,你的编码同行一定会发现你的代码更容易审查。
编码快乐!
如何检查支持线性回归模型的主要假设?
数据科学
大多数认为他们需要高级人工智能/人工智能的公司实际上只需要对清理后的数据进行线性回归
除了这句话的讽刺之外,还有一个事实:在所有的统计技术中,回归分析通常被认为是商业分析中最重要的技术之一。大多数公司使用回归分析来解释特定的现象,建立预测或做出预测。这些新的见解对于理解什么可以改变业务非常有价值。
当你作为一名数据科学家工作时,建立一个线性回归模型可能听起来相当枯燥,尤其是当它与你周围的人工智能有关时。但是,我想强调的是,掌握线性回归模型的主要假设比看起来要复杂得多,需要一些扎实的统计学基础。另一件事是,线性回归模型是同一算法家族的一部分,又名广义线性模型(GLM)。 GLM 是数据科学家的一个重要话题,因为它们符合各种各样的现实世界现象。
也许这就是为什么在 2017 年 11 月, Jame Le 将线性回归模型排在科学家需要掌握的统计技术数据的前 10 位。
既然我们已经清楚为什么要关注线性回归模型,那就让我们开始着手吧。
您运行了线性回归分析,结果是显著的(或不显著的)。你可能认为你已经完成了。不,还不是我的朋友。
运行回归分析后,您应该检查模型对数据的表现是否良好。也许你从回归结果开始,比如斜率、p 值或 R 平方。这些参数可以让您初步了解模型对给定数据的表现。但这并不是全部。
在这篇文章中,我将带你了解如何检查支持线性回归模型的主要假设。
但是等等!首先要做的是…
四名维和人员
有四个主要假设支持使用线性回归模型进行推断或预测:
第四批维和人员(图片由 Unsplash 上的 freestocks 拍摄)
- 线性:听起来很明显!我们的特征和反应之间必须有线性关系。这是我们的估计和预测无偏所必需的。
接下来是关于残差的:
2。正态性: 残差必须是正态分布(即方差趋于 1,均值趋于零)。这对于一系列的统计检验是必要的,例如 t 检验。由于中心极限定理,我们可以在大样本中放宽这个假设。
3。同方差性: 是指无论因变量的水平如何,残差都具有恒定的方差。
4。独立性:
残差必须完全没有自相关。
第五个麻烦制造者
第五个麻烦制造者(照片由 val Ccrn 在 Unsplash 上拍摄)
对于多元线性回归,您还需要检查:
5。多重共线性缺失: 多重共线性是指当两个(或更多)预测因子提供关于响应变量的相同信息时。这可能是模型的一个问题,因为它会生成:
- 冗余→导致预测系数不可靠(尤其是线性模型)
- 估计器的高方差→倾向于过度拟合,这意味着算法倾向于对训练数据中的随机噪声建模,而不是对预期输出建模。
- 一个重要的预测因素可能变得不重要。
研究案例:广告预算和销售预测
为了说明这篇文章,我们将尝试使用三个预测因素来预测一家公司的销售额:Youtube 广告的预算投入、脸书广告的预算投入和报纸广告的预算投入。所有变量均以千美元表示。使用的数据集可以在 Kaggle 中找到。
资料组
df.head(7)
数据集一瞥(来源:作者)
Python 库:Statsmodel
对于本文,我选择使用 StatsModel 而不是 Scikit-learn 。
尽管 StatsModel 没有 Scikit-learn 提供的各种选项,但它提供了顶级的统计和计量经济学工具,并通过了 Stata 和 r 等其他统计软件的验证。
🚩 在这里你可以找到一篇关于这两个库的有趣文章:
[## Scikit-learn vs. StatsModels:哪个,为什么,如何?
在数据孵化器,我们为拥有最新的数据科学课程而自豪。我们的大部分…
blog.thedataincubator.com](https://blog.thedataincubator.com/2017/11/scikit-learn-vs-statsmodels/)
输出
我们开始吧!
**import statsmodels.formula.api as smf**
reg = smf.ols(‘sales~youtube+facebook+newspaper’,data=df).fit()
print(reg.summary())
OLS 摘要(来源:作者)
✅如果我们检查“基础”参数,这里是我们可以看到的:
- R 平方相当高
- Prob (F 统计)非常低
- p 值<阿尔法风险(5%)除了预测因子报
R 平方:如果你忘了或者不知道,R 平方和调整的 R 平方是经常伴随回归输出的统计数据。他们都应该用从 0 到 1 范围内的值来评估模型性能。
通常,您会选择具有较高校正和预测 R 平方值的模型。
现在,我有这个问题要问你:谁在用 R 平方检验模型的拟合优度?
我愿意。
或者至少,我做到了。
直到我看了几篇题目的文章,显示 R 平方确实而不是度量拟合优度因为它可以:
-即使模型正确也任意低,
-即使模型错误也危险地接近 1。
我不打算在这篇文章中进一步讨论这怎么可能,但你可以在这里找到一些非常好的信息:
当你的回归模型有一个高的 R 平方,你认为这是一件好事。你想要一个高 R 平方,对吧…
statisticsbyjim.com](https://statisticsbyjim.com/regression/r-squared-too-high/) [## 弗吉尼亚大学图书馆研究数据服务+科学
2015 年 10 月 16 日,星期四,一个不相信的学生在 Reddit 上发帖:“我的统计学教授”开始咆哮……
data.library.virginia.edu](https://data.library.virginia.edu/is-r-squared-useless/#:~:text=1.,is correct in every particular.)
因此,根据上面的 3 个参数,你可以认为你的回归模型可以很好地预测基于广告预算的销售额。然后,您可能希望删除报纸预测值,因为它对模型似乎不重要。
当然,在这里停止我们的分析是非常错误的。
首先也是最重要的,你不应该盲目地跟随 R 平方所说的。然后,因为我们不知道这 5 个关键假设是否得到验证。所以我们基本上不知道我们是否在这个模型表现良好的范围内。
是时候让我向你们介绍每一个假设以及如何验证它们了。
1.线性
如果您试图用线性模型来拟合非线性或非可加性的数据,您的预测很可能会出现严重错误,尤其是当您的推断超出了样本数据的范围时。例如,为了确认线性,我们可以:
- 应用哈维-科利尔乘数检验。
**import statsmodels.stats.api as sms**sms.linear_harvey_collier(reg_multi)>> Ttest_1sampResult(statistic=-1.565945529686271, pvalue=0.1192542929871369)
✅小 p 值表明 存在违反线性度的 。
此处 p 值高于α风险(5%),意味着线性条件得到验证。
- 观察值与预测值的对比
**# Plot Predicted values VS Real values**
df['Predicted_sales'] = reg_multi.predict()
X_plot = [df['sales'].min(), df['sales'].max()]ax = sns.scatterplot(x="sales", y="Predicted_sales", data=df)
ax.set(xlabel='sales', ylabel='Predicted_sales')
plt.plot(X_plot, X_plot, color='r')
plt.show()
看起来数据点沿着对角线分布。意味着线性条件可能被验证。尽管如此,我们可以看到在我们的数据集中显示潜在异常值的尾部(来源:作者)
✅如果线性条件得到验证,则点应围绕对角线对称分布,方差大致恒定。
- 学生化残差与拟合值
学生化残差在检测异常值、检查线性度和评估等方差假设方面比标准化残差更加有效。
**# Get the Studentized Residual**
student_residuals = pd.Series(np.abs(reg.get_influence().resid_studentized_internal))**# Plot the Studentized Residual**
fig, ax = plt.subplots()
ax.scatter(fitted, student_residuals, edgecolors = ‘k’)
ax.set_ylabel(‘Studentized Residuals’)
ax.set_xlabel(‘Fitted Values’)
ax.set_title(‘Scale-Location’)plt.show()
看起来残差很小而且没有结构,但是我们可以看到至少一个异常值(来源:作者)。
✅理想情况下,所有残差都应该很小且无结构(即不形成任何聚类)。这意味着回归分析已经成功地解释了因变量变化的主要部分。然而,如果残差显示出一种结构或者呈现出任何看起来不是随机的特殊方面,那么它就给回归带来了“不好的影响”。
2。常态
您可以通过验证残差是否正态分布来开始分析残差。严格地说,残差的非正态性表明模型不合适。这意味着模型产生的误差在变量和观测值之间是不一致的(即误差不是随机的)。
有许多方法来执行这一步,我个人是数据可视化的忠实粉丝,所以我总是从这两个图开始来检查正态性假设:
**# Plot residual Q-Q plot****import scipy as sp**
fig, ax = plt.subplots(figsize=(6,2.5))
_, (__, ___, r) = sp.stats.probplot(residuals, plot=ax, fit=True)
良好的拟合表明正态性是一个合理的近似值,即使我们看到了轻微的尾部。由于异常值,数据尾部可能不是高斯分布的(来源:作者)。
- 残差分布直方图
**# Get Residuals**
residuals = reg_multi.resid**# Plot Histogram of the residuals** plt.hist(residuals, density=True)
plt.xlabel('Residuals')
plt.title('Residuals Histogram')
plt.show()
在这里我们可以看到,尽管略有偏斜,但并没有严重偏离正态分布。我们可以说这个分布满足正态假设。有时,由于一些大的异常值的存在,误差分布是“扭曲的”(来源:作者)。
✅如果残差呈正态分布,我们应该会看到一个以 0 为中心、方差为 1 的钟形直方图。
其他参数可用于加深理解:
- 综合 是对残差的偏度和峰度的检验。高值表示偏态分布,而低值(接近零)表示正态分布
- Prob(Omnibus)执行统计测试,表明残差呈正态分布的概率。我们希望看到接近 1 的东西。
Omnibus 59
Prob(Omnibus): 0.000
🚩这里,Prob(Omnibus)值= 0,指示残差不是正态分布,并且 Omnibus 是高的,指示已经在直方图上视觉检测到的偏斜度。这些观察结果可能是由于异常值的影响。
- Jarque-Bera 统计量表示残差是否正态分布。这个检验的零假设是残差是正态分布的。当该测试的 p 值(概率)较低(< 5%)时,残差不是正态分布,表明潜在的模型设定错误(即模型中缺少一个关键变量)。
Prob(JB): 1.47e-38
🚩JB 统计表明残差不是正态分布的。同样,这可能是由于离群值的影响。
- 偏斜系数反映了数据的对称性,可以表示正态性。我们希望看到接近于零的东西,说明残差分布是正态的。负偏斜是指分布左侧的尾巴更长或更胖,而正偏斜是指右侧的尾巴更长或更胖。
Skew: -1.411
🚩我们可以清楚地看到左侧直方图中已经检测到的偏斜度。非常偏斜的分布很可能在偏斜方向上有异常值。
关于正态性还有各种各样的统计测试,包括科尔莫戈罗夫-斯米尔诺夫测试、夏皮罗-维尔克测试和安德森-达林测试。这些测试都是比较“挑剔”的。真实数据很少有完全正态分布的误差,并且可能无法用误差在 5%显著性水平下不违反正态假设的模型来拟合您的数据。
通常更好的做法是更多地关注对其他假设的违反和/或离群值的影响,它们可能是违反正态性的主要原因。
3.同方差性
同方差假设是所有预测的因变量得分的残差相等。换句话说,这意味着回归线周围的方差对于预测变量(X)的所有值都是相同的。
当因变量的值似乎作为自变量的函数增加或减少时,违反同异方差假设会导致异方差。
通常,当一个或多个被调查的变量不是正态分布时,会发生同方差违规。要检查同质性,您可以从以下内容开始:
- 学生化残差与拟合值
我们以前已经使用过这个图,在这种情况下,我们希望看到残差对称地分布在一条水平线上。
在我们的示例中,相应的残差图似乎合理地分布在水平带上。但我们至少可以看到一个离群值(来源:作者)。
- 布鲁什-帕甘试验
该测试测量误差如何在解释变量中增加。
**import statsmodels.stats.api as sms
from statsmodels.compat import lzip**name = ['Lagrange multiplier statistic', 'p-value',
'f-value', 'f p-value']test = sms.het_breuschpagan(reg_multi.resid, reg_multi.model.exog)
lzip(name, test)>> [('Lagrange multiplier statistic', 4.231064027368323),
('p-value', 0.12056912806125976),
('f-value', 2.131148563286781),
('f p-value', 0.12189895632865029)]
✅如果检验统计量的 p 值低于α风险(例如 0.05),则拒绝同向异方差的零假设,并假设异方差。在我们的例子中,我们验证了同方差假设。**
还有许多其他方法来测试异方差的条件,在这个长长的列表中,我想提一下白色测试,它特别适用于大型数据集。
4.独立性ˌ自立性
残差的独立性通常被称为完全没有自相关。即使不相关的数据不一定意味着独立,如果随机变量的互信息趋向于 0,人们也可以检查随机变量是否独立。
- 这种假设在时间序列模型或纵向数据集中尤其危险,因为残差中的序列相关性意味着模型有改进的空间。
- 这个假设在非时间序列模型或横截面数据集的情况下也很重要。如果残差在特定条件下总是具有相同的符号,这意味着模型系统地低估/高估了发生的事情。
为了验证该条件,我们可以使用 ACF(自相关函数)图和 Durbin-Watson 测试。
- ACF 图
我们想看看 ACF 的值对任何滞后是否有意义。在我们的例子中,我们使用非时间序列数据,所以我们可以使用行号来代替。在这种情况下,应以(仅)取决于要素值的方式对行进行排序。
**import statsmodels.tsa.api as smt**
acf = smt.graphics.plot_acf(reg_multi.resid, alpha=0.05)
除了第一行,所有值都在置信区间内。我们没有统计意义上的偏自相关(来源:作者)。
- 德宾-沃森
测试将输出 0 到 4 之间的值。下面是如何解读测试结果:
—值= 2 表示样本中不存在自相关,
—值< 2 表示正自相关,
—值> 2 表示负自相关。
**import statsmodels.stats.stattools as st**
st.durbin_watson(residuals, axis=0)>> 2.0772952352565546
✅ 我们可以合理地考虑残差的独立性。
5.多重共线性
最后但同样重要的是,对于多元线性回归,检查多重共线性是一个好主意。当您的模型包含多个不仅与响应变量相关,而且彼此相关的因素时,就会出现多重共线性。换句话说,当你有一些多余的因素时,它就会产生。
你要记住,好的模型是简单的模型。
你可以想到足球比赛中的多重共线性:
如果一名球员铲球了对方的四分卫,很容易把功劳归于该得到荣誉的那一袋。但是,如果三名球员同时处理四分卫,就很难确定三人中谁对麻袋做出的贡献最大。
多重共线性使得一些本应显著的变量在统计上变得不显著。
您可以使用方差膨胀因子(VIF)检查两个或多个变量之间是否存在共线性。它是多元回归中预测变量之间共线性的度量。
我们通常认为 VIF 为 5 或 10 及以上(取决于业务问题)表示多重共线性问题。
**from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.tools.tools import add_constant**# For each X, calculate VIF and save in dataframe
df.drop(['sales'],axis=1,inplace=True)
X = add_constant(df)vif = pd.DataFrame()
vif["VIF Factor"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
vif["features"] = X.columnsvif.round(1) #inspect results
每个变量的方差膨胀因子(来源:作者)。
✅ 在这里,我们可以看到我们没有多重共线性问题。
除此之外,预测值的相关矩阵可能表明多重共线性的存在。如果不总是如此,相关矩阵可以很好地指示多重共线性,并突出进一步调查的需要。
plt.subplots(figsize=(15,7))
sns.heatmap(df.corr(), annot = True, cmap='coolwarm')
我们可以看到解释变量之间并不相关(来源:作者)。
sns.pairplot(data = df)
同样,我们也没有发现解释变量之间有任何强有力的联系(来源:作者)。
结论
运行线性回归模型并获得一些重要结果后,您应该检查支持模型有效性的假设是否得到验证:
- X(解释变量)和 Y ( 因变量)之间的关系是线性的。
- 残差呈正态分布。
- 残差具有恒定的方差(即同方差
- 残差是独立的。
- 不存在多重共线性。
在本文中,我们使用数据集根据广告预算来预测销售额。我们认为条件已经满足,但是(当然!)模型是可以改进的。
事实上,我们现在可以着手于:
——评估离群值的影响并去除最大的影响者;
-添加相关解释变量
-转换解释变量(fx。这在我们的例子中是相关的,因为 X 和 Y 之间的关系看起来非常接近多项式回归。
最后,如果您正面临一个条件未被验证的数据集,您可以在这里找到一些关于如何修复它的好提示:
线性回归分析注释(pdf 文件)线性回归分析介绍回归实例啤酒…
people.duke.edu](http://people.duke.edu/~rnau/testing.htm)
感谢阅读!如果你喜欢这篇文章,一定要按住按钮鼓掌支持我的写作。也可以关注我在 Linkedin 上的工作。
[## 通过我的推荐链接加入 Medium-aurélie Giraud
不要错过我的下一篇文章,阅读 Medium 上成千上万的其他作者的文章!我写的是如何使用数据科学…
agiraud.medium.com](https://agiraud.medium.com/membership)
模型的性能测量
学校和大学定期进行测试。这背后的基本思想是衡量学生的表现。要明白哪个是自己的强项,哪里需要再努力。以类似的方式,我们也测试我们的机器学习模型来衡量他们的表现,并基于这种表现,我们试图了解模型做得对的地方以及它需要更加努力的地方(基本上我们需要更加努力)
在机器学习领域,除了建立模型,衡量模型的性能同样重要。基本上,我们检查我们的模型做出的预测有多好。
在这一系列文章中,我们将试图理解模型的各种性能度量是什么。
准确(性)
这可能是最简单的性能指标。它被定义为:
作者图片
精确度值介于 0 和 1 之间。如果该值接近 0,则认为性能差,而如果该值接近 1,则认为性能好。这是最简单和容易理解的度量之一。
让我们用一个例子来理解这个指标:
假设我们已经使用训练数据训练了我们的模型。现在,我们想使用测试数据并检查预测的准确性。假设我们的测试数据集中有一个 100 个数据点的分类问题。目标是分类该点是正的还是负的。假设满分 100 分,我们有 60 个正分,40 个负分(注意这是原始/实际的班级标签)。现在,当我们将这个测试数据输入到我们的模型中时,假设我们得到下面的输出:
作者图片
因此,根据上面的例子,我们的模型将 7 个点误分类为负面,将 5 个点误分类为正面。所以总体误分类点= 7+5 = 12。
因此,模型的精度可以计算为:
作者图片
现在我们已经了解了如何计算精度,让我们了解一些与之相关的问题。
不平衡数据集
假设我们有一个返回负类作为输出的模型。现在,假设我们有一个不平衡的数据集,也是我的测试数据集,假设总测试数据集的 90%是负的。现在,当我们将这个测试数据输入到我们的模型中时,我们将获得 90%的正确分类,因为我的模型返回负的类标签,而我的测试数据集的 90%是负的。在这种情况下,即使是哑模型也能给我 90%的准确率。所以当你有一个不平衡的数据集时,远离准确性。
准确性不考虑概率分数
考虑下面的例子来理解这一点:
作者图片
x ➜数据点
y ➜实际类别标签
模型 M1 的 M1➜概率得分
M2 模型 M2 的➜概率得分
Y1 ➜预测模型 M1 的类别标签
Y2 ➜预测模型 M2 的类别标签
让我们假设我们通过两个模型 M1 和 M2 运行我们的数据,这些模型返回概率得分。所以给定一个数据点,我们得到概率 P(y=1)。
M1 可以被解读为当通过模型 M1 运行时,y1=1 的 x1 的概率得分是 0.9。类似地,具有 y3=1 的 x3 的概率得分是 0.1,这意味着当运行模型 M1 时,y3=1 的概率非常小(意味着 P(y3=0) = 0.9)。
考虑 x1。对于 x1,实际的类标签是 1。我们的模型 M1 给出了 P(y=1) = 0.9 的概率分数,并且被预测为属于类标签 1。另一方面,在模型中,M2 给出了概率分数 P(y=1) = 0.6,因此它也被分类为类标签 1。但如果我们考虑概率得分,很明显我的模特 M1 比我的模特 M2 表现得更好。类似地,对于 x2、x3 和 x4,模型 M1 的概率得分比模型 M2 好得多。但是,它们的预测类标签在两个模型中保持不变。准确性作为一种衡量标准并不能区分哪个模型更好,因为它不使用概率得分。它只能使用预测的类别标签,由于它使用预测的类别标签来计算精度,它会说模型 M1 和 M2 具有相同的精度,但从概率得分来看,很明显 M1 比 M2 好。
混淆矩阵
为了理解混淆矩阵,让我们进行一个二进制分类任务,目标是将类别标签分类为 0(负)或 1(正)。让我们构建同样的混淆矩阵。
TN ➜真阴性
FN ➜假阴性
FP ➜假阳性
TP ➜真阳性
N ➜总阴性点数
P ➜总阳性点数
让我们来理解上述每个术语:
- 真负当实际值为 0 且模型预测值也为 0 时的➔
- 假阴性 ➔当实际值为 1 而模型预测值为 0 时
- 实际值为 1 且模型预测值也为 1 时的真正 ➔
- 实际值为 0 而模型预测值为 1 时的误报 ➔
现在我们已经了解了如何构建混淆矩阵及其基本术语,让我们了解一些与之相关的关键指标。
真阳性率(TPR) = #的 TP /总的 P
真实负比率(TNR) = #总人数/总人数
假阳性率(FPR)= FP 的数量/N 的总数
假阴性率(FNR) = # of FN / Total # of P
Precision = TP / (TP + FP)
这意味着,在模型预测为正的所有点中,有多少%实际上是正的。准确地说,我们并不担心负类。我们唯一关注的是正面的阶级标签。
Recall = TPR = TP / Total # of P
这意味着,在“实际上”属于类标签 1 的所有点中,有多少个模型被预测为类标签 1。
对于一个好的模型,我们总是希望精确度和召回率的值很高。
f1-得分:
它是指标精度和召回率的组合,给出如下:
结论
到目前为止,我们已经介绍了准确性和混淆矩阵,并且了解了混淆矩阵下的各种术语。在本系列的第二部分,我们将了解 ROC 和 AUC、对数损失、测定系数和误差的中值绝对偏差。
直到那时,快乐学习!
迪帕克·贾恩
绩效指标:准确性
你的机器学习模型有多好?
图片由 Pexels 的 Engin Akyurt 提供
机器学习模型是使用训练数据(具有输入和输出)构建的。使用相同的模型对测试数据(没有输出标签的看不见的数据)进行预测。但是你如何判断这个模型的有效性呢?必须有一些措施来评估模型的性能。
评价模型性能的性能指标有很多,如准确率、精确度、召回率、F1 值、ROC 曲线等。每一种都有其优点和缺点。在本文中,我们将讨论准确性性能指标。它只与分类任务相关。
有人可能想知道什么是分类?
当机器学习模型将数据分类成类(输出属于一个有限集)时,该任务被称为分类。分类任务的示例有:欺诈交易的识别、患者任何疾病的检测等。
性能度量总是在测试数据集上评估模型的性能。在本文中,我们将使用二进制分类(这里只有两个类),因为它很容易理解。
准确(性)
它是正确分类的点(预测)与预测总数的比率。其值介于 0 和 1 之间。
例子
实际值和预测值
上面例子中的精度是多少?
因为 4 个预测中有 2 个是正确的,所以准确度=2/4 = 0.5 或 50%。我们是否可以得出这样的结论:如果模型的精度是好的,那么模型就是充分的?
图片由 Pexels 的 cottonbro 提供
准确性性能指标中存在一些漏洞:
- 不平衡数据
- 当模型给出概率分数时
1.不平衡数据
上面讨论的例子是两个类具有相等比例的数据点。这种类型的数据集称为平衡数据集,其中两个类的数据点大致相等。
如果我们有一个不平衡的数据集(其中一个类的点数远远大于另一个类),该怎么办?不平衡的数据集在现实世界的例子中非常常见。罕见疾病的数据集与阴性人群相比,阳性人群非常少。
示例
患者总数为 100 人,其中 95 人呈阳性(这是一个不平衡的数据集)。假设机器学习模型是哑的,它只给出正输出。
现在,精确度是多少?
该模型将正确分类 100 分中的 95 分。我们将获得 95%的准确率。
图片由来自 Pexels 的 Andrea Piacquadio 提供
别太激动;记住,模型是愚蠢的。
在处理不平衡的数据时,准确性可能会产生误导。因此,最好不要使用带有不平衡数据的准确性性能指标。
2.当模型给出概率分数时
假设我们有三个模型 A,B,C,用于二进制分类,所有这三个模型都以概率得分的形式给出输出。在本例中,我们将阈值设为 0.5,即如果概率> 0.5,则预测值为 1,否则为 0。
具有概率得分和基于阈值 0.5 的预测的 a、B 和 C 模型
基于阈值,所有三个模型都给出了 100%的准确性。但是你能根据预测的准确性推断出哪个模型是最好的吗?
否
因此,当模型给出概率作为输出时,精确度不足以比较模型。
结论
在本文中,我们已经看到了分类任务中准确性性能度量的使用。当处理不平衡数据时,以及当模型给出概率得分时,准确性度量是不合适的。这些准确性问题可以通过其他性能指标来解决,如混淆矩阵、精确度、召回率和 F1 分数,这些将在下一篇文章中讨论。
感谢阅读!
性能指标:混淆矩阵、精确度、召回率和 F1 分数
解开困惑矩阵背后的困惑
图片由乔恩·泰森拍摄,来自 Unsplash
在处理不平衡数据时,准确性性能指标可能是决定性的。在这篇博客中,我们将学习混淆矩阵及其相关术语,这些术语看起来令人困惑,但却很琐碎。与准确性相比,混淆矩阵、精确度、回忆和 F1 分数给出了更好的预测结果的直觉。为了理解这些概念,我们将把本文仅限于二进制分类。
什么是混淆矩阵?
它是一个大小为 2×2 的矩阵,用于二进制分类,实际值在一个轴上,而预测值在另一个轴上。
混淆矩阵
让我们来理解混淆矩阵中容易混淆的术语:真阳性、真阴性、假阴性、假阳性并举例说明。
举例
训练机器学习模型来预测患者中的肿瘤。测试数据集由 100 人组成。
用于肿瘤检测的混淆矩阵
真正 (TP) —模型正确预测正类(预测和实际均为正)。在上面的例子中, 10 个患有肿瘤的人被模型预测为阳性。
【真负(TN)】—模型正确预测负类(预测和实际均为负)。在上面的例子中, 60 个没有肿瘤的人被模型预测为阴性。
假阳性(FP) —模型给出了阴性类别的错误预测(预测阳性,实际阴性)。在上面的例子中, 22 个人被预测为患有肿瘤的阳性,尽管他们没有肿瘤。FP 也被称为类型 I 错误。
【假阴性(FN) —模型错误预测阳性类别(预测-阴性,实际-阳性)。在上面的例子中,8 个患有肿瘤的人被预测为阴性。FN 也被称为类型 II 错误。
借助这四个值,我们可以计算真阳性率(TPR)、假阴性率(FPR)、真阴性率(TNR)和假阴性率(FNR)。
即使数据不平衡,我们也能判断出我们的模型是否运行良好。为此,TPR 和 TNR 的值应该高,FPR 和 FNR 的值应该尽可能低。
在 TP、TN、FN 和 FP 的帮助下,可以计算其他性能指标。
精确度,回忆
精确度和召回率对于信息检索都是至关重要的,其中正面类别比负面类别更重要。为什么?
当在网络上搜索某些东西时,模型不关心与无关的和未被检索到的(这是真正的否定情况)。因此,只有 TP、FP、FN 用于精度和召回。
精度
在所有积极的预测中,真正积极的占百分之几。
精度值介于 0 和 1 之间。
回忆
在全部阳性中,有多少百分比被预测为阳性。与 TPR(真阳性率)相同。
精确和召回有什么用?我们通过例子来看。
示例 1-信用卡欺诈检测
用于信用卡欺诈检测的混淆矩阵
我们不想错过任何欺诈交易。因此,我们希望假阴性尽可能低。在这些情况下,我们可以向低精度妥协,但召回率应该很高。同样,在医疗应用中,我们也不想漏掉任何一个患者。因此,我们注重高召回率。
到目前为止,我们已经讨论了什么时候召回比精确更重要。但是,什么时候精度比回忆更重要?
示例 2 —垃圾邮件检测
垃圾邮件检测的混淆矩阵
在垃圾邮件的检测中,如果有任何垃圾邮件未被检测到(假阴性),这是可以的,但是如果我们错过了任何重要的邮件,因为它被归类为垃圾邮件(假阳性),那该怎么办呢?在这种情况下,假阳性应该尽可能的低。在这里,精确比回忆更重要。
当比较不同的模型时,将很难决定哪个更好(高精度和低召回或反之亦然)。因此,应该有一个将这两者结合起来的指标。一个这样的指标是 F1 分数。
F1 分数
这是精确和回忆的调和平均值。它同时考虑了假阳性和假阴性。因此,它在不平衡数据集上表现良好。
F1 分数给予回忆和精确相同的权重。
有一个加权 F1 分数,其中我们可以给召回率和精确度不同的权重。正如上一节所讨论的,不同的问题给召回率和精确度不同的权重。
Beta 代表多少次回忆比精度更重要。如果召回的重要性是精确的两倍,那么 Beta 的值就是 2。
结论
与准确性性能指标相比,混淆矩阵、精确度、召回率和 F1 分数为预测提供了更好的见解。精确度、召回率和 F1 分数的应用是在信息检索、分词、命名实体识别和许多其他方面。
分类机器学习问题的性能度量
有大量的性能指标,并且每一个指标都有变化(图片由 Unsplash 上的 Cesar Carlevarino Aragon 拍摄)
准确度、精确度、召回率、F1 分数、ROC AUC、对数损失
已经提出了许多学习算法。评估算法的有效性通常是有价值的。在许多情况下,这种评估是相对的,即评估几个备选算法中的哪一个最适合特定的应用。
人们甚至最终会创建适合应用程序的指标。在本文中,我们将看到问题分类设置中一些最常见的指标。
分类问题最常用的性能度量如下:
- 准确(性)
- 混淆矩阵
- 精确度、召回率和 F1 分数
- ROC AUC
- 原木损失
准确(性)
准确度是正确分类的点数与总点数之间的简单比率。
为了计算精度,scikit-learn 提供了一个实用函数。
**from** **sklearn.metrics** **import** accuracy_score#predicted y values
y_pred = [0, 2, 1, 3]#actual y values
y_true = [0, 1, 2, 3]accuracy_score(y_true, y_pred)
0.5
精确度计算起来很简单,但有其自身的缺点。
准确性的限制
- 如果数据集是高度不平衡的,并且模型将所有的数据点分类为多数类数据点,则准确率会很高。这使得准确性对于不平衡数据来说不是一个可靠的性能指标。
- 根据精确度,可以推导出模型预测的概率。所以从精度上,我们无法衡量模型的预测有多好。
混淆矩阵
混淆矩阵是特定表格布局中预测结果的总结,其允许对二分类问题(2 类)或多分类问题(多于 2 类)的机器学习模型的性能测量进行可视化
二元分类的混淆矩阵
- TP 表示真正。它可以被解释为模型预测的正类并且是真实的。
- FP 表示假阳性。它可以被解释为模型预测的正类,但它是假的。
- FN 表示假阴性。它可以被解释为模型预测的负类,但它是假的。
- TN 表示真负。它可以被解释为模型预测的负类并且它是真实的。
对于一个合理的模型,主对角线元素值将是高的,而非对角线元素值将是低的,即 TP,TN 将是高的。
为了在现实世界的问题中找到一个合适的例子,考虑一个诊断测试,它试图确定一个人是否患有某种疾病。在这种情况下,当这个人测试呈阳性,但实际上并没有患病时,就会出现假阳性。另一方面,当一个人测试结果为阴性时,就会出现假阴性,表明当他们实际上患有疾病时,他们是健康的。
对于具有“c”类标签的多类分类问题,混淆矩阵将是(c*c)矩阵。
为了计算混淆矩阵,sklearn 提供了一个效用函数
**from** **sklearn.metrics** **import** confusion_matrix
y_true = [2, 0, 2, 2, 0, 1]
y_pred = [0, 0, 2, 2, 0, 2]
confusion_matrix(y_true, y_pred)
array([[2, 0, 0],
[0, 0, 1],
[1, 0, 2]])
混淆矩阵的优点:
- 混淆矩阵提供了分类的详细结果。
- 混淆矩阵的导数被广泛使用。
- 使用热图可以增强对结果的直观检查。
精确度、召回率和 F-1 分数
精度是正确分类的实例占总分类实例的比例。召回是正确分类的实例占总分类实例的比例。精度和召回率给出如下:
利用混淆矩阵计算查准率和查全率的数学公式
例如,考虑一个搜索查询产生 30 个页面,其中 20 个是相关的。并且结果未能显示 40 个其他相关结果。所以精度是 20/30,召回率是 20/60。
精确度有助于我们理解结果有多有用。回忆帮助我们理解结果有多完整。
但是为了减少两次口袋检查,使用 F1 分数。F1 分数是精确度和召回率的调和平均值。它被给定为,
何时使用 F1 分数?
- F-score 通常用于信息检索领域,用于测量搜索、文档分类和查询分类性能。
- F-score 在自然语言处理文献中得到了广泛的应用,如命名实体识别和分词的评价。
原木损失
对数损失(或对数损失)衡量分类模型的性能,其中预测是介于 0 和 1 之间的概率值。随着预测概率偏离实际标签,测井曲线损失增加。日志损失是一个广泛用于 Kaggle 比赛的指标。
这里‘N’是数据集中数据点的总数,yi 是 y 的实际值,pi 是 y 属于正类的概率。
对数损失值越低,模型的预测就越好。
为了计算日志损失,scikit-learn 提供了一个实用函数。
**from** **sklearn.metrics** **import** log_losslog_loss(y_true, y_pred)
ROC AUC
通过在各种阈值设置下绘制真阳性(TP)对假阳性(FP)来创建接收器工作特性曲线或 ROC 曲线。ROC 曲线通过绘制 y 轴上真阳性的累积分布函数与 x 轴上假阳性的累积分布函数来生成。
虚曲线是 ROC 曲线
ROC 曲线下的面积(ROC AUC)是用于评估性能的单值度量。
AUC 越高,模型区分类别的性能越好。
一般来说,0.5 的 AUC 表明没有歧视,0.5-0.7 之间的值是可以接受的,任何高于 0.7 的值都是可以使用的模型。然而,医学诊断模型,通常 AUC 为 0.95 或更高被认为是良好的模型。
什么时候用 ROC?
- ROC 曲线被广泛用于比较和评估不同的分类算法。
- 当数据集不平衡时,ROC 曲线被广泛使用。
- ROC 曲线也用于验证气象学中的预测
谢谢你的阅读。以后我会写更多初学者友好的帖子。请在媒体上关注我,以便了解他们。我欢迎反馈,可以通过 Twitter ramya_vidiyala 和 LinkedIn RamyaVidiyala 联系我。快乐学习!
性能指标:受试者操作特征(ROC)-曲线下面积(AUC)
通过示例了解 ROC-AUC 性能指标
图片来自 Unsplash 的 Issac Smith
受试者工作特征曲线
接收器工作特性(ROC)曲线明确用于二元分类。但是,它可以扩展为多类分类。
在二元分类中,当一个模型给出概率得分作为输出时,我们用 0.5 作为最简单模型的阈值。如果查询点的概率大于 0.5,则模型会将其分类为第 1 类(即肯定的),否则为第 0 类(否定的)。为了衡量模型的性能,我们可以使用准确度、混淆矩阵、精确度、召回率和 F1 分数。
这里出现的一个问题是,使用 0.5 作为阈值会给每个模型带来显著的结果吗?
否
选择产生显著结果的阈值取决于具体问题。例如,在癌症检测问题中,如果我们保持低阈值,模型将会预测更多的人为阳性。此外,如果我们设置一个高得令人难以置信的阈值,就有可能遗漏实际患者。
我们应该如何决定合适的阈值?
ROC 是一种可以给出最佳阈值的技术。
让我们举一个微不足道的例子
步伐
- 将唯一概率分数(按降序)作为阈值,并预测类别标签。如果我们有 k 个唯一的概率得分,就会有 k 个阈值。
- 对于每个阈值,我们测量所有查询点的类别标签。
对于每个预测,我们计算真阳性率(TPR)和假阳性率(FPR)。
我们来看混淆矩阵。
混淆矩阵
真阳性率衡量实际阳性总数中的真阳性(真阳性(TP) +假阴性(FN))。
假阴性率衡量实际阴性总数中的假阳性(真阴性(TN) +假阳性(FP))。
如果我们有 k 个唯一的概率得分,我们将有 k 个不同的预测。因此,我们将有 k 对(TPR,FPR)。
在上面的例子中,我们有 5 个预测,所以会有 5 个 TPR,FPR 对。
预测 1(阈值= 0.95)
预测 1 的混淆矩阵(阈值= 0.95)
TP R1 = 1/(1+2)= 1/3
FP R1 = 0/2 = 0
预测 2(阈值=0.92)
预测 2 的混淆矩阵(阈值= 0.92)
TPR 2 = 2/(2+1)= 2/3
FPR 2 = 0/2 = 0
预测 3(阈值= 0.7)
预测 3 的混淆矩阵(阈值= 0.7)
TPR3 = 3/3 = 1
FPR3 = 0/2 = 0
预测 4(阈值= 0.6)
预测 4 的混淆矩阵(阈值= 0.6)
TP R4 = 3/3 = 1
FP R4 = 1/(1+1)= 1/2
预测 5(阈值= 0.46)
预测 5 的混淆矩阵(阈值= 0.46)
TPR5 = 3/3 = 1
FPR5 = 2/2 = 1
3.绘制 FPR 对 TPR 曲线,y 轴为 TPR,x 轴为 FPR。
具有 5 个(FPR,TPR)对的 ROC 曲线。括号中的数字表示预测数。
蓝线显示了随机模型,这意味着模型给出了随机输出(无法对查询点进行分类)。适当的阈值将是 TPR 最大的点 和 FPR 最小的点,因为我们希望真阳性和真阴性多于假阳性和假阴性。
对于我们的示例,最佳阈值将是预测 3,,即 0.70 。
罗马纪元
TPR-FPR 曲线下的面积将给出模型有效性的概念。AUC 分数越高,模型越好。 AUC 分数用于比较不同的模型。
AUC 的最大值可以是 1。为什么?
因为 TPR 和 FPR 的最大值分别是 1。所以,最大面积是 1。
分类器正确地预测了这两个类别。在上面的例子中,我们有一个完美的分类器,因为橙色曲线下的面积是 1。
如果 AUC =0.5 我们可以推断出什么?
这意味着分类器不能将两个类分开。该模型给出随机输出。
从 AUC < 0.5 可以推断出什么?
这意味着模型预测了相反的值,即实际的阳性标签被预测为阴性,反之亦然。
下图显示了两个模型。我们如何决定哪一个更好?
两个模型的 ROC 曲线
模型 2 的曲线下面积(AUC)大于模型 1。因此,模型 2 是比模型 1 更好的分类器。
AUC 分数的一个限制是它们对不平衡的数据敏感。如果模型是哑的,AUC 分数可以很高。
结论
接收器工作特性(ROC) 曲线用于确定模型的适当阈值,其给出概率得分作为二进制分类的输出。曲线下面积(AUC) 分数用于比较不同模型。但是,它们会受到不平衡数据集的影响。
感谢阅读!
Apache Spark 在 Kubernetes 上的表现已经赶上了 YARN
了解我们的基准设置、结果和关键技巧,以便在 Kubernetes 上运行时将洗牌速度提高 10 倍!
Apache Spark 是一个开源的分布式计算框架,但是它并不管理运行它的机器集群。为此,您需要一个集群管理器(也称为调度器)。最常用的是 Apache Hadoop YARN 。在 2.3 版本中增加了对在 Kubernetes 上运行 Spark 的支持,此后 Spark-on-k8s 的采用一直在加速。
如果你对 Spark-on-Kubernetes 的核心概念、与 Yarn 的区别以及优缺点感到好奇,请阅读我们之前的文章:在 Kubernetes 上运行 Spark 的利弊。要深入了解,您还可以观看我们在 Spark Summit 2020 上的会议:在 Kubernetes 上运行 Apache Spark:最佳实践和陷阱。
在本文中,我们展示了在 Kubernetes 和 Yarn 上部署 Spark 的性能比较基准。我们的结果表明 Kubernetes 已经赶上了 Yarn——两者之间不再有显著的性能差异。特别是,我们将比较 YARN 和 Kubernetes 之间的 shuffle 性能,并为您提供在 Kubernetes 上运行 Spark 时提高 shuffle 性能的关键技巧。
基准协议
TPC-DS 基准测试
我们使用著名的 TPC-DS 基准来比较 Yarn 和 Kubernetes,因为这是 Apache Spark 和一般分布式计算的最标准基准之一。TPC-DS 基准由两部分组成:数据和查询。
- 这些数据是合成的,可以在不同的尺度上生成。它是倾斜的——意味着一些分区比其他分区大得多——以便代表真实世界的情况(例如:7 月的销售额比 1 月多得多)。对于这个基准测试,我们使用一个 1TB 数据集。
- 大约有 100 个 SQL 查询,旨在涵盖一般零售公司的大多数用例(TPC-DS 表是关于商店、销售、目录等)。因此,查询有不同的资源需求:一些查询有很高的 CPU 负载,而另一些查询是 IO 密集型的。
我们优化是为了什么?
分布式计算框架的性能是多维的:应该考虑成本和持续时间。例如,一个持续 10 小时、花费 10 美元的查询和一个 1 小时、花费 200 美元的查询,哪一个最好?这个要看你公司的需求。
在这个基准测试中,我们给 Yarn 和 Kubernetes 分配了固定数量的资源。因此,查询的成本与其持续时间成正比。这允许我们在一个维度上比较两个调度器:持续时间。
设置
该基准测试比较了 Spark 运行数据机制(部署在谷歌 Kubernetes 引擎)和 Spark 运行在 Dataproc (GCP 的托管 Hadoop 产品)。
驱动程序:N2-标准-4 实例
- 4 个虚拟 CPU
- 16GB 内存
n2-highmem-4 实例上的 5 个执行器
- 4 个虚拟 CPU
- 32GB 内存
- 375GB 本地固态硬盘
我们将每个查询运行 5 次,并报告平均持续时间。
我们在这个基准测试中使用了最近发布的 Spark 3.0 版本。与 Spark 2.4 相比,它带来了显著的性能提升,我们将在以后的博客文章中展示这些。
Kubernetes 上的火花赶上了纱线
下图显示了 Kubernetes 和 Yarn 的所有 TPC-DS 查询的性能。总体而言,它们表现出非常相似的性能。对于几乎所有的查询,Kubernetes 和 YARN 查询都在对方的+/- 10%范围内完成。从视觉上看,纱线似乎以微弱优势占据上风。
比较 Kubernetes 上的火花与纱线上的火花的性能,逐个查询。
汇总结果证实了这一趋势。使用两个调度程序运行基准测试的总持续时间非常接近,YARN 的优势为 4.5%。
当对所有查询的持续时间求和时,Kubernetes 和 YARN 也得到类似的结果,YARN 稍有优势(平均快 4.5%)。
由于每个查询我们只运行了 5 次,这 5%的差异在统计上并不显著。一般来说,与您可以获得的其他收益相比,5%的差异是很小的,例如,通过做出智能基础架构选择(实例类型、集群大小、磁盘选择),通过优化 Spark 配置(分区数量、内存管理、随机调优),或者通过从 Spark 2.4 升级到 Spark 3.0!
因此,Kubernetes 在性能方面已经赶上了 YARN 这对 Kubernetes 上的 Spark 来说是一件大事!这意味着,如果您需要在下一个项目的两个调度器之间做出决定,您应该关注性能以外的其他标准(阅读在 Kubernetes 上运行 Apache Spark 的利弊以了解我们的看法)。
在下一节中,我们将重点介绍 shuffle 的性能,这是一个可怕的全对全数据交换阶段,通常会占用 Spark 作业的大部分。我们将看到,对于 shuffle 来说,Kubernetes 也赶上了 YARN。更重要的是,我们会给你一些关键的配置技巧,让 shuffle 在 Kubernetes 上的 Spark 中表现出色。
如何在 Kubernetes 上用 Spark 优化 shuffle
TPC-DS 基准测试的大多数长查询都是无序的。下图显示了 Kubernetes 上 TPC-DS 查询的持续时间,它是混洗数据量的函数。当混洗数据量很大时(向右),混洗成为查询持续时间的主要因素。在这个区域中,洗牌和性能之间有明显的相关性。
每个 TPC-DS 查询的持续时间是被混洗的数据量的函数,两者都是对数标度。无序播放量和查询持续时间之间的关系是显而易见的(“性能墙”)。
为了减少混洗时间,调整基础设施是关键,以便尽可能快地交换数据。混洗性能取决于机器对机器数据交换的网络吞吐量,以及磁盘 I/O 速度,因为混洗数据块被写入磁盘(映射端)并从磁盘中取出(缩减端)。
更复杂的是,云提供商的大多数实例类型都使用远程磁盘(AWS 上的 EBS 和 GCP 上的持久磁盘)。这些磁盘与实例不在同一位置,因此对它们的任何 I/O 操作都将计入实例网络限制上限,并且通常会更慢。
这和 Spark shuffle 没关系。
当你的 Spark 应用程序遭遇长时间随机播放时,这里有一些简单但重要的建议:
- 尽可能使用本地固态硬盘。在 GCP 上添加本地磁盘是一个简单的切换功能。在 AWS 上,将实例类型从 r5.2xlarge 更改为 r5d.2xlarge。这些磁盘的较高成本通常在很大程度上通过整体性能的提高得到补偿。
- 如果本地磁盘不可用,增加磁盘的大小,因为磁盘延迟和吞吐量几乎与磁盘大小成正比。
在下图中,我们展示了错误选择磁盘的影响。我们使用标准持久磁盘(GCP 的标准非 SSD 远程存储)来运行 TPC-DS。它显示了当磁盘大小从 500GB 减少到 100GB 时,不同查询的持续时间增加了。对于混乱的查询,持续时间要长 4 到 6 倍!
该图显示了两种设置下的标准化 TPC-DS 查询持续时间,一种是 500GB,另一种是 100GB 持久(远程)磁盘。我们可以看到,对于大量混排的查询,性能提高了 4-6 倍。
如何在 Kubernetes 上的 Spark 中挂载本地磁盘
正如我们所展示的,本地 SSD 性能最好,但是在 Kubernetes 上运行 Spark 时,这里有一个小配置问题。
简单地定义一个本地磁盘并将其附加到您的 Kubernetes 是不够的:它们将被挂载,但默认情况下,Spark 不会使用它们。在 Kubernetes 上,需要一个 hostPath 来允许 Spark 使用挂载的磁盘。
下面是一个配置示例,在 Spark 操作符 YAML 清单风格中:
executor:
...
volumeMounts:
- mountPath: /tmp/spark-local-dir-1/
name: spark-local-dir-1
volumes:
- name: spark-local-dir-1
hostPath:
path: /mnt/disks/ssd0/ # GCP specific
...
sparkConf:
...
spark.local.dir: /tmp/spark-local-dir-1/
...
结论
⚠️免责声明: Data Mechanics 是一个无服务器的 Spark 平台,自动调整基础设施和 Spark 配置,使 Spark 尽可能简单和高效。在幕后,它部署在我们客户的云帐户中的 Kubernetes 集群上。
所以我们偏向于 Kubernetes 上的 Spark——事实上我们确信 Kubernetes 是 Apache Spark 的未来!在本文中,我们用一个标准基准测试证明了 Kubernetes 的性能已经赶上了 Apache Hadoop YARN 的性能。我们还与您分享了我们认为最重要的 I/O 和 shuffle 优化,以便您可以重现我们的结果,并在 Kubernetes 上成功使用 Spark。
如何更进一步:
- 2021 年 3 月: Apache Spark 3.1 发布版现在宣布 Spark-on-Kubernetes 正式上市,并准备好投入生产!
- 2021 年 4 月:使用我们优化的 Docker 映像,通过 Kubernetes 上的 Spark 获得最佳 I/O 性能
- 2021 年 4 月:检查我们的开源监控工具 Delight 来解决 Spark 的性能问题(在 Kubernetes 上,或者在任何 Spark 平台上)
熊猫和熊猫的性能比较
用 python 实现算法时的常见错误及 pandas 和 numpy 中常用方法的效率分析。
介绍
没有 numpy 和 pandas ,Python 中似乎就没有数据科学。(这也是 Python 在数据科学领域如此受欢迎的原因之一)。然而,将库转储到数据上很难保证性能。那到底怎么了?
让我们从根本问题开始。在设计算法时,许多涉及计算的任务可以归纳为以下几类:
- 给定条件下数据子集的选择,
- 对每个数据/表格条目应用数据转换函数,
- 向量矩阵乘法(也称为典型的线性代数)。
在本帖中,我们将尝试对这三种最常见的操作进行更多的阐述,并尝试理解发生了什么。对于所有的性能评估,我们使用了:
- Python 版本 3.6.7,
- Numpy 1.16.4 和熊猫 0.24.2,
- Ubuntu 16.04,
- 电脑:英特尔酷睿 i5–7200 u CPU @ 2.50 GHz,
- IPython 和
%timeit
命令。
性能测试
案例 1:数据子集的选择
对于第一种情况,我们从均匀随机生成的数据中选择一个正值子集。此外,我们以 numpy 数组和 pandas 数据帧的形式组织数据,作为大小为1 x N
的一维对象或大小为sqrt(N) x sqrt(N)
的二维数组,其中N
是元素的数量。对于每个N
,我们测试以下操作:
if DIM == 1:
npx = np.random.randn(N)
else:
N = int(N**0.5)
npx = np.random.randn(N, N)
dfx = pd.DataFrame(npx)# explicit loop (DIM == 1)
%timeit [x > 0 for x in npx]
# explicit loop (DIM == 2)
%timeit [x > 0 for x in [y for y in npx]]
%timeit npx > 0 # numpy
%timeit np.where(npx > 0, True, False) # np.where
%timeit dfx > 0 # dataframe
%timeit dfx.applymap(lambda x: x > 0) # applymap
%timeit dfx.apply(lambda x: x > 0, axis=0) # apply, axis=0
%timeit dfx.apply(lambda x: x > 0, axis=1) # apply, axis=1
%timeit dfx.pipe(lambda x: x > 0) # pipe
图一。选择数据子集。左图:一维数组。右图:二维数组。
首先,numpy
无论如何是最快的。原因是它是 C 编译的,并且存储相同类型的数字(参见这里的),与显式循环相反,它不对指向对象的指针进行操作。np.where
函数是在 numpy 数组上实现元素条件的常用方法。它经常派上用场,但是它的性能代价很小,这与函数调用的开销有关。
当谈到 pandas dataframes 时,它们的主要优势是能够存储不同类型的关联数据,这改进了数据分析过程和代码可读性。同时,这种灵活性也是 dataframes 在性能方面的主要缺点。查看图 1,我们可以看到,无论数组大小如何,调用计算都需要付出 1 毫秒的初始代价。然后,剩下的就只看数组大小和…它的元素排列了!
x > 0
是一个非常简单的条件,可以应用于任何数值数据。因为我们所有的日期元素都是数字,所以可以在所有行(df.apply(..., axis=0)
、列(df.apply(..., axis=1)
)、元素方式(df.applymap
)或整个数据帧(df.pipe
)上应用它,因此它给了我们一个很好的测试方法。比较一维数组和二维数组,我们可以立即发现apply
方法中axis
参数的重要性。虽然我们的数据可能不总是允许我们在这些方法之间进行选择,但是我们应该尝试沿着最短的轴(在这个例子中是列)对进行矢量化。如果列数和行数相当,那么df.applymap
或df.pipe
是更好的选择。
最后但同样重要的是,可以注意到阵列的形状也会影响缩放比例。除了 numpy(在初始常数之后),数据帧上的执行时间不是线性的。尽管如此,与 numpy 和 pandas 方法相关的执行时间之间可能的交叉似乎发生在至少有1e15
个元素的区域,这就是云计算的用武之地。
案例 2:对数据应用原子函数
现在,让我们看看应用一个简单的原子计算需要什么:对每个数求平方根。为了避免陷入复数,让我们只使用正数。此外,我们引入了vsqrt
——sqrt
函数的矢量化版本(但不等同于np.sqrt
),以便考虑将外来函数引入 numpy 的情况。最后,我们来看看直接通过.apply
调用sqrt
,还是通过一个lambda
调用sqrt
的区别。
我们测试以下功能:
if DIM == 1:
npx = np.random.random((N, 1))
else:
N = int(N**0.5)
npx = np.random.random((N, N))
dfx = pd.DataFrame(npx)
def sqrt(x):
return x**0.5
vsqrt = np.vectorize(sqrt)
# explicit loop (DIM == 1)
%timeit [sqrt(x) for x in npx]
# explicit loop (DIM == 2)
%timeit [sqrt(x) for x in [y for y in npx]]
%timeit sqrt(npx) # numpy
%timeit vsqrt(npx) # np.vectorize
%timeit dfx.applymap(sqrt(x)) # df.applymap
%timeit dfx.apply(sqrt, axis=0) # df.apply, axis=0
%timeit dfx.apply(sqrt, axis=1) # df.apply, axis=1
%timeit dfx.apply(lambda x: sqrt(x), axis=0) # df.apply, axis=0, as lambda
%timeit dfx.apply(lambda x: sqrt(x), axis=1) # df.apply, axis=1, as lambda
图二。对数据应用函数。左图:一维数组。右图:二维数组。
同样,基本 numpy 击败了所有其他方法。与前一种情况相比,我们还可以看到 pandas dataframe 对象的类似行为。
然而,有趣的是,与显式循环相比,平方根函数的矢量化形式似乎表现不佳。虽然对于 1 维阵列几乎相同,但对于 2 维情况,它的性能远不如 loop,甚至不如 pandas。如果原始函数相对更复杂,包含多个循环和条件,也许它确实有意义?无论如何,构造可以直接应用于 numpy 数组的函数似乎更有效。
最后,图 2。显示了使用lambda
或直接调用df.apply
方法之间没有实际区别。匿名函数确实提供了更多的灵活性(当x
变成行或列时),但是这里没有损失。
案例 3:向量矩阵乘法
最后,本文的最后一个案例涉及到最常见的数字运算之一:计算向量和矩阵之间的点积。数学上,这个操作可以定义为y = A x
,其中y
的每个元素都是通过取
M
次,对于N x M
数组,产生NM
乘法和N(M — 1)
加法。
暂时把熊猫放在一边,numpy 已经提供了一堆功能可以做同样的事情。
- np.dot —两个数组的类属点积,
- np.matmul —将所有数组的元素视为矩阵,
- np.inner —替代
np.dot
,但灵活性降低, - np.tensordot —最一般的(一般化到张量)点积。
为简单起见,我们使用一个方阵,并计算以下维度的乘积(N x M) x (N x 1)
,产生N(2N — 1)
运算。
A = np.random.randn(N, N)
X = np.random.randn(N)
def naive(A, X):
Y = np.zeros(A.shape[1])
for i in range(A.shape[0]):
for j in range(A.shape[1]):
Y[j] += A[i, j]*X[i]
return Y
%timeit naive(A, X)
%timeit np.dot(A, X)
%timeit np.matmul(A, X)
%timeit np.inner(A, X)
%timeit np.tensordot(A, X, axes=1)
图 3。向量矩阵乘法。黑色虚线表示操作次数。
从图 3 中可以明显看出,定制的基于循环的实现甚至要差三个数量级。同时,上面列出的点积的不同变体之间没有真正的区别。最初的平坦特征可以用与函数调用本身相关联的惩罚来解释。函数越复杂(如np.tensordot
),它就变得越高。然而,一旦数字相对较大,执行时间就由实际计算时间决定,而实际计算时间对于初始函数来说是不可知的。
结论
研究了这三个案例后,以下建议是有意义的:
- 数学永远用 numpy,避免“幼稚计算”。
- 如果存在 numpy 本地方法,最好使用它们。
- 如果没有,至少尝试将数据封装在 numpy 数组中。
- 如果使用熊猫数据帧,使用
apply
、applymap
和pipe
。 - 但是,请记住,数据帧的形状会强烈影响特别是
apply
方法的性能。
那么…你的代码还是运行缓慢吗?再找找看!也许现在还不是从 python 转向 C 的时候?也许还有一两种方法可以让整个事情慢下来,或者你甚至已经进行了计算切腹自杀?
还会有更多…
我计划把文章带到下一个层次,并提供简短的视频教程。
如果您想获得关于视频和未来文章的更新,订阅我的 简讯 。你也可以通过填写表格让我知道你的期望。回头见!
R 中的性能优化:并行计算和 Rcpp
R 中性能优化的快速介绍:并行和 Rcpp 包。
“平行”包装
通过使用并行计算,R 中的许多计算可以变得更快。一般来说,并行计算是在多个计算处理器或核心上同时执行较大计算的不同部分。
parallel
包可用于将任务(编码为函数调用)并行发送到机器上的每个处理核心。
mclapply()
函数本质上是将对lapply()
的调用并行化。mclapply()
的前两个参数与lapply()
的参数完全相同。然而,mclapply()
有更多的参数(必须命名),其中最重要的是mc.cores
参数,您可以使用它来指定您想要将计算划分到多少个处理器/内核上。例如,如果您的机器上有 4 个内核,您可以指定mc.cores = 4
来中断您的跨 4 个内核的并行化操作(尽管如果您正在后台运行除 R 之外的其他操作,这可能不是最好的主意)。
你可能想用parallel
包检查的第一件事是你的计算机实际上是否有你可以利用的多个内核。
require(parallel)
cores <- detectCores()
cores## [1] 8
mclapply()
函数(以及相关的mc*
函数)通过 Unix 风格的操作系统上的 fork 机制工作。由于使用了 fork 机制,Windows 操作系统的用户通常无法使用mc*
功能。
mclapply(1:7, FUN = function(x) return(x), mc.cores = cores-1)## Error in mclapply(1:7, FUN = function(x) return(x), mc.cores = cores - : 'mc.cores' > 1 is not supported on Windows
在您的计算机上使用分叉机制是执行并行计算的一种方式,但它不是并行包提供的唯一方式。另一种使用计算机上的多核构建“集群”的方法是通过插槽。
用 R 中的makeCluster()
函数构建一个套接字集群很简单。
cl <- makeCluster(cores-1)
cl
对象是整个集群的抽象,我们将使用它向各种集群函数表明我们想要进行并行计算。
为了在套接字集群上执行lapply()
操作,我们可以使用parLapply()
函数。
# sample function
test <- function(){
Sys.sleep(2)
return(TRUE)
}
# call "test" in parallel apply
parLapply(cl = cl, 1:7, fun = function(x) {
test()
})## Error in checkForRemoteErrors(val): 7 nodes produced errors; first error: could not find function "test"
不幸的是,您会注意到在运行这段代码时有一个错误。原因是,虽然我们已经将硫酸盐数据加载到 R 会话中,但是这些数据对于由makeCluster()
函数生成的独立子进程是不可用的。数据,以及子进程执行代码所需的任何其他信息,需要通过clusterExport()
函数从父进程导出到子进程。需要导出数据是“多核”方法和“套接字”方法之间行为的一个关键区别。
# export "test" to the cluster nodes
clusterExport(cl, "test")
# call "test" in parallel apply
parLapply(cl = cl, 1:7, fun = function(x) {
test()
})## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] TRUE
##
## [[6]]
## [1] TRUE
##
## [[7]]
## [1] TRUE
需要多长时间?
# parallel
t0 <- proc.time()
xx <- parLapply(cl = cl, 1:7, fun = function(x) {
test()
})
t1 <- proc.time()
t1-t0## user system elapsed
## 0.03 0.00 2.05# serial
t0 <- proc.time()
xx <- lapply(1:7, FUN = function(x) {
test()
})
t1 <- proc.time()
t1-t0## user system elapsed
## 0.02 0.00 14.14
clusterEvalQ()
计算每个集群节点上的文字表达式。它可以用于将包加载到每个节点中。
# load the zoo package in each node
clusterEvalQ(cl = cl, require(zoo))## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] TRUE
##
## [[6]]
## [1] TRUE
##
## [[7]]
## [1] TRUE# call zoo functions in parallel apply
parLapply(cl = cl, 1:7, fun = function(x) {
is.zoo(zoo())
})## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] TRUE
##
## [[6]]
## [1] TRUE
##
## [[7]]
## [1] TRUE
一旦您完成了集群的工作,最好清理并停止集群子进程(退出 R 也会停止所有子进程)。
stopCluster(cl)
“Rcpp”包
Rcpp
包提供了 C++类,使用 R 提供的.Call()
接口极大地方便了 R 包中 C 或 C++代码的接口。它在 R 之上提供了一个强大的 API,允许 R 和 C++之间直接交换丰富的 R 对象(包括 S3、S4 或引用类对象)。
将 C++代码保存在自己的源文件中有几个好处(推荐)。然而,也可以进行 C++代码的内联声明和执行,这将在下面的示例中使用。
让我们用 R 和 C++实现斐波那契数列:
与 F₀=1 和 F₁=1 。
fibR <- function(n){
if(n==0) return(0)
if(n==1) return(1)
return(fibR(n-1) + fibR(n-2))
}Rcpp::cppFunction("
int fibC(const int n){
if(n==0) return(0);
if(n==1) return(1);
return(fibC(n-1) + fibC(n-2));
}")
比较性能:
require(microbenchmark)
microbenchmark(fibR(20), fibC(20))## Unit: microseconds
## expr min lq mean median uq max neval
## fibR(20) 11354.8 13077.50 16629.646 14720.85 18781.75 33800.5 100
## fibC(20) 35.6 39.05 68.486 51.10 57.10 1224.5 100
参考
[1]https://book down . org/rd Peng/rprogdatascience/parallel-computation . html
[2]http://heather.cs.ucdavis.edu/~matloff/158/RcppTutorial.pdf
用 Locust 对 ML 服务 API 进行性能测试!
确保您的 ML-serving API 在生产中使用时能够处理适当的预期性能负载
朋友们,又见面了!欢迎回到另一个数据科学快速技巧。现在,当谈到数据科学的整个领域(从发现到生产)时,这篇文章肯定会落在这个领域的末端。事实上,一些公司可能认为这是机器学习工程师的工作,而不是数据科学家的工作。作为一名机器学习工程师,我可以证明我的情况确实如此。
尽管如此,我相信有许多数据科学家负责部署他们自己的机器学习模型,这篇文章有望为如何使用这个名为 Locust 的简洁工具进行简单的性能测试提供一些启示。
在我们开始性能测试之前,让我们花一点时间讨论一下 API 本身。使用我们在以前的一篇文章中创建的虚拟模型,我们将使用 Flask 和 Gunicorn 来服务 API 端点后面的模型。用户将向端点发布适当的 JSON 数据,并从机器学习模型接收回预期的预测。现在,这篇文章不是关于创建一个 ML-serving API,所以我很快为我们的目的创建了一个。为了使用它,您需要做的就是从 GitHub 下载我的代码,导航到“api”文件夹,并在您的终端中运行以下命令:
bash run.sh
这将做的是在本地主机:5001 的一个 Gunicorn 服务器后面运行您的 API。如果你看到这个屏幕,你就在正确的轨道上。
在终端中保持该标签打开,并打开一个新标签。只是为了验证 API 是否真的在工作,我创建了一个单独的小测试,它将通过 API 快速运行 2 次观察。在同一个代码库中,导航到“test_data”目录并运行以下命令:
bash tests.sh
如果 API 正常工作,您应该会看到下面的屏幕:
好了,我们准备进入这篇文章的核心部分:性能测试!当机器学习模型在生产环境中的 API 上下文中使用时,确保它能够处理适当的请求负载是非常重要的。如果你有太多的用户或太多的请求,你可能会遇到一些大问题。你不想成为导致产量下降的人吧!
幸运的是,人们制作了这个叫做 Locust(或 Locust.io)的工具来帮助解决这个问题。起初,代码可能看起来很奇怪,但是我们将在这里简单地解释一下,这样您就可以很快开始运行了。
首先,你可能需要第一次在你的机器上安装 Locust。简单到可以做!只需运行以下 pip 命令从 PyPi 下载 Locust:
pip install locust
好了,现在我们准备好构建我们的 Locustfile 了!Locustfile 是一个简单的 Python 脚本,我们将调用它来启动 Locust,它的用户界面非常方便。默认情况下,Locust 命令行工具会查找一个名为“locustfile.py”的文件,但是您可以随意命名它(只要您用-f 标志指定它)。为了简单起见,我们将我们的文件命名为默认的 locustfile.py,这是我们要放入的所有内容。
from locust import HttpUser, task, between
import json# Loading the test JSON data
with open('test_data/test_1.json') as f:
test_data = json.loads(f.read())# Creating an API User class inheriting from Locust's HttpUser class
class APIUser(HttpUser):
# Setting the host name and wait_time
host = '[http://localhost:5001'](http://localhost:5001')
wait_time = between(3, 5) # Defining the post task using the JSON test data
[@task](http://twitter.com/task)()
def predict_endpoint(self):
self.client.post('/predict', json = test_data)
这是一个非常小的脚本,但它将为我们做一些强大的事情!现在,您第一次看到这个语法时,可能会有点奇怪,所以让我们一点一点地分解它,以便您理解这里发生了什么。从这第一点开始…
from locust import HttpUser, task, between
import json# Loading the test JSON data
with open('test_data/test_1.json') as f:
test_data = json.loads(f.read())
我们只是从 Locust 和 JSON 中导入我们需要的东西,并加载我已经提供的测试 JSON 数据。到目前为止,这可能不是你不熟悉的。但是事情开始变得有点棘手了。我们会慢慢来。
# Creating an API User class inheriting from Locust's HttpUser class
class APIUser(HttpUser):
好的,所以你可能熟悉 Python 类。这是在创建一个新类,它继承了由 Locust 创建的父类“HttpUser”的内容。我不打算深入探讨那个类的属性/方法,但可以说,这就是我们很快将在用户界面中使用的 Locust。
向前移动…
# Setting the host name and wait_time
host = '[http://localhost:5001'](http://localhost:5001')
wait_time = between(3, 5)
这里的主机可能非常简单:我们只是提供 API 当前服务的基本 URL。(回想一下,我的 Gunicorn 脚本服务于 localhost:5001。)这段“等待 _ 时间”可能对你来说是新的。与“between()”方法一起,它记录了 Locust 在产生更多用户之前应该等待多长时间。“between()”以秒为单位,所以在我们的例子中,新用户每 3 到 5 秒就会产生一段时间。
我们剧本的最后一部分:
# Defining the post task using the JSON test data
[@task](http://twitter.com/task)()
def predict_endpoint(self):
self.client.post('/predict', json = test_data)
那个“@task()”装饰器告诉我们的 APIUser 类,当 Locust 启动时,它需要采取什么行动。您实际上可以有多个任务,甚至可以适当地对它们进行加权,但这超出了我们在这里的范围。对于我们的目的,一个任务就可以了。我们的任务需要做的就是调用“/predict”API 端点,并将我们在脚本顶部加载的 JSON 测试数据传递给它。
现在有趣的部分来了!在我们的 API 仍在运行的情况下,在您的终端中打开一个新标签。导航到包含 locustfile.py 的目录,并运行以下命令:
locust
记住,默认情况下,Locust 会查找 locustfile.py 文件,这就是为什么我们不需要在命令行中指定任何其他内容。你应该看到的是这样的东西。
值得注意的是,Locust 已经在一个特定的端口号上启动了一个 web 用户界面。在我的例子中,您会注意到 Locust UI 在 localhost:8089 之后提供。打开你选择的浏览器,导航到那里。您应该会看到下面的屏幕。
其实用户数和产卵率都会是空的。在这个例子中,我在这里指定的是我想要测试总共 100 个用户。在最开始,蝗虫将只开始测试 5 个用户的 API。然后每隔 3-5 秒(我们在脚本中指定为 wait_time),Locust 将添加另外 5 个用户,直到用户总数达到 100。来吧,点击“开始蜂拥”按钮,观看蝗虫工作的魔力。这是您将看到的第一个屏幕。
您可以看到,当我拍摄这个截图时,Locust 已经达到了 100 个用户,并且有 1866 个请求被传递给它。您还可以看到,每个请求的平均运行时间为 26 毫秒,API 每秒可以有效地处理 25 个请求(RPS)。整洁!但是你是一个视觉的人吗?导航到图表!
正如您在这些图表中看到的,很明显我们的 API 正在以非常稳定的速度运行。显然,在达到 100 个用户的上限之前,我们的 RPS 较低,但一旦我们达到 100 个用户的上限,一切都基本持平。(可能是少数几个你真的想看到扁平化的案例之一!)在这个页面上实际上还有第三个图表,它也将以图形方式显示不同种子点的用户数量,但是我已经没有截图的空间了。我们不会涉及其他选项卡,但您可能会猜到它们是做什么的。
这就是蝗虫的全部!如果您的性能与预期的用户数量持平,那么您就可以开始了。如果没有,您可能需要探索不同的部署选项。也许您可能需要扩展更多的 API 实例,或者您可能需要探索如何进一步优化模型本身的选项。至少您可以高枕无忧,因为当您将最终的 ML-serving API 推向生产时,您不会导致性能瓶颈。
这就是这篇文章的全部内容,各位!希望你喜欢这个。让我知道你还想让我在以后的帖子中涵盖哪些内容!总是喜欢听你的想法。
手动执行贝叶斯分析
概率论
现实生活中贝叶斯分析的逐步演练
介绍
与纯粹的频率分析方法相比,贝叶斯分析提供了从数据中获得更多洞察力的可能性。在这篇文章中,我将带你看一个真实生活中的例子,如何进行贝叶斯分析。我将演示当选择一个错误的先验时可能会出错,我们将看到如何总结我们的结果。为了让你阅读这篇文章,我假设你熟悉贝叶斯统计的基础和贝叶斯定理。
频率主义者和贝叶斯统计的根本区别
towardsdatascience.com](/how-to-become-a-bayesian-fcaaf6302d68)
方案
作为示例分析,我们将讨论一个来自物理实验室的真实生活问题。别担心,你不需要任何物理知识。我们想要确定粒子探测器的效率。粒子探测器是一种传感器,当某些粒子穿过它时,它可以产生可测量的信号。我们要评估的检测器效率是检测器实际测量穿越粒子的机会。为了测量这一点,我们将需要评估的检测器放在一个三明治结构的两个传感器之间。如果我们测量顶部和底部传感器中的信号,我们知道粒子也应该从中间穿过检测器。实验装置的图片如下所示。
我们想测量粒子探测器(被测设备)的效率。两个不同的传感器(触发器)放置在检测器的顶部和底部,以便检测穿过装置的粒子(在这种情况下是μ介子)。
为了测量,我们计算在一定时间内穿过的粒子数 N (由顶部和底部传感器报告)以及在我们的探测器 r 中测量的信号数。对于这个例子,我们假设 N=100 和 r=98 。
频繁结果
在频率主义方法中,我们可以使用我们的测量数据,并得出检测器效率为 e = r/N = 98% 的结论。这只能给我们一个点估计。如果要回答更复杂的问题,比如:“探测器效率在 99%以上的概率是多少”,那么就需要更复杂的分析。
贝叶斯分析
贝叶斯方法的目标是在给定我们的数据 p(e|D)的情况下,推导出检测器效率的完整后验概率分布。为了做到这一点,我们需要贝叶斯定理:
贝叶斯定理
我们将在下面讨论不同的术语。
概率模型/可能性: p(D|e)
在贝叶斯分析中,我们总是需要选择一个模型来描述我们想要分析的过程,称为可能性。对于我们的问题,我们可以将效率解释为在一定数量的尝试( N )中获得成功的机会( r )。这类问题,类似于确定硬币出现正面的几率,可以用二项式分布来建模:
二项分布
先前:p(e)
接下来,我们需要定义一个先验。这里,我们从最简单的选择开始,平坦先验。稍后我们将讨论不同的优先选择的影响。
边际可能性
边际可能性是贝叶斯定理的分母。幸运的是,它只是一个归一化常数,不依赖于效率。我们可以通过找到将后验概率归一化为 1 的常数来确定它的数值。
结果
现在我们可以根据贝叶斯定理计算后验概率。
N=100,r=98 且先验平坦的后验分布 p(e|D)
可以看到最可能的值是 e=98% 与直观的 frequentist 结果相同。但是我们在这里得到了更多的信息,因为我们得到了完整的后验概率分布。例如,我们可以看到分布是不对称的。低于 97%的效率比高于 99%的效率具有更高的可能性。对于这两种概率,我们可以给出精确的数字。我们是如何获得这些额外信息的?这是因为我们利用了更多的信息,也就是说我们已经假设检测机的行为遵循二项式分布,以及平坦的先验分布。
先验的影响
先验在贝叶斯分析中起着重要的作用。在下面,我们将看到如果我们改变它会发生什么。比方说,我们在检波器的数据手册中发现,效率可以假设为 98%左右的高斯分布,标准偏差为 s=1% 。然而,在旧版本的数据手册中,我们发现检波器的效率应呈高斯分布,约为 92%,具有相同的标准偏差 s=1% 。我们通过相应地改变先验来将这些信息合并到后验中。两种情况的结果如下所示。
不同先验的后验概率和先验概率
在这里,后验显示在顶部面板中,相应的先验显示在下面的面板中。黑色曲线显示了具有平坦先验的先前结果。当将先验改变为具有平均值 m=98% (绿色)的高斯先验时,后验峰值再次为 98%,并且与平坦先验的情况相比,我们的估计的置信度更强。先验支持我们的数据。虽然在平坦先验的情况下,低于95%的效率仍有合理的可能性,但现在几乎被排除了。从旧数据手册中获取效率达到 92%峰值的先验数据(红色),,我们可以看到后验数据与其他两个数据有显著不同。最有可能的值是 93%左右,完全改变了我们的结果。怎么会这样呢?问题在于,由于选择了错误的先验,数据和先验彼此不一致。这个例子表明,选择一个错误的先验可能会有灾难性的后果。始终评估先验、概率模型和后验之间的一致性非常重要。
包含附加测量值
先验的另一个用例是附加测量。假设您的同事测量了相同的探测器。他测量了 N1=300 和 r1=280 。如何才能正确利用这些数据?我们可以用它作为我们分析的先验。结果如下所示。
使用先前的测量作为先验
您可以看到我们的测量值(黑色)和同事的测量值(蓝色)的后验分布,两者都使用平坦先验。如果我们使用同事的测量值作为我们分析的先验,我们会得到绿色曲线。绿色曲线最有可能的值在其他两条曲线之间,但由于我们同事的测量数据更多,更偏向蓝色曲线。此外,与其他两条曲线相比,绿色曲线的分布略窄。旁注 : 得到的后验概率又是一个二项分布。此外,我们将得出相同的后验概率,就好像我们将重复分析并假设只有一个测量值,其中 N=N1+N2=400 和 r=r1+r2=378 。如您所料,结果也与两次测量的执行顺序无关。这很容易通过分析来验证。
如何展示你的成果
计算后验概率后,我们现在要展示我们的结果。理想情况下,您希望显示完整的后验分布,因为这反映了完整的信息。然而,这并不总是可能的,您可能希望用一组值来总结它。通常你想给出一个点估计和一个区间来概括分布的宽度。有不同的方法来做这件事。受欢迎的选择包括:
- 期望值和标准偏差
- 中间和中央区间
- 模式和最小间隔
此外,我们需要选择区间中应该包含多少概率(通常使用:68%或 90%)。
对于正态分布,点估计和置信区间的三种选择给出相同的结果。然而,在我们的偏态分布的例子中,情况并非如此。
点估计和相应区间的不同组合,以总结后验概率
你可以看到这三个选择导致了不同的结果。这些都没有错或对,重要的是准确报告你使用了什么样的点估计以及你是如何构建区间的。这里我们可以说,例如,我们的后验概率的最可能值(模式)是 0.98,置信区间是 0.962-0.991(最小区间包括 68%的概率密度)。
结论
我们执行了一个完整的贝叶斯分析,从建立一个概率模型开始,选择适当的先验,一直到用点估计和相应的区间总结后验。贝叶斯方法的优点是我们可以获得完整的后验概率分布。这使我们能够优雅地结合先前的知识,例如制造商的信息,或以前的测量。此外,我们看到,选择错误的先验可能对我们的结果产生重大影响,这突出表明,在任何贝叶斯分析中,仔细选择先验并评估其与概率模型和后验的一致性都是非常重要的。
生成数字和图形的 python 笔记本可在此处找到:
[## 比特桶
贮藏室ˌ仓库
bitbucket.org](https://bitbucket.org/chgraf/blog/src/master/Performing a Bayesian Analysis by Hand/)
使用正态方程执行线性回归
并不总是需要运行优化算法来执行线性回归。你可以解一个特定的代数方程——正规方程——直接得到结果。尽管对于大数据集来说,它甚至还没有接近计算最优,但它仍然是值得注意的选项之一。
安托万·道特里在 Unsplash 上拍摄的照片
1。简介
线性回归是数据分析中最重要和最流行的预测技术之一。它也是最古老的——著名的 C.F .高斯在 19 世纪初用它在天文学中计算轨道。
其目标是通过计算最小化特定成本函数(误差)的回归函数参数,例如均方误差(MSE ),将最佳线(或超平面/平面)拟合到给定点(观察值)的集合。
提醒一下,下面有一个扩展形式的线性回归方程。
情商。1:线性回归方程
在矢量化形式中,它看起来像这样:
情商。2:矢量化形式的线性回归方程
其中θ是参数权重的向量。
通常,通过运行某种优化算法(例如梯度下降)来最小化成本函数,从而找到最佳模型参数。然而,也可以通过求解称为标准方程的代数方程来获得这些参数的值(权重)。其定义如下。
情商。3:正常方程
2。手工计算
在本文中,我们将对一个非常基本的情况进行线性回归,这样我们就可以避免冗长的手工计算。顺便说一下,如果你认为你需要刷新你的线性代数技能,互联网上有很多好的资源(例如 YouTube 系列我推荐)。
在这个例子中,只有三个只有一个变量(X₁).)的点(观测值)在图表上,它们看起来像下面。
显示本例中使用的点的散点图
在这种情况下,线性回归方程具有以下形式:
情商。4:本例的线性回归方程
特征(X)和标签(y)是:
功能和标签矩阵
请注意,我们添加了一个默认偏置项 1,它将在计算过程中更新。不加这个项会导致一个错误的解。
第一步:矩阵 X 的转置
这是一个相对简单的任务——行变成新列。
步骤 2: 转置矩阵与矩阵 X 相乘
步骤 3: 对合成矩阵求逆
要对简单的 2x2 矩阵求逆,我们可以使用以下公式:
因此,我们得到:
注意:对于更大的矩阵(大于 3X3),求逆变得更加麻烦,通常使用算法方法——比如高斯消去法。记住这一点很重要!
步骤 4:X 转置的逆矩阵的乘法
第五步:最终相乘得到最佳参数的向量
最后,我们的线性回归方程采用以下形式:
情商。5:具有最佳权重的线性回归方程
将这条线绘制到前面的图上,如下所示。
具有原始点和回归线的散点图(红色)
3。用 Python 实现
使用 Numpy 库可以在 Python 中实现相同的计算,该库包含了 numpy.linalg 集合中的一组线性代数函数。
import numpy as npX = np.c_[[1,1,1],[1,2,3]] # defining features
y = np.c_[[1,3,2]] # defining labels
theta = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y) # normal equation
print(theta)
运行上述代码的结果
现在,我们可以定义我们想要预测其值的新要素。
X_new = np.c_[[1,1,1,1],[0, 0.5,1.5,4]] # new features
通过实施等式 2,我们获得了预测值。
y_pred = X_new.dot(theta) # making predictions
print(y_pred)
预测值
4。备注
如您所见,使用法线方程并在 Python 中实现它非常容易——它只有一行代码。那么为什么不常用呢?
问题在于它的数值复杂度。求解这个方程需要对一个矩阵求逆,这是一个计算量很大的操作——取决于实现方式,在大 O 符号中,它是 O(n)或略小。这意味着规模会可怕地扩大,实际上意味着当要素数量增加一倍时,计算时间会增加 2 = 8 倍。也有可能第二步的结果根本不可逆——导致大麻烦。这就是这种方法在实践中不常见的原因。
从好的方面来看,这种方法只需一步就能计算出来,你不必选择学习率参数。此外,就内存使用而言,这种方法是线性的 O(m ),这意味着它可以有效地存储大型数据集,如果它们只适合您的计算机内存的话。
功率 BI 的周期比较
在 Power BI 中比较不同时间段变得容易!
像素上的全息照相术照片
就在最近,我在 LinkedIn 平台上遇到一个问题,是否有可能在 Power BI 中创建以下可视化:
因为一个常见的业务请求是在不同的时间段之间执行不同的比较,所以我认为 Power BI 在这方面有很大的优势。我已经解释了一些与时间智能相关的基本计算,但显然有相当数量的用户并不十分熟悉它们。
首先,我要强调一个很棒的功能,叫做“快速测量”,在这里你可以获得多个常用计算的现成解决方案,例如:年初至今总计、本季度至今总计、本月至今总计、同比变化、滚动平均等。
为了让快速措施发挥作用,你需要有一个正确定义的日期表。
但是,我们不会在这里使用快速措施来实现我们最初的目标,所以让我们切换到 Power BI 桌面并开始行动吧!像往常一样,我将使用 Contoso 数据库进行演示。
创建基本度量
第一步是创建一个基本度量来计算销售额:
Sales Amt = SUM(FactOnlineSales[SalesAmount])
我将立即创建另一个度量,它将计算相同的数字,但向后移动一个月:
Sales Amt PM = CALCULATE([Sales Amt],
DATEADD(DimDate[Datekey],-1,MONTH)
)
有多种不同的方法来计算这个度量,但我更喜欢使用 DATEADD() 函数,因为它给了我更多的灵活性来改变周期(这是一个官方借口:)…实际上,我来自 SQL 世界,其中 DATEADD() 是处理日期时最重要的函数之一)。
现在,当我选择 11 月 17 日和 12 月 17 日之间的日期时,我可以看到我的数字是如何相互关联的:
正如您可能注意到的,我们的公式工作得很好—正如预期的那样,我们看到 12 月 17 日的销售额 PM 与 11 月 17 日的销售额相匹配。此外,我们的折线图很好地显示了趋势,以便于比较,而左上角的卡片视觉效果显示了所选期间的销售额以及我们正在比较的两个期间之间的差异。
对于这些差异,我创建了两个额外的衡量标准:
Sales Amt Diff PM = [Sales Amt] - [Sales Amt PM]
Sales Amt Diff PM % = DIVIDE([Sales Amt],[Sales Amt PM],BLANK()) - 1
下方的卡片是根据值有条件地格式化的,因此当我们的表现比前一时期差时,它会变成红色,而当结果相反时,它会显示绿色:
添加更多配料
现在,这很好,你也看到了我们如何轻松地回答最初的问题。然而,我想在这里添加一些更多的成分,使我们的用户能够在月环比和年同比之间进行选择。
在我们的示例中,如果我们再次选择 11 月 17 日和 12 月 17 日之间的日期,而不是显示前一个月的值(比较 12 月 17 日和 11 月 17 日),使用同比比较,我想比较 2009 年 12 月 17 日和 2008 年 12 月 17 日!
因此,让我们为此创建一个度量。同样,您可以使用不同的函数来实现这一点,如 SAMEPERIODLASTYEAR() 函数,但我想保持一致性,因此我将再次使用 DATEADD():
Sales Amt PY = CALCULATE([Sales Amt],
DATEADD(DimDate[Datekey],-1,YEAR)
)
与 MoM 计算相同,需要两个额外的指标来计算同比数据的差异:
Sales Amt Diff PY = [Sales Amt] - [Sales Amt PY]
Sales Amt Diff PY % = DIVIDE([Sales Amt],[Sales Amt PY],BLANK()) - 1
然后,我将创建两个书签,以便用户可以通过单击相应的按钮导航到 MoM 或 YoY:
默认情况下,他们应该看到 MoM 比较,但是一旦他们单击 YoY 按钮,报告看起来就会略有不同:
您可以注意到,卡片视觉效果中的数字发生了变化,以反映同比差异计算,而折线图也显示了不同的趋势!
在我们结束之前,这里是我们的报告的最后行为:
结论
正如我们所见,在时间智能计算方面,Power BI 是一个非常强大的工具。基本上,可以创建不同时期之间的各种比较—最常见的比较,甚至不需要编写一行 DAX!
如果您需要扩展内置的快速测量,有一整套有用的时间智能功能。你可以在这里更深入地查看所有这些。
感谢阅读!
订阅这里获取更多有见地的数据文章!
语言模型的困惑
使用加权分支因子评估 NLP 模型
困惑度是自然语言处理中评估模型的一个有用的度量。本文将介绍它通常的两种定义方式以及它们背后的直觉。
概述
- 语言模型的快速回顾
- 评估语言模型
- 作为测试集的归一化逆概率的困惑 3.1 测试集的概率
3.2 归一化
3.3 将所有这些集合在一起 - 作为交叉熵的指数的困惑 4.1 语言模型的交叉熵
4.2 加权分支因子:掷骰子
4.3 加权分支因子:语言模型 - 总结
1.语言模型的快速回顾
语言模型是给单词和句子分配概率的统计模型。通常,我们可能会试图猜测 中的下一个词 w 在一个句子中给出的所有前一个词,通常称为 【历史】 。
例如,给定历史“晚餐我正在做 __ ”,下一个单词是“水泥”的概率是多少?下一个单词是“fajitas”的概率有多大?希望是 P(我正在做的晚餐用的 fajitas)>P(我正在做的晚餐用的水泥)。
我们也经常对我们的模型分配给由单词序列( w_1,w_2,…,w_N )组成的完整句子 W 的概率感兴趣。
例如,我们想要一个模型来分配更高的概率给真正的句子和语法正确的句子。
一个unigram模型只在单个单词的层面上起作用。给定单词序列 W,单字模型将输出概率:
其中个体概率 P(w_i)可以例如基于训练语料库中单词的频率来估计。
一个 n-gram 模型,相反,查看前(n-1)个单词来估计下一个。例如,三元模型会查看前面的两个单词,因此:
单字对单字
语言模型可以嵌入更复杂的系统中,以帮助执行语言任务,如翻译、分类、语音识别等。
2.评估语言模型
困惑是语言模型的评估度量。但是我们为什么要用它呢?为什么不能只看我们最终系统在我们关心的任务上的损失/精度?
事实上,我们可以使用两种不同的方法来评估和比较语言模型:
- 外在评价。这包括通过在实际的任务(例如机器翻译)中使用这些模型来评估它们,并查看它们的最终损失/准确性。这是最好的选择,因为这是切实看到不同模型如何影响我们感兴趣的任务的唯一方法。然而,由于它需要训练一个完整的系统,所以在计算上可能是昂贵和缓慢的。
- 内在评价。这包括找到一些度量标准来评估语言模型本身,而不考虑它将被用于的特定任务。虽然内在评估作为最终指标不如外在评估“好”,但它是快速比较模型的有用方式。困惑是一种内在的评价方法。
3.作为测试集的归一化逆概率的困惑
这可能是困惑最常见的定义。在这一节中,我们将看到它为什么有意义。
3.1 测试集的概率
首先,什么构成了一个好的语言模型?如前所述,我们希望我们的模型将高概率分配给真实且语法正确的句子,而将低概率分配给虚假、不正确或非常罕见的句子。假设我们的数据集由事实上真实和正确的句子组成,这意味着最佳模型将是为测试集分配最高概率的模型。直观地说,如果一个模型给测试集分配了一个高概率,这意味着看到它并不惊讶(它没有被它迷惑,这意味着它对语言如何工作有很好的理解。
3.2 标准化
然而,值得注意的是数据集可以有不同数量的句子,句子可以有不同数量的单词。显然,添加更多的句子会带来更多的不确定性,所以在其他条件相同的情况下,一个较大的测试集比一个较小的测试集有更低的概率。理想情况下,我们希望有一个独立于数据集大小的指标。我们可以通过用总字数对测试集的概率进行归一化来得到这个结果,这将给我们一个每个单词的度量。
我们该怎么做?如果我们想要归一化的是一些项的总和,我们可以用它除以单词数来得到每个单词的度量。但是单词序列的概率是由乘积给出的。
例如,让我们来看一个 unigram 模型:
我们如何使这个概率正常化?通过查看对数概率更容易做到这一点,对数概率将乘积转换为总和:
我们现在可以通过除以 N 来标准化它,以获得每个单词的对数概率:
…然后通过指数运算删除日志:
我们可以看到,通过取第 N 个根,我们已经得到了归一化。
3.3 将所有内容整合在一起
现在回到我们最初的困惑等式,我们可以看到,我们可以将其解释为测试集、的、的逆概率,通过测试集中的字数:
注意:
- 由于我们采用的是逆概率,较低的复杂度表示更好的模型
- 在这种情况下,W 是测试集。它包含了所有句子一个接一个的单词序列,包括句首和句尾标记,
和 。
例如,一个包含两个句子的测试集应该是这样的:
W = ( < SOS >,this,is,the,first,sentence,。,< EOS >,< SOS >,这个,是,那个,第二个,一个,。,< EOS > )
N 是我们测试集中所有记号的计数,包括 SOS/ EOS 和标点符号。在上面的例子中,N = 16。如果我们愿意,我们也可以计算单个句子的困惑度,在这种情况下,W 将仅仅是那一个句子。
4.作为交叉熵指数的困惑
注:如果你需要复习一下熵,我强烈推荐这篇由 Sriram Vajapeyam 撰写的文献。
困惑也可以定义为交叉熵的指数:
首先,我们可以很容易地检查出这实际上等价于前面的定义:
但是我们如何根据交叉熵来解释这个定义呢?
4.1 语言模型的交叉熵
我们知道熵可以解释为在变量中存储信息所需的平均位数,它由下式给出:
我们还知道,交叉熵由下式给出:
如果我们使用一个估计的分布 q 来代替真实的概率分布 p,它可以被解释为在一个变量中存储信息所需的平均比特数。
在我们的例子中,p 是我们语言的真实分布,而 q 是我们的模型在训练集上估计的分布。显然,我们无法知道真正的 p,但给定足够长的单词序列 W(如此大的 N),我们可以使用香农-麦克米兰-布雷曼定理来近似每个单词的交叉熵(有关更多细节,我推荐【1】和【2】):
让我们重写这段代码,以便与上一节中使用的符号保持一致。给定长度为 N 的单词序列 W 和经过训练的语言模型 P,我们将交叉熵近似为:
让我们再来看看我们对困惑的定义:
根据我们对交叉熵的了解,我们可以说 H(W) 是编码每个单词所需的平均比特数。这意味着困惑度 2^H(W) 是可以使用【w】位编码的平均字数。
对此我们该如何解读?我们可以把困惑看作是加权分支因子。
4.2 加权分支系数:滚动模具
所以我们说:
例如,如果我们发现 H(W) = 2,这意味着平均每个字需要 2 位来编码,使用 2 位我们可以编码 2 = 4 个字。
但这意味着什么呢?为了简单起见,让我们暂时忘记语言和文字,想象我们的模型实际上试图预测掷骰子的结果。普通模具有 6 个面,因此模具的分支系数为 6。分支因子简单地表明了当我们掷骰子时有多少可能的结果。
假设我们在这个公平骰子上训练我们的模型,模型知道我们每次掷骰子都有 1/6 的概率得到任何一方。然后,假设我们通过滚动骰子 10 次以上来创建一个测试集,我们获得了(高度缺乏想象力的)结果序列 T = {1,2,3,4,5,6,1,2,3,4}。我们的模型在这个测试集上的困惑是什么?
所以困惑符合分支因子。
现在让我们想象一下,我们有一个不公平骰子,它以 7/12 的概率掷出一个 6,所有其他面各有 1/12 的概率。我们再次在用这个不公平骰子创建的训练集上训练一个模型,以便它学习这些概率。然后,我们通过掷骰子 12 次来创建一个新的测试集 T:我们在 7 次掷骰子中得到 6,在剩下的 5 次掷骰子中得到其他数字。现在的困惑是什么?
困惑度降低。这是因为我们的模型现在知道滚动 6 比任何其他数字都更有可能,所以看到 1 就不那么“惊讶”了,而且由于测试集中的 6 比其他数字多,所以与测试集相关联的总体“惊讶”更低。分支因子仍然是 6,因为所有 6 个数字在任何掷骰子中都是可能的选项。然而, 加权分支因子现在更低,因为一个选项比其他选项更有可能。这就好比说,在这些新的条件下,在每一次掷骰子时,我们的模型都是结果不确定的,就好像它必须在 4 个不同的选项中做出选择,而不是所有方面都有相同概率的 6 个选项。
为了进一步澄清这一点,我们把它推到极致。假设我们现在有一个不公平的骰子,给出一个概率为 99%的 6,其他数字的概率各为 1/500。我们再次在这个骰子上训练模型,然后用 100 个骰子创建一个测试集,其中我们得到 6 99 次,另一个数字一次。现在的困惑是:
分支因子仍然是 6,但加权分支因子现在是 1,因为在每次掷骰子时,模型几乎可以确定它将是 6,这是理所当然的。因此,虽然从技术上来说在每一次掷骰子时仍有 6 个可能的选项,但只有一个选项是最受欢迎的。
4.3 加权分支因子:语言模型
让我们把它与语言模型和交叉熵联系起来。
首先,如果我们有一个试图猜测下一个单词的语言模型,分支因子就是每个点上可能出现的单词的数量,也就是词汇量的大小。
我们之前说过,语言模型中的困惑是使用【W】**位可以编码的平均字数。我们现在可以看到,这仅仅代表了模型的平均分支因子。正如我们之前所说,如果我们发现交叉熵值为 2,这表明困惑度为 4,这是“可以编码的平均字数”,这就是平均分支因子。所有这一切意味着,当试图猜测下一个单词时,我们的模型** 和一样困惑,好像它必须在 4 个不同的单词之间进行选择。**
5.摘要
- 困惑是用来判断一个语言模型有多好的度量标准
- 我们可以将困惑度定义为测试集、的逆概率,由的字数归一化:
- 或者,我们可以通过使用交叉熵来定义困惑度,其中交叉熵表示编码一个单词所需的平均比特数,而困惑度是可以用这些比特编码的单词数:
- 我们可以把困惑解释为加权分支因子。如果我们的困惑度是 100,这意味着每当模型试图猜测下一个单词时,它就像必须在 100 个单词之间进行选择一样困惑。
有用的参考资料
1 Jurafsky,d .和 Martin,J. H. 语音和语言处理。第三章:N 元语言模型 (草案) (2019)。
【2】Koehn,P. 语言建模(二):平滑与回退 (2006)。数据密集型语言学(讲座幻灯片)
【3】Vajapeyam,S. 理解信息的香农熵度量 (2014)。
【4】Iacobelli,F. 困惑(2015)YouTube
【5】Lascarides,A. 语言模型:评估与平滑 (2020)。自然语言处理基础(讲座幻灯片)
【6】毛,L. 熵,困惑及其应用 (2019)。毛蕾的日志
发现这个故事有用吗?考虑 订阅 到媒体支持写手!
从 R 闪亮的应用程序中持久地存储和检索数据
在 R Shiny 中处理文件的高级指南(包括 pdf)
由 Unsplash 上的Cup 先生/杨奇煜·巴拉拍摄
我的 上一篇帖子引起了相当多的人的兴趣,因为我的上一个 R 闪亮应用的新颖和创新性质。Shiny 的这个用例几乎是闻所未闻的,因为这根本不是 R Shiny 的设计初衷。然而,由于一些个人和专业原因,我决定在 R Shiny 中承担“为我的大学考试资源创建一个集中平台”的项目,在本文中,我将分享使用 R Shiny 处理文件时可能面临的最大障碍的解决方案。
问题:R Shiny 中的持久数据
每当你在 R Shiny 应用中处理持久数据存储时,你一定会看到 Dean Attali 的文章,尤其是这篇文章。本文涵盖了各种可能的场景。然而,我还想要更多,因为它仍然不符合我的要求。
我想要的是让用户上传任意数量的文件,然后让其他用户能够下载这些文件的功能。
当你使用 R Shiny 时,你会碰到函数fileInput()
和downloadHandler()
。这些函数只能帮助您处理临时文件(只要“会话”不变,这些文件就会保留)。为了持久地存储数据,我尝试将“临时”上传的文件导出到一个“”的“配置文件中,并借助“ rdrop2 ”,但这个过程非常缓慢。此外,由于我在处理 PDF 文件,连接 MongoDB 服务器或 MySQL 服务器并不是一个可行的选择。
解决方案:本地存储
这整个磨难让我痛苦地明白了一件事。远程存储无法用于我的项目,而本地存储是可行的方法。
所以,如果你在互联网上寻找可用的资源,你最接近实现这一点的方法就是遵循迪安的例子。但是,这里他只是简单地使用了函数write.csv()
并在函数的 path 参数中提供了本地存储路径名。这仍然让我们弄清楚如何将 PDF 文件“写入”到本地存储中的特定位置(www 文件夹)。
我在网上搜索处理 pdf 的 R 包,偶然发现了包“pdf tools”和qpdf。因为“qpdf”是“ pdftools ”包的一个要求,所以我决定只安装“ pdftools ”包。不幸的是,这个函数也没有为我们提供在给定的文件路径上“写 PDF”的功能。提供的功能“【pdf tools】如下。**
“pdf tools”R 软件包的描述
【qpdf】包提供的功能如下。
“qpdf”R 包的描述
在这些函数中,引起我注意的是函数pdf_subset()
,因为它有一个名为 输出 的参数。因此,这可能意味着通过输出参数将特定 PDF 文件的子集存储在指定的文件路径中。“ 页面 ”参数是使用上述pdf_info()
函数(pdf_info$pages
更具体)指定的。
瞧啊。!!我的这个小实验成功了,我获得了我想要的功能。下面是我实际部署的应用程序的代码片段,突出了我如何在我的应用程序中管理上传部分。
https://gist . github . com/Hindu bale/5356 a 228372 B3 EC 8 Fe 08 ea 04 c 1802 b 71
**output$upload_questionSet_button <- renderUI({ validate(
need(input$display_name_ques != "", "Please enter the display name of the uploader")
)
fileInput( inputId = "multi_sol_id", label = "Please upload the question set available with you :) (PDFs only)", multiple = TRUE, accept = c(".pdf"), buttonLabel = "Upload Papers")
})
observeEvent(input$multi_sol_id, { fileName_set <- sprintf("www/%s/questionSet_%s_%s.pdf", input$sem_select_mul,input$exam_select_mul, input$display_name_ques) pages_set <- pdf_info(input$multi_sol_id$datapath)$pages pdf_subset(input$multi_sol_id$datapath,
pages = 1:pages_set,
output = fileName_set)
})**
现在,在文件被成功上传后,我需要一种方法来使用 R Shiny 从我的本地存储中下载资源。为此,我四处寻找,阅读官方文件,但毫无结果。在鼓捣了所有可用的选项并如饥似渴地阅读了互联网上的相关资料后,我从这个堆栈溢出答案中得到了一点提示。因此,我使用了file.copy()
函数,并将“ 文件名 ”和“ 内容 ”参数转换为函数来完成我的任务。
下面是我实际部署的应用程序的代码片段,展示了我如何处理应用程序的下载部分。
https://gist . github . com/Hindu bale/eddf 858962865 f 43 F4 AC 152116 DC 84 e 3
**output$download_single_qp_button <- renderUI({
downloadBttn(
outputId = "qp_down_sin",
label = "Download Question Paper :)",
style = "float",
color = "warning",
size = "lg",
no_outline = FALSE)
})
output$qp_down_sin <- downloadHandler(
filename <- function(){
search_ques_fileName <- sprintf("www/%s/%s",
input$sem_select_solution,
input$avail_paper)
return (search_ques_fileName)
},
content <- function(file) {
file.copy(filename(), file)
},
contentType = "application/pdf"
)**
结论:
因此,我们实际上已经能够完成我们计划要做的事情。当你解决一个问题时,它确实是令人兴奋的,不是吗!!
布鲁克·卡吉尔在 Unsplash 上拍摄的照片
当我解决这个问题的时候,我真的很想打我的屏幕很多次,因此我写了这篇文章,这样它可以挽救一个宝贵的生命,或者至少是一个宝贵的显示器😜
这里需要注意的一点是,在编写本文时,https://www.shinyapps.io/的没有提供本地存储的功能(至少在他们的空闲层中没有)。所以,如果你想获得本地存储的好处,你必须在你选择的云服务提供商上建立你自己的 R 服务器。
你也可以请我喝杯咖啡来支持我的工作。
谢谢你,祝你成功。
个人数据所有权:经济考虑
任何对避免冲突感兴趣的社会都需要一个清晰的财产规则体系。
我们之前的媒体帖子描述了个人可以声称对其个人数据拥有所有权的情况。特别是,我们认为个人数据云中的数据应该被视为私有财产。概括来说,以下是我们从那篇文章中获得的主要内容:
- 这些数据可以手动添加到个人数据云中(直接上传个人文档的个人副本),或者通过连接各种数据源,如可穿戴设备、在线账户、物联网设备等。(目前,根据欧盟的 GDPR 和加利福尼亚州的 CCPA,个人有权要求企业提供其客户数据的副本。)
- 一旦这些数据进入个人数据云,就会立即得到处理和“规范化”,使其成为一种独特的资产,比第三方收集的任何数据都更加准确和及时。
- 我们认为,在个人数据云中收集的此类数据应被视为财产法背景下的资产或“东西”(就像您银行账户中的证券或您加密钱包中的加密货币一样)。
这篇文章中的观点在发表于《哈佛法律与技术文摘杂志》(2021) 这里 的法律评论文章中有进一步的解释。
Frepik.com
缩小可以拥有的数据的范围
如前所述,个人数据所有权仅适用于个人在其个人数据云中保存的数据。在我们之前的文章中,我们也强调了个人声称对所有存在的个人数据拥有所有权是不合理的。由于不清楚什么数据应被视为“个人”数据,这种宽泛的主张是不合理的。
考虑到数据有很多层,这一点尤其正确。这种数据层可以根据(I)对个人私生活的敏感程度或接近程度来区分;(ii)谁拥有它(设备制造商、服务提供商、数据聚合商、政府或个人本身);或(iii)个人是否可以访问该数据或影响该数据的使用方式。
以下是一些属于复杂、模糊类别的数据示例:国家税务机构收集的数据、联邦移民当局收集的个人数据、医疗服务提供商收集的医疗数据、雇主收集的员工数据、拼车服务提供商收集的位置和移动数据、关于性犯罪者名单的公共数据、托管平台用户手动提供的数据的社交网站。诸如此类。
Valto Loikkanen 和 Paulius Jurcys,Prifina。
客户数据的公司所有权
个人数据的收集及其在营销中的使用还有另一个层面。从广告业务的角度来看,寻求与客户建立联系的公司对以下数据层最感兴趣:
- 输入数据:个人正在手动添加到某个平台的所有数据(想想你上传到 Instagram 账号的一张照片);
- 元数据:包含在某些信息片段中的数据。例如,如果你上传一张照片到 Instagram,那张照片也包含 GPS 数据(照片是在何时何地用什么相机拍摄的,等等。);
- 观察到的和可观察到的数据:描述某些客户数据在平台中如何使用的数据(例如,有多少人喜欢你的 Instagram 照片);
- 导出数据:由服务提供商推断的关于用户的信息(例如,偏好、行为模式等。).
正如我们之前的所示,即使法律要求服务提供商提供有关其收集的数据的非常详细的信息(这显然属于上述第 3 类和第 4 类),他们也拒绝这样做。事实上,我们已经测试了几十个服务,并且发现如果用户能够获得一些输入数据的副本,那他们就是幸运的。
从我们早期的研究250 多名利益相关者对 CCPA 法规提案的评论中,我们了解到企业和服务提供商将他们收集的大部分客户数据视为专有数据或商业秘密。
个人数据所有权:法律视角
个人消费者普遍认为,我们每个人都应该拥有自己的数据。将个人数据所有权授予个人的道德理由非常充分。在过去的两个世纪中,一个坚实的哲学学派(由伊曼纽尔·康德完善)提出了一个论点,即个人与他们的个人财产有着独特而密切的联系。这里有一个相关的引用:
在把这个东西变成我自己的时候,我在它上面打上了我自己的印记;谁攻击它,谁就攻击我;打击打击着我,因为我就在其中。财产不过是我的人身延伸到事物的外围。—r·冯·杰林
这种个人联系在个人和他们不断产生的数据之间的关系中更加明显。然而,不管这种道德情感在社会成员中有多强烈,道德理论本身实际上是没有意义的,除非:
(a) 个人消费者拥有帮助他们编辑数据副本和控制数据使用方式的工具;
(b) 对这种所有权涵盖哪些种类的个人数据有明确的界定;和
个人数据所有权的授予有着坚实的经济基础。
关于(a)和(b),建立在以用户为中心、用户持有的数据模型上的技术有助于实现这两个目标。然而,一些消费者数据权利倡导者认为,即使人们可以同意个人原则上应该拥有自己的个人数据,物权法体系也无法处理数据带来的复杂性。他们是正确的吗?
受过良好教育的律师可能会评论说,所有权既包括有形资产,也包括无形资产。这种资产分割的历史根源可以追溯到古典罗马法时代(公元前 250 年),当时罗马人区分了有形的东西和无形的财产,前者是土地、牛和其他物质财产,后者是个人的权利和义务、其他类型的可继承资产和无形财产,如财产权
这种物质和非物质的体系在几乎所有的法律体系中都得到了继承。几乎所有的西方民主国家(除了德国和希腊的一些例外)都一致承认所有权的客体包括有形资产和无形资产。
对于那些需要进一步说明的人,这里有一些无形资产的例子,它们无疑被认为是个人所有权的客体:
- 你银行账户里的钱;
- 电;
- 中介证券;
- 知识产权(如版权、专利、商标、商业外观、商业秘密);
- 应收账款;
- 浮动抵押;
- 比特币和其他加密货币。
不言而喻,数据是一种新型资产,但这并不意味着它不能被视为所有权的对象。事实上,科技巨头已经确立了他们的立场,即他们在服务器上保存的所有数据都构成了专有资产和商业秘密。对于个人数据云中的个人独特数据集来说,也应该如此。
个人数据所有权:经济考虑
几个世纪以来,哲学家和律师试图阐明私有财产的哲学基础,并确立法律赋予个人的财产权。另一方面,经济学家的作用主要集中在解释私有财产的一般效用上。
从经济角度来看,对新型资产设定所有权需要:( a)对是否需要承认所有权提出强有力的理由;( b)澄清所有权保护的是什么,所有权存在哪些例外和限制,以及所有权权利受到侵犯的人可以获得哪些补救。在接下来的章节中,我们将从成本和收益的角度来分析处理个人数据所有权时出现的一些问题。
1.资源稀缺
经济学的主要目标是确定有限资源的最有效配置。这种稀缺性的概念在行动者每天必须做出的复杂经济决策中扮演着至关重要的角色。众所周知,在当前的经济环境下,许多品牌甚至难以理解其客户的基本特征,并正在寻找新的机会与个人消费者建立直接关系。然而,市场的不对称使这项任务变得困难(例如,缺乏工具和直接与消费者互动的高成本等)。
消费者信息的匮乏是消费市场的强大驱动力。在这方面,以用户为中心的用户持有数据模型有助于个人拥有自己的个人数据,并在与各种品牌和服务提供商的互动中激活这些数据,这些品牌和服务提供商渴望获得关于其客户的准确数据。这与脸书时代“要么接受,要么放弃”的情况有很大不同,在那种情况下,个人消费者必须无条件同意服务提供商的使用条款。在以用户为中心、用户持有的数据环境中,个人数据成为长期资产,为个人带来日常价值。
2.垄断
经济学家在评估授予新资产所有权的可能性时可能考虑的第二个问题与垄断问题有关。资产持有人是否可能获得市场垄断,从而对其他利益相关者产生不利影响?这种所有权将如何影响市场竞争?对手头的商品和服务质量会有什么影响?会涨价吗?最后,会不会出现增加市场准入难度的人为壁垒?
当前的数据生态系统由四大科技巨头主导:谷歌、亚马逊、脸书和苹果(还有更多大型参与者,但这些是最大和最知名的)。事实是,这四大技术巨头已经主宰了数据市场。他们在各种垂直行业的地位以及商业实践导致了欧盟和加利福尼亚州监管机构的干预,他们在各自的管辖范围内采用了 GDPR 和 CCPA。
GDPR 和 CCPA 赋予个人某些数据权利,如知情权、访问权、删除权、选择不出售数据的权利以及数据便携性权利。需要强调的是,GDPR 和 CCPA 都是公法(即立法)干预者,它们的主要目标是调整市场参与者之间的平衡(具体来说,是数据处理者和数据被处理的个人)。因此,GDPR 和 CCPA 不应与诸如财产权等私法文书相混淆,因为这些文书服务于非常不同的目的。
从经济学的角度来看,私法制度——如所有权——有两个主要目标:(a)界定个人与他们所拥有的财产之间的关系,( b)防止他人干预个人财产。作为资产的所有者,个人可以自由选择如何使用资产。所有民主制度都授予资产所有者四种“传统”权利:占有权、使用权、处置权和从资产中获取收益的权利(这些权利在罗马财产法的传统时期就已经存在)。
因此,作为个人持有数据的所有者,个人可以就如何利用其财产做出独立选择;他们可以决定第三方可以访问他们的数据的条件,并在他们认为合适的时候撤销权限。
3.创新的影响
从经济学的角度来看,研究授予新的产权将如何影响创新也很重要。为了给授予用户持有数据的所有权提供经济上的理由,我们需要研究价值是如何产生的。
传统的经济模式是基于垂直价值链的,其中价值是在分配的每一步增加的。然而,在以用户为中心的数据经济中,大多数决策都基于数据和人工智能工具,这些工具有助于实现流程自动化,并降低交互每一步的交易成本。在以用户为中心的数据经济中,交互的基础是为个人提供定制的个性化价值,同时更彻底地保护他们的隐私。与当前的生态系统相比,以用户为中心的环境也将有更少的进入壁垒,因为应用程序可以直接在用户持有的数据上运行。
以用户为中心、用户持有的数据模型为数据市场中的所有利益相关者带来了新的机遇:个人终于能够行使自主权并利用工具来控制个人数据的使用,而公司则有了直接与客户互动并提供新型定制产品和服务的新机遇。
此外,整个社会都受益于以用户为中心、用户持有的数据模型,因为它促进了新技术的实施,如边缘计算、机器学习、人工智能的个性化、差异隐私等。在社会公平方面,个人将知道他们的个人数据被安全地存储并由第三方负责任地使用,这增加了他们的授权和安全感。
4.保护范围
我们已经表明,在以用户为中心、用户持有的数据环境中,数据所有权仅扩展到个人可以主张事实排他性的个人数据部分(即,个人在个人数据云中持有的个人数据)。
一般来说,如果无法确定对某一物品/资产的事实上的排他性,则不应授予个人所有权。
从交易成本和可用补救措施的角度来看,授予财产权很重要。作为资产的所有者,个人或实体可以决定如何从资产中产生最大价值。财产法也给予物主最强有力的法律保护:物主可以寻求保护,对抗任何威胁要干预物品的第三方。(这不适用于合同义务的情况,在这种情况下,法律补救办法通常只在缔约各方之间存在)。
因此,财产权导致对合法所有者的更大保护,同时也有助于社会中更大的法律确定性。
在这篇文章中,我们只研究了个人数据所有权的一个狭窄部分:个人在其个人数据云中持有的数据的所有权。对于非用户持有的数据的使用,如防止新冠肺炎病毒传播的接触者追踪,显然需要进一步辩论。
向前迈进一步
我们可以通过查看特定的数据段来进一步推进关于数据的辩论,并确定它们是否可以归属于以下所有权类别之一:
Freepik.com
- 受个人财产 规则约束的数据:数据的使用由特定个人决定;
- 受集体财产 : 规则约束的数据,其中数据的使用由特定群体或社区整体决定;和
- 受公共财产系统规则约束的数据:数据受规则约束,这些规则旨在使数据可供社会的任何和所有成员使用。界定财产所有权和分配的法律是合法期望的基础。就社会效用而言,承认用户持有数据的产权在多个方面都是有利的。一旦个人能够使用帮助他们控制个人数据的工具,数据的巨大价值将变得更加明显。因此,个人会变得更加依恋他们的数据。从本质上讲,明确的权利分配将为社会带来更多的确定性,一旦这些权利得到明确界定,对个人数据的尊重就会增加。
- 数据的实际价值可以通过多种方式获得。我们 Prifina 一直专注于开发人员主导的合作,开发人员可以根据用户持有的综合数据构建直接面向消费者的应用程序。你可以了解更多关于我们在 dev.prifina.com 的工作。
个人数据所有权
用户持有数据的个人所有权的法律、经济和政治原因。
“数据”和“隐私”是当今使用最频繁的流行语。接触追踪、监控和数据使用一直是决策者、经济学家和消费者权益倡导者争论的话题。
最突出的争论之一围绕着回答一个关键问题:谁应该拥有个人数据和由手机和智能设备产生的数据?我们中的绝大多数人都会同意,个人应该是他们生成的数据的所有者。
这篇文章中的观点在哈佛法律杂志&技术文摘(2021 年秋季)上发表的一篇法律评论文章中有更详细的解释——可在这里访问。
当前的方法:关注获取,而不是所有权
这可能会让您感到惊讶,但个人数据所有权(赋予个人拥有和控制其个人数据的权利)尚未在任何司法管辖区得到认可。法学界也不承认任何此类个人数据所有权。事实上,最受尊敬的法律思想家和智库认为,到目前为止,“没有任何法律原则或理论可以证明数据的专有财产权的分配是合理的。”
法律专家的主流观点是,我们不应该谈论所有权,而应该关注对数据的访问。这种方法在最近发布的欧洲数据 2020 战略和欧洲人工智能白皮书中也受到了青睐。因此,许多计划是围绕各种组织(公司、研究机构、政府机构等)之间的数据可移植性和互操作性而建立的。).
新兴技术正在改变关于个人数据所有权的争论
我们生活在一个有趣的时代,新技术正在改变我们生活的方方面面。这些新技术的出现为个人消费者(不仅仅是公司)控制他们的数据以及管理和控制这些数据的使用方式带来了新的机遇。
我们认为,必须根据这些新技术重新考虑个人数据所有权的问题,以及个人消费者是否应该对其个人数据拥有合法所有权。
更具体地说,我们认为,一旦接触到帮助个人在一个地点收集个人数据(姑且称之为“个人数据云”)并控制数据使用方式的技术解决方案,许多拒绝个人数据所有权想法的人将会改变主意。
基于以用户为中心的数据模型的技术
目前,我们正在见证基于所谓的“以用户为中心”的数据模型的新技术解决方案的出现。以用户为中心的数据模型的主要前提是,个人在“个人数据云中”持有自己数据的主副本(可以把它想象成一个存放所有数据的 Dropbox 文件夹)。每个个人数据云都由组织数据的软件支持。
个人数据云中保存的所有数据来自两个来源之一。首先,一些数据是由个人手动添加的,比如上传的驾照、财务报表和每月账单的副本。第二,一些数据是从第三方数据聚合器或数据处理器收集的。这一过程是完全自动的,可以由“生活”在个人个人数据云中的软件来执行。从第三方服务提供商处检索个人数据是可能的,因为法律法规要求数据聚合器和数据处理器向个人提供公司收集的关于他们的所有数据的副本,如 GDPR 第 15(3)条“获得数据副本的权利”和 GDPR 第 20 条“数据便携性权利”所规定。
保利乌斯·尤尔西斯和金京培
通过能够在一个地方收集不同数据源的个人数据,个人实际上获得了对其数据的控制权。就像任何其他在线帐户一样,只有个人用户可以访问她的个人数据云(第三方只有在用户事先明确同意的情况下才能访问某些用户持有的数据)。此外,个人数据云中保存的数据会由嵌入个人云中的软件不断更新。收集在个人数据云中的数据也是“标准化的”,使用户易于阅读。
采用以用户为中心、用户持有的数据模型有三个重要的影响。
(a)所有权和控制权
首先,在以用户为中心的模式中,个人对如何使用数据以及与谁共享数据拥有最终控制权。这在法律上和实践中都适用。
(b)在客户事先明确同意的情况下访问客户数据
第二,公司将能够直接从客户那里获得有关他们客户的信息。这将通过每个人事先表达的同意来授予,并将允许个人决定什么数据用于什么目的(想想数字水印)。
(c)有数据的新机会
第三,以用户为中心、用户持有的数据模型为新型数据用途打开了新市场。将创建一种全新的应用程序,以帮助个人更好地了解他们的个人数据(“数据小工具”)并“激活”个人数据(例如,基于从网飞、Hulu、亚马逊 Prime video 和 IMDB 收集的数据生成的电影推荐)。从技术角度来看,最吸引人的方面是这些新的数据应用程序将在用户持有的数据之上本地运行(数据永远不会离开用户的云或设备,即使在应用程序中使用)。
在接下来的部分中,我们将讨论与授予个人消费者对个人数据的合法所有权相关的问题。
(1)您拥有哪些数据?
在关于数据所有权的法律辩论中,定义可能属于个人的个人数据一直是一个历史性的问题。更具体地说,许多辩论都集中在什么样的数据是“个人”的,什么样的数据数据巨头可以声称是自己的。另一个挑战是,大量数据实际上掌握在服务提供商(谷歌、脸书等)手中。).
以用户为中心、用户持有的数据技术从非常实用的角度解决了这一难题。一旦数据“进入”个人数据云,它在物理上就处于个人的专有控制之下,因为除了个人之外,没有人可以访问云。
关于这个问题:个人应该拥有哪些数据?
简而言之就是: 个人数据云中的所有数据。
一旦有关个人的数据被收集到个人数据云中,这些数据就会自动“规范化”(即统一和分类),从而成为一个新的对象,具有自己独特的结构和布局。
然而,所有权问题的更长答案需要进一步澄清。如果我们分析科技公司的数据收集做法,可以识别出四层数据:(a)输入数据,由用户手动提供;元数据;(c)可观察和观察到的数据;以及(d)导出数据。
让我们以 Instagram 为例:如果你上传一张照片到你的个人 Instagram 账户,Instagram 能够生成四层数据:
- ****输入数据:个人手动添加的所有数据(例如,照片本身、照片说明、评论、标签等。);
- ****照片中的元数据:附在照片上的 GPS 数据,以及关于相机、分辨率和照片附带的其他信息的详细信息。
- ****观察到的数据:关于照片如何被使用的数据。这个数据既不是 Instagram 创造的,也不是个人创造的。同样清楚的是,个人无法访问它。
- ****衍生数据:基于上面提到的所有这三类数据,Instagram 使用推理来生成用户的信息档案(例如,偏好、行为模式、关于个人的数百种见解等)。).
丹尼尔·阿里和保罗·尤尔西斯,普里菲纳
目前,技术巨头声称他们合法拥有观察到的和衍生的数据(上面的第 3 类和第 4 类)。
然而,在以用户为中心的模式中,( a)个人能够通过行使其 GDPR 和 CCPA 权利从第三方服务提供商检索的数据,以及(b)存储在仅个人可访问的个人数据云中的数据,应被视为在个人的实际拥有和控制范围内。此外,由于数据是以独特的方式排列的,因此有可能声称个人也应被视为其合法的所有者。
(2)个人拥有哪些权利?
法律学者一再质疑个人对其数据的权利范围。产权制度——版权或其他知识产权、商业秘密或新型的产权制度(所谓的特殊权利等等。—个人数据应归入哪个类别也受到了广泛讨论。似乎大多数作者都同意个人数据不属于任何一类。
实际拥有和控制是将个人数据定性为个人可以合法拥有的对象所需的核心缺失部分。然而,随着新的以用户为中心、用户持有的数据解决方案的出现,没有人反对在物权法中将此类数据归类为个人所有的物品。
诚然,每个国家的财产制度都不同,每个国家都有不同的要求,需要满足这些要求才能将一个物体视为财产法的“物”。例如,在英国普通法中,从严格的财产法角度来看,必须满足四个主要要求才能将一个物体视为"物":确定性、排他性、控制和可转让性(根据 Mummery LJ 在案件fair star【2013】EWCA Civ 886(2013 年 7 月 19 日)中的意见)。
保存在用户个人数据云中的数据满足所有这些要求:(a)可以确定地识别这些数据,(b)个人有权禁止他人访问这些数据,个人显然可以控制这些数据,以及(d)个人可以将这些数据分配或传输给任何第三方(如果她愿意)。
承认个人数据的所有权意味着个人拥有以下四项与所有权相关的“传统”权利:拥有个人数据的权利;按照自己的意愿使用和享受这些数据的权利;处置数据的权利;以及从个人数据中获取价值的权利。
个人数据所有权是常识
个人应该拥有自己的个人数据是常识。大多数人会同意他们应该拥有自己的个人数据。
这一观点与数据技术公司(GAFA)对其收集的数据所持的观点一致。如果大公司可以声称他们长期收集的数据形成了专有资产和商业秘密,那么个人也应该能够声称他们对他们在个人数据云中聚集的数据拥有所有权。
最后,个人是个人数据云中个人数据的合法所有者,这一观点绝不需要对现有法律框架进行变革。相反,它应该被看作是由数字空间的发展引起的自然演变。
实际影响
承认个人拥有自己的个人数据具有重要的社会、法律、经济和技术意义。
普里菲娜·保利乌斯·尤尔西斯
首先,用户持有的数据在技术上更优越:如前所述,用户持有的数据代表了关于个人的最准确的数据。这是最新的数据。没有任何其他组织比个人本身拥有更准确和可靠的数据。
第二,用户持有的数据在法律上是优先的。承认个人对数据的所有权解决了企业目前面临的许多问题。在以用户为中心的模式中,个人在事先明确同意的情况下,允许第三方服务提供商访问他们的数据。因此,组织将能够遵守数据最小化要求,并合法地访问和处理所有数据。
第三,用户持有的数据为企业创造了在用户持有的数据上构建新型应用的机会。这将使数字互动更加安全:数据泄露的可能性将会下降,因为集中服务器中保存的客户数据将会减少。此外,用户持有的数据将让公司做出更个性化的报价,并向客户提供定制服务。
最后,用户持有的数据具有社会优势。通过创建实用的工具,让个人对其个人数据进行粒度控制,个人将变得更懂数据。此外,与服务提供商的互动将更加平等,因为个人将设定第三方访问数据的条款。这可能有助于改变对个人数据价值的看法;个人数据将不再是一次性物品,而是一种能产生长期价值的资产。
加入运动吧!
如果你有兴趣加入一个以用户为中心的社区,我想邀请你加入" Liberty。平等。数据。“懈怠组。
如果你有关于数据所有权或隐私的其他想法,并希望与我讨论,请评论这篇文章或直接与我联系。
使用 FunkSVD 推荐个性化餐厅
除了推荐顶级餐厅,我们还能做得更好吗?
多伦多的天际线相当惊人——但是食物有多好呢?(图片来自维基共享资源)
2009 年 9 月,网飞授予100 万美元的奖金,奖励一个开发了一种算法的团队,该算法将他们的评级预测提高了 10%以上。排在第三位的是由西蒙·芬克——芬克 SVD 创作的个人作品。
在这篇文章中,我描述了我尝试使用 FunkSVD 根据用户的预览评论推荐餐馆。
我的目标——我能向用户提供更个性化的推荐,而不只是简单地推荐一个城市的顶级餐厅吗?
推荐引擎被世界各地的公司广泛使用:比如 AirBnB、网飞和 Spotify。像这样的公司的成功取决于他们向用户提供准确和中肯的推荐的能力(如果 Spotify 一直推荐你最不喜欢的音乐类型,你多久会回到它那里?))
这些推荐引擎通常从用户输入(例如,用户评级)中学习,但是该领域中的一个关键问题是:
当一个用户只给少数几个项目评分时,你如何预测每个项目的评分?
截图来自 Yelp
为了调查这个问题,我使用 Yelp 数据来查看餐馆评论。以多伦多为例,那里有一千多家餐馆,但是 Yelp 的绝大多数用户给其中不到 10 家打了分。我们如何填补空白——预测他们所有的评论,以便我们可以推荐他们会喜欢的新餐馆?这就是 FunkSVD 的用武之地。
多伦多的餐厅位置
什么是 Funk SVD?
FunkSVD(其中 SVD 代表奇异值分解)是一种填充用户项目评级矩阵的方法。考虑下面不同餐厅的用户评分矩阵(蓝色),最终目标是用预测填充未知评分,以便我们可以将这些预测中最高的作为我们的推荐。思考正在发生的事情的另一种方式是看下面的例子:
大多数喜欢顶级汉堡的用户也喜欢 Wings 'n' Things,但不喜欢 La Parisienne。
如果我们知道凯特喜欢顶级汉堡,她很可能也喜欢鸡翅之类的东西。
我们这样做的方法是通过引入一组被称为潜在因素的中介实体。为了赋予这些内容,我们可以将它们视为用户可能喜欢或不喜欢的餐馆属性——每个潜在因素都是对餐馆进行评级的新尺度。例如,一家餐馆可能或多或少比较贵,或多或少可能使用白色桌布,等等。其中的每一个都可能与用户的评级有关,我们希望了解两件事:
- 每个餐厅与每个潜在因素的关联程度如何?
- 每个用户对每个潜在因素的关心程度如何?
给定这些信息,我们可以使用矩阵乘法来重建完整的用户-餐厅评级矩阵:
用户矩阵(绿色)和餐馆矩阵(橙色)相乘形成用户-餐馆评级矩阵(蓝色)。
因此,我们有一个攻击计划,计算每个餐馆和用户的每个潜在因素的值,然后乘以矩阵,以获得每个用户-餐馆对的预测评级。但是我们该如何着手呢?
这就是 FunkSVD 方法发挥作用的地方。首先,我们猜测。我们用随机数填充两个矩阵。然后我们一个接一个地检查我们已知的等级(蓝色),并根据需要上下移动绿色和橙色的猜测值,以接近真实的数字。我们对每个已知的评级都这样做,并多次迭代该过程,以接近用户和餐馆矩阵的稳定解决方案。这是梯度下降的一个例子。
下面我们展示迭代的一个步骤:
已经用随机值初始化的潜在因子矩阵
我们计算预测的评级(目前来看一点也不好,因为它是随机的)。
我们相应地微调红色突出显示的单元格中的每个值。我们微移的量与误差的梯度有关——也就是说,我们希望在最小化预测值和实际值之间差异的方向上微移它。
这里的左箭头←表示“用右边的值设置左边的值”
一旦这六个值被更新,算法就移动到下一个已知的评级,这里是 Emily 对 Wings‘n’Things 的评级,并重复上述步骤。学习速率 𝛼 决定了算法下山的速度,并且对于允许拟合的及时但受控的收敛是重要的。
一旦我们为每个已知的评级完成了一个完整的循环,我们就重复这个过程,直到与训练数据达到合适的拟合。
谈谈潜在因素
上面,我建议潜在的因素应该是用户和餐馆容易理解的属性。不幸的是,这过于简单化了。实际上,它们可能是一个完全任意的概念,只是碰巧描述了用户和餐馆之间的关系。模型使用的潜在变量的数量由您决定,但是太多的潜在变量将意味着模型过度适应训练数据,这将降低模型根据看不见的数据进行预测的能力。
它做得怎么样?
为了量化模型的表现,我将评论数据分成训练和测试子集。对于这样的评级数据,重要的是对时间顺序数据进行分割(这样模型就不会在已经从未来学习的情况下预测过去的评级)。为了实现这一点,2014-2018 年的评论被用作训练集,而 2019 年的评论被用作测试集。
下面,我绘制了预测值与实际值的混淆矩阵,以及它们在模型中的代表性分布(一个完美的模型将显示一个只有对角线条目的混淆矩阵)。
我们的模型似乎低估了极端情况下的评分
使用的关键评分指标是 2019 年预测和实际之间的误差平方和(SSE)。为了计算这一点,对于具有相关联的实际值的所有预测(即,具有 2019 年评论的用户-餐馆对),我们计算预测和实际之间的差的平方。这些值的总和就是我们的 SSE。
这个度量标准可以很好地比较不同版本的模型,但实际上只是一个相对的度量。我们真正想知道的是——模型需要好到什么程度才能有用?
为了回答这个问题,我比较了模型中的 SSE 和通过简单地向每个用户推荐城市中的顶级餐馆计算的 SSE(或者更准确地说,每个用户对餐馆的预测评级只是餐馆的平均历史评级,没有考虑用户与餐馆的互动)。
在对模型进行了一点优化之后,通过调整潜在因素的数量、学习率和迭代次数,我得到了一个比天真地向每个用户推荐顶级点评餐厅(SSE 低 4)略好的模型。
推荐器的输出是一个推荐餐馆的地图,上面有每个餐馆的预测评级和一个作为工具提示的 google 搜索链接。通过运行下面的代码笔,您可以看到一个示例建议。
对于那些没有做出足够数量的评论以从 FunkSVD 方法中受益的用户,推荐者依靠推荐城市中的顶级餐馆。它还允许最终用户进入某个餐馆,并接收对相似餐馆的推荐(由相似用户的相似评级定义)。
最后的想法
这种对 FunkSVD 餐厅推荐的初步尝试表明,不需要太多的优化,就可以预测出比预测每个餐厅平均值的简单方法更好的评级。
对于进一步的功能,允许最终用户通过其他上下文因素过滤结果将是有用的(因此也建立了基于知识的方法)。两个显而易见的因素是餐馆的位置(“帮我找两英里内我喜欢的餐馆”)和菜肴(“帮我找我喜欢的中国餐馆”)。
最后一个大声喊出来的是 Numba ,通过使用这个 python 库,对我现有的函数进行有限的修改,我能够将模型拟合的执行速度提高超过 400 倍的!如果你有正在运行的主要使用 numpy 核心函数的函数/循环,检查一下,看看它能让你的代码加速多少。
[## Paul stub ley-LinkedIn 自由职业数据和决策科学家
我是一名经验丰富的数据科学家,在个人工作和管理团队方面都有卓有成效的工作经历。
www.linkedin.com](https://www.linkedin.com/in/paul-stubley-32b134100/)
如果你想联系我,你可以在 LinkedIn 上联系我。如果你想看代码和分析,可以在GitHub上查看。使用的数据是 Yelp 评论数据 。
个性化您的 Python 通知声音与谐音
创建一个听觉通知系统来通知你正在运行的代码
瑞安·昆塔尔在 Unsplash 上拍摄的照片
我很确定当你在写代码的时候;有些时候你平行做。有一段时间,许多项目正在进行,你需要运行一个很长的脚本,你需要等待。
以前,我写过如何在你完成运行机器学习模型或任何脚本时通过电子邮件获得通知。
使用 Python 自动化您的通知系统
towardsdatascience.com](/automatic-notification-to-email-with-python-810fd357d89c)
虽然,有时你根本没有检查电子邮件,或者只是因为太忙于你的笔记本电脑,但当建模或编码脚本完成时,你真的需要通知。
在这种情况下,我之前发现了一个有趣的开源软件,由 Max Halford 开发,名为 Chime ,当你运行完所有代码时(或者当你遇到错误时),它会用声音通知你。
让我们试试这个!
和谐
首先,让我们安装所需的软件包。chime 包不依赖于任何依赖项,所以它会破坏您现有的任何包。
pip install chime
当你完成了软件包的安装,我们就可以开始使用提示音软件包得到通知了。
让我们从测试 chime 的基本功能开始。
#importing chime package
import chime#Successfully running the code sounds
chime.success()
当您运行上面的代码时,应该会有一个通知声音响起。这是一种特定的声音,当您成功运行代码或任何进程时会发出这种声音。
我们还可以通过运行以下代码来测试错误声音。
chime.error()
声音应该与成功的声音不同。
请注意,声音是在异步进程中播放的,因此是非阻塞的。无论声音长度如何,每个声音在编码处理后都需要大约 2 毫秒才能发出。
你可以尝试测试的另一个功能是警告功能和信息功能,它们也会发出不同的声音。
#Warning notification sound
chime.warning()#Information notification sound
chime.info()
与 Jupyter 整合的谐音
好吧,你可以在你的脚本中包含谐音函数来通知你觉得需要通知的每个进程。尽管如此,如果你需要在每个 Jupyter 笔记本单元格中通知,我们需要做一些准备。
首先,我们需要通过运行下面的代码来加载扩展。
%load_ext chime
之后,我们准备在运行的每个 Jupyter 单元中获得声音通知。让我们尝试一个简单的方法,在 Jupyter 单元格中包装一行代码。
#Wrapping a single line in jupyter cell
%chime print("Sounds out")
如果您想要包装整个 Jupyter 单元,您需要以不同的方式运行它,就像下面的代码一样。
%%chime
print("Whole cell")
当您成功运行单元时,它将产生来自chime.success()
的声音,否则它将使用chime.error()
声音。
改变声音主题
您可以通过设置可用的钟声主题来更改钟声通知声音主题。要检查哪个主题可用,您可以运行以下代码。
chime.themes()
目前只有三个主题可用;《钟声》、《马里奥》和《塞尔达》。要更改主题,只需运行以下代码。
#Change the notification sound with Zelda theme
chime.theme('zelda')
这将把主题声音改为塞尔达通知主题声音。如果你想每次都有不同的声音,你可以把主题改为“随机”。
如果你想有自己的声音通知,你需要提出一个新的主题,通过打开一个 pull 请求来添加必要的。wav 文件到[themes](https://github.com/MaxHalford/chime/tree/main/themes)
目录下。对于一个主题,它们由四个文件组成:success.wav
、warning.wav
、error.wav
和info.wav
。
结论
Chime 是一个开源包,当您成功运行代码时(或者出现错误时),它会发出声音通知您。
当你想添加自己声音的新主题时,需要先拉请求,提出自己的声音。
希望有帮助!
如果您喜欢我的内容,并希望获得更多关于数据或数据科学家日常生活的深入知识,请考虑在此订阅我的简讯。
如果您没有订阅为中等会员,请考虑通过我的推荐订阅。
深层神经网络(DNN)训练中的扰动理论
消失梯度,鞍点,对抗训练
图 1:扰动可以帮助接近正确吸引点(来源)。
先决条件——这篇文章假设读者对神经网络架构有初步的了解,并训练过某种形式的深度网络,在此期间可能会面临一些与训练或模型鲁棒性相关的问题。
与训练相关的各种参数/组件(如梯度、重量、输入等)的小扰动或微移。可以影响 DNN 训练以克服可能遇到的一些问题,例如,消失梯度问题,鞍点陷阱,或者通过对抗训练创建健壮模型以避免恶意攻击等。
通常, 微扰理论 是研究一个系统中的微小变化,这种变化可能是由于第三个物体与该系统相互作用的结果。例如,天体(行星、月亮等)的运动。)太阳周围的物体受到其他行星/卫星的影响,尽管太阳的质量几乎是太阳系的 99.8%。几乎与此类似,在 DNN 训练中,其组件(梯度、权重、输入)中的小扰动被用于解决在训练过程中或从训练模型中可能遇到的一些问题。
免责声明:需要非常明确的是,深度学习/机器学习中没有正式的扰动理论。然而,术语“扰动”的使用在机器学习文献中并不陌生。这经常被用来描述一个主体的包含部分的微移。这个博客是文学中扰动相关技术积累的结果。
消失梯度
神经网络只是一种近似函数的方法,它接受输入并产生输出。下面的最终训练的黑盒模型实际上是许多函数(f(g(x))的组合,即每个隐藏层代表组合系列中的一个函数,接着是非线性激活函数的组合,然后是隐藏层,等等;非线性激活函数是为了引入非线性,否则,它只是一系列矩阵乘法,可以简化为只能模拟线性函数的单个矩阵,对于该单个矩阵,仅仅一层就足够了。
神经网络参数(权重和偏差)用某一组初始值初始化。然后根据训练数据对这些进行更新。使用下降方向上的梯度来执行更新以找到最小值(实际上是局部最小值,因为函数是非凸的,并且不能保证达到全局最小值),这在实践中不管网络的深度或复杂性如何都有效。
为了计算梯度,我们从最后一层的参数开始,然后向后传播(一个解决的说明反向传播)到第一层的参数。当我们从最后一层移动到初始层时,每一层参数的梯度计算增加了乘法项的数量(函数组合的梯度)。许多项的这种相乘可以导致初始层的梯度消失(随着项的数量越来越深,项越来越多),并且如果这些项的值在[0,1]的范围内,这是激活函数的变化率(即梯度,暂时忽略隐藏层的贡献,因为它们可能在该范围内,也可能不在该范围内),例如,sigmoid (0,1/4),双曲线正切(0,1)等。换句话说,在消失梯度中,最后一层的参数将学习得很好,但当我们向初始层移动时,梯度可能会开始消失或足够低,从而显著增加训练时间。为了避免这种情况,可以使用 ReLU 或其变体或批量标准化,但这里我们将讨论梯度中的一点扰动(来自论文 Neelakantan et al. (2015) )也可以帮助缓解问题。
梯度中的扰动是通过添加具有零均值的高斯分布噪声,并且衰减方差- 比固定方差更好地工作,此外,即使当成本函数 (J)值接近收敛时,我们也不想要恒定扰动。这个过程也有助于避免过度拟合,并可以进一步降低训练损失。每个训练步骤 t 的扰动梯度计算如下:
其中训练步骤 t 的衰减方差(σ)为:
其中η (eta)的值通常取为 1(尽管可以调整,但必须在 0 和 1 之间),γ参数设置为 0.55。
另外,这一过程实际上增加了培训过程的随机性,有助于避免早期学习过程中的平稳期。
鞍点
它是曲线上的一个固定点,其形状是马鞍的形状(如图 2 所示的骑马)。例如,在损失函数最小化的曲线中,如果当其中一个轴固定时驻点是局部最小值,而当另一个轴固定时驻点是最大值,则驻点是鞍点(不失一般性,为了解释的目的,我们将考虑三维曲线)。从鞍点开始,沿着一个轴的移动函数值增加,而在另一个轴上函数值减少,然而,对于一个点,最小值应该在它移动的所有方向上增加,而最大值在所有方向上减少。
图 2:马鞍形曲线(来源
有时,训练过程会停滞在鞍点,因为梯度评估为零,这导致权重参数(w)没有更新。为了有效地逃离鞍点可以扰动权重。权重 的扰动 取决于权重的梯度,例如当梯度的 L2 范数小于某个恒定值 c 时,则应用扰动。扰动由下式给出:
其中 w t 是训练的第 t 次迭代的权重,并且 ξ t 是从以零为中心的具有适当小半径的球上均匀采样的。然而,没有必要添加均匀噪声,也可以使用高斯噪声,然而,这在经验上没有任何额外的优势。为了便于分析,使用了均匀噪声。同样,仅在梯度较小时添加噪声既不必要也不必要;由我们来决定何时以及如何扰动权重,例如,间歇扰动(每隔几个迭代,没有条件)也将工作,它具有多项式时间保证。
对抗训练
字典将对手定义为a force that opposes or attacks; an opponent; an enemy; a foe.
类似地,深度学习模型的一个对抗性例子将是恶意、垃圾或有毒的输入,它可以通过高度自信地预测不正确的输出来欺骗它发生故障。
为了使神经网络模型对这些对立的例子具有鲁棒性 Goodfellow 等人提出了 对输入 的扰动。扰动产生多个输入,这些输入是通过将具有符号值为的梯度(相对于训练输入计算)乘以常数值的输入相加而获得的。扰动图像的创建被给出为:
其中 x 是训练输入,xˇ是新的扰动图像,∇x(J)是损失函数(j)相对于训练输入 x 的梯度, ϵ 是预定的常数值,由于其有限的精度,小到足以被数据存储设备丢弃,并且如果输入为正,则符号函数输出 1,如果输入为负,则输出 1,如果输入为零,则输出 0。这项技术最近在美国专利法下获得了专利,你可以在这个链接中详细阅读。我强烈推荐它的阅读,以便更好地澄清,也作为一个软件算法专利是如何撰写的例子。
用扰动的输入训练网络扩展了输入分布的多元性。这使得网络对恶意输入具有鲁棒性,例如,它可以帮助避免图像分类任务中的像素攻击。
结论
我们已经了解了扰动如何帮助解决与神经网络训练或训练模型相关的各种问题。这里,我们已经看到了与神经网络训练和训练模型相关的三个分量(梯度、权重、输入)的扰动;扰动,在梯度中是为了解决消失梯度问题,在权重中是为了避开鞍点,在输入中是为了避免恶意攻击。总的来说,不同方式的扰动起到了加强模型对抗各种不稳定性的作用,例如,它可以避免停留在正确性破坏点(图 1 ),因为该位置将通过扰动(输入、权重、梯度)进行测试,这将使模型接近正确性吸引点。
到目前为止,扰动主要是偶然的经验实验设计的直觉,以解决遇到的问题。如果干扰训练过程的一个组成部分直观上是有意义的,我们需要进行实验,并进一步根据经验验证它是否有助于缓解问题。然而,在未来,我们将在深度学习或机器学习中看到更多的扰动理论,这也可能得到理论保证的支持。
参考文献
1.加入梯度噪音可以改善深度网络的学习。arXiv 预印本 arXiv:1511.06807 (2015)。
[2].金,迟,等。如何有效地避开鞍点。第 34 届国际机器学习会议录第 70 卷。JMLR。org,2017。
3.古德菲勒、伊恩·j、黄邦贤·史伦斯和克里斯蒂安·塞格迪。"解释和利用对立的例子."arXiv 预印本 arXiv:1412.6572 (2014)。
岩石物理学:Python 中的伽马射线归一化
麦克斯韦·尼尔森在 T2 的照片
实践教程
测井数据的标准化是岩石物理工作流程中常见的常规过程,用于校正井间测井曲线的变化。这些变化可能是由许多不同的原因引起的,如不正确的工具校准、不同的工具年份和井眼环境条件的变化。
在本文中,我们将回顾:
- 什么是正常化
- 为什么我们要标准化测井数据
- 我们如何实现标准化
- Python 中规范化的示例
什么是正常化?
标准化是重新标定或重新校准测井曲线的过程,以便它们与油田或区域内其他井的其他测井曲线一致。这可以通过对所需曲线应用单点标准化(线性移位)或两点标准化(“拉伸和挤压”)来实现。
标准化通常用于伽马射线测井,但也可用于中子孔隙度、体积密度、声波和自然电位测井。除非有足够的理由,否则电阻率测井一般不进行标准化(Shier,2004)。
在进行标准化之前,您应该与您的地层学家/地质学家进行讨论,并对您的数据进行彻底的质量控制,以了解是否有必要。盲目地对数据应用归一化会导致地质变化和要素从数据中移除。所以要慎重考虑。Shier (2004)就如何对测井数据进行标准化提供了很好的讨论和指南,非常值得回顾。
为什么我们要标准化我们的测井数据?
在一个区域内,通常假设相似的地质和地层单元应显示相似的最小和最大测井值以及相似的测井响应。然而,测井响应可能不同于测井井之间的预期模式有多种原因。这些可以包括:
- 钻孔环境的差异,如岩性变化
- 井眼形状的差异会影响测量,例如,井眼的扩大部分会有更多的井内流体,这会导致伽马射线测井的衰减,进而导致数值的降低
- 不正确地应用工具校准
- 测井工具技术和传感器随时间的变化
- 岩石的成岩变化
- 工具响应的漂移
- 来自多家服务公司的不同工具
执行标准化使我们能够在多口井之间进行有用的比较。这也使得批处理更加有效,尤其是在选择关键解释参数时。
此外,即使数据之前已经过井眼校正,也可以对数据进行归一化处理(Shier,2004)。这在处理不知道应用了何种校正的数据集时非常有用,尤其是在较旧的数据集中。
我们如何标准化测井数据?
标准化的工作流程通常包括在区域或油田内选择一口关键井,从中选择关键参数,并重新调整其他井的比例以进行匹配。
使用下面的等式,通过对测井数据进行简单的线性移位,可以实现标准化。这在我们知道数据有固定变化的情况下很有用,例如在不同岩性单元中记录中子孔隙度(Crain's Petrophysics,2020)。
更常见的是应用两点移位,由此数据被拉伸和压缩以匹配参考井。这经常应用于伽马射线数据。计算方法如下:
关键参数(从参考/关键点池中选择)可以是最小值或最大值。更常见的是,它们是从第 5 和第 95 百分位等百分位获得的值。如果使用最小值和最大值,您需要警惕异常值和异常读数。例如,如果参考井中的伽马射线读取 600 API 的单个值,并且它被用作参考最大值,那么它将延伸出被标准化到该范围的其他井。
使用 Python 的规范化示例
以下代码演练展示了一种使用 pandas 数据框架和 Volve 数据集(Equinor,2018)中的三口井标准化测井数据的方法。
您可以在 GitHub 上我的岩石物理学和 Python 系列的 8 —曲线归一化. ipynb 中找到这个例子的完整 Jupyter 笔记本,链接如下:
[## andymcdgeo/岩石物理学-Python-系列
本系列 Jupyter 笔记本将带您了解使用 Python 和岩石物理数据的各个方面。
github.com](https://github.com/andymcdgeo/Petrophysics-Python-Series/)
设置库和加载数据
第一步是导入我们计划使用的所需库。在这种情况下,将是熊猫和 matplotlib 库。
import pandas as pd
import matplotlib.pyplot as plt
我们可以直接从 CSV 加载数据。在这个例子中,我使用了 Volve 数据集中的 3 个井的子集。
data = pd.read_csv('Data/VolveWells.csv')
然后,我们可以使用几个简单的命令来获取数据的详细信息:
data.head()
这将返回列标题和前 5 行数据:
我们可以立即看到在这个数据集中有哪些曲线,并且空值由 NaN(不是数字)值表示。这很好,意味着我们不需要替换价值观。
要检查数据中有哪些井,我们可以调用unique()
方法:
data['WELL'].unique()
这将返回井名数组,如下所示:
array(['15/9-F-1 C', '15/9-F-4', '15/9-F-7'], dtype=object)
从这一初步探索中,我们可以说我们已经:
- 数据集内的 3 口井:15/9-F-1 C、15/9-F-4 和 15/9-F-7
- 没有必要将任何-999 值替换为空值
- 每口井 10 条测井曲线
绘制原始数据
在我们绘制数据之前,我们需要做的第一步是按照井列对数据框进行分组。这将使绘制柱状图变得更加容易。
wells = data.groupby('WELL')
wells.head()
我们可以看到,如果我们调用wells.head()
,我们会得到一个分组数据帧,显示每个井的前 5 行。
比较分布和选择关键参数的最简单方法之一是使用直方图。在 Python 中,如果我们想看到分布线而不是条形,我们必须调用核密度估计(KDE)图,而不是直方图。使用我们的分组数据框架,我们可以循环通过每个井,将数据添加到图中,并在图例中显示正确的标签:
fig, ax = plt.subplots(figsize=(8,6))
for label, df in wells:
df.GR.plot(kind ='kde', ax=ax, label=label)
plt.xlim(0, 200)
plt.grid(True)
plt.legend()
plt.show()
从上面的图中,我们假设关键井是 15/9-F-7,我们将其他两口井标准化为这口井。
注意,分布似乎偏离了图的左侧,但是可以使用min()
方法确认伽马射线曲线不存在低于 0 的值:
wells.min()
计算百分位数
如上所述,数据集可能包含错误值,这会影响曲线内的最小值和最大值。因此,一些解释者更喜欢将他们的标准化参数建立在百分位数的基础上。在这个例子中,我将使用第 5 和第 95 个百分点。
第一步是通过按井对数据进行分组,然后应用。quantile()方法应用于特定的列。在本例中,gr . quantile 函数接受一个小数值,因此值 0.05 相当于第 5 个百分位,0.95 相当于第 95 个百分位。
gr_percentile_05 = data.groupby('WELL')['GR'].quantile(0.05)
print(gr_percentile_05)
所以现在我们需要把它带回到我们的主数据框架中。我们可以使用.map()
方法来做到这一点,该方法将合并共享一个公共列的两个数据序列。一旦它被映射,我们可以调用.describe()
方法并确认它已经被添加到数据帧中。
data['05_PERC'] = data['WELL'].map(gr_percentile_05)
data.describe()
我们可以看到,我们上面计算的第 5 个百分位数的数据现在已经添加到数据帧的末尾。我们现在可以对第 95 百分位重复上述过程:
gr_percentile_95 = data.groupby('WELL')['GR'].quantile(0.95)
data['95_PERC'] = data['WELL'].map(gr_percentile_95)
data.describe()
创建标准化函数
在规范化我们的数据之前,我们必须首先创建一个可以多次调用的函数。提醒一下,我们使用的函数如下(Shier,2004):
def normalise(curve, ref_low, ref_high, well_low, well_high):
return ref_low + ((ref_high - ref_low) * ((curve - well_low) / (well_high - well_low)))
使用上一步中计算的百分位数,我们可以设置我们的参考高值和低值:
key_well_low = 25.6464
key_well_high = 110.5413
为了将函数应用于每个值并为每个孔使用正确的百分位数,我们可以对 pandas 数据框架使用.apply()
方法,然后对我们的自定义函数使用 lamda 函数。
data['GR_NORM'] = data.apply(lambda x: normalise(x['GR'], key_well_low, key_well_high, x['05_PERC'], x['95_PERC']), axis=1)
绘制标准化数据
为了查看最终的归一化数据,我们可以重用上面的代码来生成直方图。当我们这样做的时候,我们可以看到所有的曲线都被标准化为我们的参考井。
fig, ax = plt.subplots(figsize=(8,6))
for label, df in wells:
df.GR_NORM.plot(kind ='kde', ax=ax, label=label)
plt.xlim(0, 200)
plt.grid(True)
plt.legend()
plt.show()
我们现在有了标准化的伽马射线数据。当并排比较时,我们可以看到标准化对数据的影响。
结论
测井曲线的标准化是岩石物理工作流程中的一个重要步骤,可能有多种原因需要标准化,包括:工具校准不良、工具年份变化、岩性差异和井眼环境条件。
进行标准化使得多口井之间的比较更容易,并且在进行批处理时更容易选择关键的解释参数。
在本文中,我们介绍了如何使用 Python 对伽马射线测井进行简单的标准化。但是该方法同样适用于其他测井记录,通常需要注意的是,数据需要在事后进行感官检查。
参考
克雷恩岩石物理手册(2020 年)。可在:https://www.spec2000.net/08-normalization.htm
Equinor。(2018).公开所有 Volve 数据。可在:https://www . equinor . com/en/news/14 jun 2018-discovery-volve-data . html查阅
谢尔博士(2004 年)。测井标准化:方法和指南。岩石物理学,45(3),268–280 页。
PEX——AWS EMR 工作负载完美 PySpark 部署的秘方
如何使用 PEX 加速 PySpark 应用程序在临时 AWS EMR 集群上的部署
[OC]
在大数据和数据科学领域,Spark 已经成为除深度学习之外的几乎所有事物的黄金标准:
- 用于数据湖的 ELT 取代了更传统的 ETL 范例
- 用于高级分析的 Spark SQL
- 一个用于数据科学的分布式批量计算框架,包括 Spark ML、GraphX、GeoSpark 等。
- 具有小批量流的近实时应用
虽然 Scala Spark API 在数据湖 ELT 工作负载和数据工程中发挥着重要作用,但数据科学和高级分析领域几乎完全是用 Python 编写的 PySpark 工作负载。大多数 Spark 工作负载都是在 AWS EMR 集群上执行的。一般的数据驱动型公司在数量同样惊人的短命 EMR 集群上轻松运行数百个每日 Spark 作业。
AWS Spot 实例具有吸引力的价格点已经建立了在非常短暂的 EMR 集群上运行 single Spark 作业的范例。EMR 提供短暂的计算资源,而 S3 为数据提供永久存储。使用由 100% Spot 实例组成的短暂 EMR 集群将这种模式推向极端是非常常见的,神风特攻队风格。
这种短暂的 PySpark 集群模式只有一大痛点:用 Python 包引导 EMR 集群。
https://www.flickr.com/photos/janpersiel/27706588173(ɔ)
PySpark 在电子病历上的应用:坏与丑
在 EMR 上运行 PySpark 应用程序非常复杂。
[OC]
对于短暂的 EMR,我们必须在每次运行 PySpark 应用程序之前,根据我们的应用程序需求引导我们的集群。这应该是我们的 PySpark 应用程序包的一个简单的 pip 安装。但是现实远非如此简单。
- 我们需要在 EMR 上安装和更新软件包,因为许多版本后面缺少默认安装。
- 我们应该为我们的应用程序创建一个虚拟的 Python 环境,因为全局 Python 包会以奇怪的方式干扰。我们都去过那里。
- 我们需要用 pip 安装我们的 PySpark 应用程序和需求
- 我们需要从 S3 复制资产,例如 spark-submit 的 main.py 脚本
- 最后,我们使用我们的特定于应用程序的 spark 配置调用 spark-submit 来最终运行我们的工作负载
这种 EMR 方法有一些难点:
- 虽然我们实际上并不运行任何真正的工作负载,但是引导过程是为集群时间付费的。这对大型集群来说尤其重要!
- 我们必须维护和下载不属于 Python 应用程序包的资产,例如 spark-submit main.py 脚本
- 我们必须独立于 Python 应用程序包为我们的作业维护特定于应用程序的 Spark 配置,我们将 Python 应用程序包作为参数传递给 spark-submit 脚本。这些通常分散在 Jenkins 管道或气流 Dag 中,增加了维护 PySpark 应用程序的不必要的复杂性。
- 打包和安装具有复杂依赖关系的 Python 应用程序会直接将您引向臭名昭著的依赖地狱。
考虑到 Python 的流行程度,Python 工具链远非理想,安装带有复杂依赖链和 pip 的包通常会导致众所周知的依赖地狱。不幸的是,pip 缺少一个强大的依赖解析器。
CC BY-NC 2.5,https://xkcd.com/1987/
在商业数据科学团队中,通常还有第二层复杂性,即专有代码的私有包索引。我打赌你的私人包裹索引很慢。因此,您的 PySpark 应用程序的常见 EMR 引导脚本可能如下所示:
[OC]
如果运行 PySpark 应用程序像调用可执行文件一样简单不是很好吗?无 pip 安装要求。没有主。py。没有火花-提交与火花内存配置搞乱詹金斯或气流。
解决方案:PEX
PEX (Python 可执行文件)是一种文件格式和相关工具,用于创建类似于 virtualenv 的通用 Python 环境虚拟化解决方案。PEX 最初于 2011 年在 Twitter 上开发,用于将 Python 应用程序部署到生产中。PEX 文件是自包含的 可执行的 Python 虚拟环境。重点在于自包含和可执行性,这使得 PEX 文件非常适合应用程序部署到生产环境中。使用 PEX 文件,部署应用程序所需的唯一步骤是复制文件。不需要安装 pip,也不需要修改路径。
在 PEX 的帮助下,在 EMR 上运行 PySpark 应用程序不再需要任何引导!
- PEX 大大简化了 PySpark 应用程序的运行
- 通过利用集群更早地运行我们的实际应用工作负载,而无需任何集群引导,从而节省资金。
[OC]
要创建 pex 归档,您可以使用 PEX 实用程序。您可以简单地安装它
pip install pex
当您在新的 Python 虚拟环境中执行此操作时,您可以使用 pex 来打包自身。以下命令使用名为“pex”的控制台脚本创建包含 pex 和请求的 pex 文件。将创建的可执行文件保存到~/bin/pex,您可以在任何 virtualenv 内部或外部使用 pex,就像您的路径上的任何其他可执行文件一样。
pex pex requests -c pex -o ~/bin/pex
PEX 有一个复杂之处:可执行文件包含一个自包含的 Python 虚拟环境,但不是 Python 解释器本身:PEX 可执行文件是依赖于平台的。目前,EMR 在 Linux 上运行 Python 3.6.10,你可以在 Mac 上开发。因此,通常最好使用 Docker 来创建可重复的结果。
构建与 EMR 兼容的 docker 映像,在 docker 容器中创建您的 PEX 归档:
FROM python:3.6-slim-busterRUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*RUN pip3 install pexRUN mkdir /app
WORKDIR /appENV PATH=/root/.local/bin:$PATH
如果您的组织使用私有包索引,例如 Artifactory,那么 PEX 显示了另一个弱点:在编写时,它没有通过 CLI 公开请求库的参数,这意味着当直接使用 PEX 解决包依赖关系时,我们不能为 pip 设置自定义网络超时。解决方法是使用舵手室。以下脚本可用于使用轮罩和带有自定义超时的私有包索引来构建 pex 归档:
#!/usr/bin/env bashpip3 download -r requirements.txt \
--dest ./build/wheelhouse \
--extra-index-url [https://private.registry.dev/pypi/simple](https://private.registry.dev/pypi/simple) \
--trusted-host private.registry.dev \
--timeout 120pex . -r requirements.txt \
-o ./dist/my_application.pex \
--platform manylinux2014-x86_64-cp-36-m \
--no-index \
-f ./build/wheelhouse
打包 PySpark 应用程序
我们的目标是打包一个完全自包含的 PySpark 应用程序,并在不需要 spark-submit 的情况下运行它。因此,我们的 Python 主函数必须创建一个 SparkSession:
if __name__ == "__main__":pex_file = os.path.basename([path for path in sys.path if path.endswith(".pex")][0])
os.environ["PYSPARK_PYTHON"] = "./" + pex_filespark = (
SparkSession.builder
.master("yarn")
.appName("my_spark_application")
.config("spark.submit.deployMode", "client")
.config("spark.yarn.dist.files", pex_file)
.config("spark.executorEnv.PEX_ROOT", "./.pex")
.config("spark.sql.shuffle.partitions", 4000)
.config("spark.executor.memory", "1G")
.enableHiveSupport()
.getOrCreate()
)
您可以将任何选项从您通常的 spark-submit 传递给 SparkSession builder。
这允许您在 PEX 可执行文件中执行 PySpark 应用程序,例如:
./my_application.pex -m my_application.main
执行 PEX 作为电子病历的一个步骤
最后一步是作为 EMR 步骤执行我们的 pex 应用程序。我们将使用脚本运行器和一个通用的瘦包装器作为一个步骤来执行 PEX。
脚本运行程序调用我们的瘦包装器,该包装器从 S3 提取一个 PEX 文件,并使用我们可能需要的所有环境变量和命令行参数来执行它。下面的脚本是一个你可以使用的名为 pex-executor.sh 的瘦包装器。只需将它放在 S3 上,即可供您的 EMR 集群使用:
#!/bin/bash
# Author: Jan Teichmann
# Version: 2020-02-10
# Wrapper to execute a PEX archive via an EMR Step
# Step type: Custom JAR
# JAR location: s3://eu-west-1.elasticmapreduce/libs/script-runner/script-runner.jar
# Arguments:
# s3://.../pex-executer.sh
# s3://.../some-etl-job.pex
# HADOOP_HOME=/usr/lib/hadoop
# SPARK_HOME=/usr/lib/spark
# ./some-etl-job.pex -m package.module -fromdate=2020-04-20 -todate=2020-04-22aws s3 cp $1 .
chmod +x $(basename -- $1);shift;
eval "$@"
现在,您可以提交 EMR 步骤,例如通过 Airflow 的 EmrAddStepsOperator:
EmrAddStepsOperator(
task_id="my_application",
job_flow_id="my_emr_cluster_id",
steps=[
{
"ActionOnFailure": "CONTINUE",
"Name": "Run my_application Step",
"HadoopJarStep": {
"Args": [
"s3://.../pex-executer.sh",
"s3://.../my_application.pex",
"HADOOP_HOME=/usr/lib/hadoop",
"SPARK_HOME=/usr/lib/spark",
"./my_application.pex",
"-m",
"my_pyspark_application.main",
"-parameter1",
"value1"
],
"Jar": "s3://eu-west-1.elasticmapreduce/libs/script-runner/script-runner.jar",
},
}
],
aws_conn_id="aws_default",
dag=dag,
)
正如您在上面看到的,我们首先传递两条 s3 路径。第一个是我们的瘦包装器 pex-executor.sh,它将由 AWS script-runner.jar 执行。pex-executor 脚本将依次下载应用程序 pex 可执行文件。
我们还定义了 EMR 需要的两个环境变量 HADOOP_HOME 和 SPARK_HOME。您可以根据需要添加任何额外的环境变量。
然后,我们传递可执行文件的名称,并传递 Python 应用程序的任何 CLI 参数。
或者通过 EMR 控制台:
[OC]
摘要
PEX 允许我们将 PySpark 应用程序作为完全自包含的可执行文件运行,就像我们使用 Scala API 时,带有 uber-JAR 或 fat-JAR 的 Spark 应用程序允许的那样。
这极大地简化了使用 PySpark 和的短暂 EMR 集群,节省了时间和节省了资金,因为我们不必引导集群。
将 pex 可执行文件的创建打包到一个 Jenkins 管道中,您就拥有了一个强大的 DevOps 模式来构建包并将它们上传到 S3 进行部署。
然后,您可以安排您的 PySpark 应用程序,例如使用气流。因为您的 PEX 应用程序是完全自包含的,所以您将能够在 Airflow 中创建非常通用的 Dag,而不会导致任何应用程序逻辑和配置分散在多个平台和存储库中。
简单是最高级的复杂
Jan 是公司数据转型方面的成功思想领袖和顾问,拥有将数据科学大规模应用于商业生产的记录。他最近被 dataIQ 评为英国 100 位最具影响力的数据和分析从业者之一。
在领英上连接:https://www.linkedin.com/in/janteichmann/
阅读其他文章:https://medium.com/@jan.teichmann
PGM 2:理解贝叶斯网络的基本概念
PGM 系列的第 2 部分,建立构建贝叶斯网络的概念理解
乔尔·菲利普在 Unsplash 上的照片
这是 PGM 系列的第 2 部分,其中我将介绍以下概念,以便更好地理解贝叶斯网络:
- 从联合分布计算条件概率——归约和归一化
- 排斥
- 结构类型—链式、叉式和碰撞式
- 条件独立性及其意义——d-sep 和马尔可夫毯
- 推理的类型:诊断型、预测型和因果关系型
从之前关于概率图形模型(PGM)介绍的文章中,我们了解到图形模型本质上编码了一组随机变量(或者简单地说变量)的联合分布。而联合分布又可以用来计算另外两种分布——边际分布和条件分布。
这些分布背后的直觉:
- 边际概率是单个事件或变量的概率,不参考任何其他变量的任何特定范围的值,例如 P(A)。
- 在大多数现实生活的例子中,有多个过程在起作用。因此,需要对同时发生的事件(如 A、B 和 C)的概率进行研究,以得出推论。它被称为联合概率,用 P (A,B,C)表示
- 给定一个事件 B 发生的先验信息,事件 A 的概率如何变化,用条件概率来度量,记为 P(A|B)。
让我们举一个快速的现实生活中的例子:
- 边际概率——在不知道疫情袭击全球的情况下,经济衰退的概率,P(衰退)
- 联合概率——“衰退”和“疫情”同时出现的概率,P(衰退和疫情)
- 条件概率——给定疫情 P(衰退|疫情)的衰退概率。疫情首先出现,导致经济放缓和衰退。因此,在这种情况下,条件概率被解释为——根据疫情的证据,衰退的可能性有多大?
使用上一篇文章中的以下示例,从联合分布计算边际分布和条件分布:
前一篇文章中的例子将用来展示边际和条件概率计算
条件分布:
假设上述网络的联合概率数据库如下所示:
联合概率表
设 C_1 是变量 C 的观测状态,那么给定观测状态 C_1 的变量 A 和 B 的条件分布是通过边缘化观测状态的联合分布来计算的。
从数学上讲,这可以分两步完成,如下所示:
- 归约:剔除未观察到状态的行,即本例中的 c0:
计算条件分布的简化步骤
请注意,“概率”栏中的数字总和不等于 1,这意味着它不是一个概率分布。因此,作为缩减步骤的结果,我们得到了非归一化的度量。这要求我们的下一步是“重整化”。
- 重正化:取剩余行的和,将每个非范数度量除以总和,得到归一化的概率分布。请注意,“Norm”列中的数字现在总计为 1
重正化
边际分布:
边缘化是指在一个单一变量或一组较大变量的一个子集上产生一个分布的过程,而不涉及一组观察到的变量。
为了计算 A 和 B 上的联合分布,我们将 C 的所有状态上的 P (A,B,C)边缘化:
排斥
常见网络结构:
用医疗保健领域的例子(基于作者的个人直觉)说明网络结构。
年龄(A)、糖尿病(D)、高胆固醇(C)和葡萄糖(G)等变量如何与心脏病(H)相关联?括号中提到的变量符号主要用于书写结构的联合分布表达式。
基于领域/经验知识,让我们尝试从医生的角度进行推理,并思考他如何将年龄知识与糖尿病概况联系起来——在没有任何其他信息的情况下,可以安全地假设随着年龄的增长,患糖尿病的可能性很高。
这种关联由从年龄到糖尿病的边缘来表示,其中年龄是糖尿病的因果因素,并且这两个变量彼此直接相关。
请注意,在下面的结构中,观察到的节点是灰色阴影。
让我们进一步理解常见的网络结构类型:
- 连锁:高葡萄糖含量可导致个体患上糖尿病。这表现为从葡萄糖到糖尿病的边缘,它们之间有直接的联系。此外,如果糖尿病患者更有可能患心脏病,那么这两者之间就有一个优势。
代表高葡萄糖导致糖尿病的链结构显示如下,高葡萄糖反过来会导致更高的心脏病风险:
链子
我们可以将联合概率分布写成:
P (G,D,H) = P(H|D) * P(D|G) * P(G)
链式结构进一步细分为两类——1)因果结构和 2)证据结构。
上面举例说明了因果链结构。因果链和证据链结构的区别主要源于突出因果关系的箭头方向。
因果结构是自上而下的,也就是说,它从原因开始,通过对链式结构中跟随它的变量的影响。然而,证据结构说,给定证据,我们可以推断出它的原因,即它是一个自下而上的关系。
踪迹:如果 P 被定义为从葡萄糖到心脏病的踪迹,那么了解糖尿病就告诉了我们关于心脏病病因的一切。因此,如果已知 D,知道葡萄糖%并不能为发现心脏病的原因提供任何额外的信息。因此,在这种结构中,葡萄糖与糖尿病引起的心脏病无关。
另一种解释这种结构和 Trail 概念的方式:如果我们意识到一个人的健康状况存在糖尿病状态,那么它就解释掉了关于心脏病风险的一切,从而阻断了在没有糖尿病信息的情况下“葡萄糖”状态可能有助于心脏病的信息流。
经验法则:路径中观察到的任何节点都会阻碍信息流,使连接变量相互独立。
理解一个变量的证据的影响如何使两个(相关的)变量独立是至关重要的。这将有助于理解 d-sep 和 Markov blanket 的概念。
2.碰撞器/共同效应:如果我们知道一个人有高胆固醇,那么仅仅这个知识并不能告诉我们一个人患糖尿病的可能性。因此,这两个变量是独立的,因为一个变量不共享另一个变量的任何信息。
然而,当它们与心脏病相关联时,如下面的结构所示,这表明在观察心脏病的常见影响时,胆固醇分享了关于其糖尿病概况的信息。
因此,我们从这个结构中得出的结论是,在我们观察到共同的影响(即心脏病)之前,这两个原因是相互独立的。这一事件导致胆固醇和糖尿病这两个变量之间的某种关系的发展,使它们相互依赖。
共同效应
联合概率分布写如下:
P (C,D,H) = P (H|C,D) * P(D) * P(C)
Trail: 观察心脏病或其任何后代(从心脏病出现并与另一个变量相连的边缘)引出了两种常见效应之间的依赖性,而缺乏心脏病知识使胆固醇和糖尿病彼此独立。
达芙妮·柯勒教授解释的水阀例子很好地将这个概念内化了。我非常鼓励她在 Coursera 主持的 PGM 专业化课程中深入研究这些概念。
我们的例子中的水阀类比如下:观察心脏病(或它的后代)打开了通道,允许从糖尿病到胆固醇的信息流动,带出依赖关系。
- Fork/Common Cause: 一般来说,糖尿病和心脏病有很强的相关性,但是如果我们引入可变年龄,那么我们就是在断言,给定一个人的年龄,在一个人的糖尿病状态和心脏病风险之间没有进一步的关系。简单地说,年龄这个隐藏变量解释了所有观察到的糖尿病和心脏病之间的相关性。
也就是说,如果老年人更容易患糖尿病和心脏病,那么年龄就成为这两种影响的共同原因,如下图所示:
共同事业
联合概率分布写如下:
P (A,D,H) = P(H|A) * P(D|A) * P(A)
线索:让我们遵循同样的水阀类比,来理解这个倒 V 型共因结构中的信息流。
如果我们观察年龄,它会阻止糖尿病和心脏病这两个变量之间的信息流,使它们相互独立。相反,对可变年龄一无所知会打开渠道,让信息流动。这使得变量是相关的。
条件独立性及其意义——d-sep 和马尔可夫毯
两个变量在第三个变量存在的情况下变得相互独立的条件依赖现象被称为 d-sep 。
根据定义,
如果两个节点 u 和 v 之间的所有轨迹都是 d-分隔的,则它们被 Z d-分隔。如果 u 和 v 不是 d 分隔的,则它们是 d 连接的。
d-sep 的概念使我们更容易理解另一个关键概念——Markov Blanket:变量的一个子集,它包含了对正在讨论的变量进行推断所需的所有有用信息(例如下图中的 A)。换句话说,它涉及所有必要的变量,没有这些变量,我们就没有足够的信息来计算节点的分布。
作者加:https://en.wikipedia.org/wiki/Markov_blanket
但是为什么条件独立或对 d-sep 和 Markov blanket 的研究在贝叶斯网络的研究中具有如此重要的意义?
嗯,那是因为网络对多个互相连接的节点的联合分布进行了编码。
使用条件独立性假设:“给定其父节点,每个变量有条件地独立于其非子节点”,计算联合分布的参数数量急剧减少,从而降低了计算复杂度(因为条件独立性仅考虑父节点,而忽略其他一切)。
好了,我们现在可以利用条件独立性的概念来模拟贝叶斯网络中的依赖关系,但是要达到什么目的呢?我们能用贝叶斯网络做什么样的推理?
- 诊断推理:诊断是一个向后看的过程。它通过深入研究数据、分析数据和识别模式,告诉你过去为什么会发生一些事情。相当于找到了某个事件背后的根本原因。
让我们根据上面的共同效应网络来理解它,其中高胆固醇和糖尿病都预示着心脏处于更高的风险中。我们诊断病人心脏病状态背后的原因。
因此,我们观察到的成为证据,我们寻求推理或推断的变量成为查询变量。在这种情况下,心脏病是证据,糖尿病和胆固醇是查询变量。
我们可以从这样的网络中得出以下推论:
- P(D1 | h1):考虑到患者有心脏病,患糖尿病的概率
- P(C1 | h1):考虑到患者有心脏病,胆固醇偏高的可能性
2) 预后推理:预后是一个前瞻性的过程。这类似于机器学习算法通常所做的,即给定历史数据和关联;它对未来做出预测。
假设一个病人患有糖尿病和高胆固醇(证据),病人携带心脏病的概率是多少(查询)。
从这样的网络中可以得出以下推论:
- P(h1 | D1):糖尿病患者患心脏病的概率
- P(H_1|D_1,C_1): 如果一个人同时患有糖尿病和高胆固醇,那么他患心脏病的可能性
3) 因果推理:
它不同于上面讨论的两个原因,因为它涉及两个原因之间的信息流。
一旦有证据表明患者具有无心脏病的高胆固醇家族史(F ),则与高胆固醇引起的心脏病相比,患者患由糖尿病引起的心脏病的可能性急剧增加。
最初,这两个原因是独立的,但随着新变量“家族史”的证据,解释了证据的原因,一个原因“糖尿病”的概率急剧增加,并分享了另一个变量“高胆固醇”不是患者心脏病主要原因的概率。
因果推理
就这样,我们到了本文的结尾。图形网络的概念通常被认为是困难的。我试图用简单的术语解释贝叶斯网络的基础,同时保留概念的本质。
我的召唤来自 Coursera 的 PGM 专业化。
希望这篇文章能成为你的召唤,激励你去学习更多关于图形网络的知识,以释放它们的力量。
请继续关注下一篇文章,我们将构建贝叶斯网络,学习参数并进行推理,以更好地理解我们的数据。
一如既往,快乐读书,快乐学习!!!
参考资料:
http://www-Prima . imag . fr/Prima/home pages/jlc/Courses/2016/ensi 2。SIRR/ENSI2。SIRR.S13.pdf
https://www . coursera . org/specializations/probability-graphical-models
Pheatmap 绘制了漂亮的热图
工具
如何用 r 中的 pheatmap 生成漂亮的热图的教程。
eatmap 是数据科学家必备的数据可视化工具包之一。
在 R 中,有很多生成热图的包,比如heat map(),heat map . 2(),以及heat maply()。不过我最喜欢的还是pheatmap()。我非常肯定你看完这篇帖子后会同意我的选择。
在这篇文章中,我将通过将这个强大的数据可视化包 pheatmap 应用到 NBA 球员在2019–2020赛季的基本统计数据中,来详细介绍这个包。
原始数据来自 篮球参考 。你可以手动下载数据集,也可以按照我之前的一篇文章来抓取数据。**
准备开始了吗?我们走吧。
准备
****语言:R。
包名: pheatmap 。
**install.packages("pheatmap")
library(pheatmap)**
****数据:2019–2020 NBA 球员场均数据。
**df = read.csv("../2019_2020_player_stats_pergame.csv")head(df)**
我们正在处理的数据集的头(由 Yufeng
上面是我们正在处理的数据框的头部。你不明白列名是什么没关系,因为它们都是篮球的统计数据。不影响我们对热图绘制的探索。
****数据清理:过滤掉每场比赛上场时间少于 30 分钟的球员,删除赛季中被交易的球员的重复,并用 0 填充 NA 值。
**df_filt = df[df$MP >= 30 ,]TOT_players = df_filt[df_filt$Tm == "TOT","Player"]df_used = df_filt[((df_filt$Player %in% TOT_players) & (df_filt$Tm == "TOT")) | (!(df_filt$Player %in% TOT_players)),]df_used[is.na(df_used)] = 0**
默认 pheatmap
首先,pheatmap 只接受数字矩阵对象作为输入。因此,我们需要通过删除分类数据的前 5 列,将数据帧的数字部分转换为矩阵。
**df_num = as.matrix(df_used[,6:30])**
由于矩阵的行名是热图中的默认行标签,我们最好通过避免数字索引使它们有意义。
**rownames(df_num) = sapply(df_used$Player,**function**(x) strsplit(as.character(x),split = "\\\\")[[1]][1])**
玩家数据的不同列在范围上有很大的变化,所以我们需要 缩放 它们以防止热图被大值所支配。
**df_num_scale = scale(df_num)**
R 中的 scale 函数对输入数据的列执行标准缩放,首先从列中减去列均值( 居中步长 ),然后将居中的列除以列标准差( 缩放步长 )。此函数用于将数据缩放至均值为 0、标准差为 1 的分布。
其方程如下所示,其中 x 为数据, u 为列均值, s 为列标准差。
****z = (x — u) / s****
您可以通过分别设置 center = FALSE 或 scale = FALSE 来关闭 R 中的中心步进或比例步进。让我们通过绘制缩放前后玩家每场比赛的积分密度来直观地了解缩放的效果。
**plot(density(df$PTS),xlab = "Points Per Game",ylab="Density",main="Comparison between scaling data and raw data",col="red",lwd=3,ylim=c(0,0.45))lines(density(df_num_scale[,"PTS"]),col="blue",lwd=3)legend("topright",legend = c("raw","scaled"),col = c("red","blue"),lty = "solid",lwd=3)**
裕丰对缩放和未缩放数据的比较
缩放后,数据准备好输入函数。
让我们看看默认的 pheatmap。
**pheatmap(df_num_scale,main = "pheatmap default")**
由于峰制作的热图
函数的默认行为包括 行和列 的层次聚类,在其中我们可以观察到相近位置的相似球员和统计类型。
例如,在热图的中间部分有一个超级温暖区域。对应的是一堆超级巨星,包括 詹姆斯·哈登、【卢卡·东契奇】、勒布朗詹姆斯、达米恩·利拉德** 。**
如果要关闭聚类,可以将 cluster_cols 或 cluster_rows 设置为 False 。下面的代码取消了列聚类。
**pheatmap(df_num_scale,cluster_cols = F,main = "pheatmap row cluster")**
俞峰制作的热图
值缩放
实际上,函数本身可以在热图中进行行列缩放。它主要用作跨行或列比较的可视化目的。以下代码显示了行缩放热图。
**pheatmap(df_num_scale,scale = "row",main = "pheatmap row scaling")**
由于峰制作的热图
向行和列添加批注
注释功能是 pheatmap 最强大的特性之一。具体来说,您可以输入一个 独立数据帧 ,并对热图矩阵的行或列进行注释。
例如,我用他们的位置注释了每个球员,使其成为一个数据框对象,并将其输入到 pheatmap 函数中。需要注意的一点是,根据您的注记目标,注记数据框的行名称必须与热图矩阵的行名称或列名称相匹配。
**pos_df = data.frame("Pos" = df_used$Pos)
rownames(pos_df) = rownames(df_num) # name matching
pheatmap(df_num_scale,cluster_cols = F,annotation_row = pos_df,main = "pheatmap row annotation")**
由于峰制作的热图
从热图中可以看到,还有一列颜色表示玩家的位置。
我们看到球员并没有按照他们的位置聚集在一起,这表明随着篮球的发展,球员的位置和他们的比赛类型之间的关系变得模糊了。
此外,我们还可以添加列注释。我用它们的分类来命名统计数据,包括进攻、防守和其他。
**cat_df = data.frame("category" = c(rep("other",3),rep("Off",13),rep("Def",3),"Off",rep("Def",2),rep("other",2),"Off"))
rownames(cat_df) = colnames(df_num)**
然后,我绘制了只带有列注释的热图。这次我只打开列集群。
**pheatmap(df_num_scale,cluster_rows = F, annotation_col = cat_df,main = "pheatmap column annotation")**
由于峰制作的热图
我们可以从热图中看到,与进攻相关的数据倾向于聚集在一起。
将热图切割成碎片
我想介绍的最后一个功能是热图切割功能。有时,如果我们通过聚类来切割热图,会给出更清晰的可视化效果。
通过分割热图,独立的区块将代表其自身的人口。让我们看看下面这个例子中的行切割。
**pheatmap(df_num_scale,cutree_rows = 4,main = "pheatmap row cut")**
由于峰制作的热图
在代码中,我输入了cutree_rows = 4
,这意味着将热图按行分割成 4 个集群。
前面提到的超星组出现在切割热图的第三块中。
我们可以对下面的列做类似的事情。
**pheatmap(df_num_scale,cutree_cols = 4,main = "pheatmap column cut")**
由虞峰制作的热图
通过这种方式,相似的统计数据显示得非常接近。
到目前为止,我已经了解了 pheatmap 的所有主要特性。当然,还有 包中更多的细节 ,比如调色板,聚类距离度量等等。有兴趣的可以参考功能手册。
希望这篇教程能帮助你强化自己的可视化工具包。如果你喜欢读这篇文章,你也可以在我的其他文章中找到有趣的东西。
** [## NBA 球员统计数据 R 包“ggplot2”中数据可视化的实践指导
应用于 NBA 数据的 R 数据可视化工具“ggplot2”的 6 分钟之旅。
towardsdatascience.com](/hands-on-guidance-of-data-visualization-in-r-package-ggplot2-of-nba-players-stats-d812ed272d66) [## 给出了随机森林分类器的特征重要性
如何建立一个随机森林分类器,提取特征重要性,并漂亮地呈现出来。
towardsdatascience.com](/present-the-feature-importance-of-the-random-forest-classifier-99bb042be4cc) [## 谁是本赛季 NBA 的最有价值球员?
一个案例研究,展示一个机器学习项目从开始到结束的样子。
towardsdatascience.com](/whos-the-mvp-of-nba-this-season-3e347c66a40a)
现象学:下一次人工智能浪潮
现象科学如何帮助 AI 进行下一步的认知探索。
现象学之父埃德蒙德·胡塞尔。资料来源:literariness.org
如果你应该解释什么是桌子,你会惊讶地发现这有多难,尤其是当你在和一台电脑说话的时候。人工智能的下一个突破将会是——给人工智能带来现象学。我们可以尝试如下定义一个表:
- 桌子有四条腿:
- 桌子是由木头制成的;
- 桌子有一个平面;
- 桌子是长方形的;
- 桌子主要是用来吃饭或放东西的。【1】
这不是一张桌子。资料来源:Roomandboard.com
大多数人对这个定义感到很舒服:一个有四条腿的物体,由木头制成,表面平坦呈长方形,主要用于用餐或放东西。然而,如果桌子是椭圆形的,放在底座上(而不是腿)等等,事情就变得复杂了。任何可以想象的用语言解释的方法都可能失败。
如果你曾经登录过 reCAPTCHA(“我不是机器人”)的网站,你应该知道计算机区分物体有多难。挑战应答测试用于确定用户是否是人类。呈现一系列图像,用户必须点击与文本相关联的相应图片。“交通灯”、“人行横道”、“汽车”,是一些常见的例子。
reCAPTCHA 试验。来源:谷歌。
reCAPTCHA 使用一套非常复杂的技术。然而,该算法并不关心对象的内在和客观定义,而是大多数人对它们的感知。大多数人点击的是真相更可能显化的内容。在正常曲线中,如果你在平均值的某个标准偏差范围内,你最有可能是人类。从这个意义上说,即使呈现的图像不是“交通灯”,而是看起来像“交通灯”,大多数人也会点击它,从而强化学习算法。
幸运的是,我们有现象学。
现象学是由埃德蒙德·胡塞尔首先提出的,它试图反思现象的结构和意识,并创建一个框架来科学地研究它们。它最重要的主题之一是所谓的想象变异。想象变异试图通过挑战来解构我们的固有观念和偏见。回到我们的表的例子,富有想象力的变化会问:
- 如果没有腿,还会是桌子吗?
- 如果不是木头做的,还会是桌子吗?
- 如果它没有平面,还会是桌子吗?
- 如果它不是长方形的,还会是桌子吗?
- 如果桌子不是主要用来吃饭或放东西的,那它还是桌子吗?
如果这些答案中有任何一个是否定的,那么,这个定义可能是错误的。
reCAPTCHA 可能专注于交通相关的挑战,这可能会给我们一个暗示,即其母公司谷歌将如何处理这些数据:上传到自动驾驶汽车中。全球每天都有数百万用户训练该算法。这些海量的数据可以帮助自动驾驶汽车确定要避开什么和遵循什么标志。在这种情况下,算法就像人类一样,不知道“交通灯”到底是什么(类似于模糊的表定义),但它知道当它看到它时,它就是交通灯。
现象学的工作方向相反。而强化 AI algo 用的是“它是什么就是什么”,现象学则试图用系统的反思来研究经验和意识。因此,交通灯不仅仅是你所看到的,而是一系列有意识的体验,如判断、感知和情感。
一张桌子不仅仅是一张桌子,因为对观察者来说它看起来是一张桌子。一定还有别的原因。如果一张桌子仅仅是物理视觉属性的集合,一个盲人将不能描述或感觉一张桌子。人类将无法拒绝“看起来”是它们本来面目的事物。
如果你想知道字典上是怎么说的,桌子是“一件由固定在腿上的光滑平板组成的家具”【2】。在你查的任何一本字典里,这个定义都是完全错误的。另一方面,谷歌有一个更好的字典:二进制字典。它不是用几个词来定义一个表格,而是有来自数百万用户的万亿字节的训练数据,这些数据准确地定义了什么是表格,什么不是。
这是未来主义词典。词语不会由或其他词语来定义,而是由一组复杂的情绪、图像和意识的数字数据来定义。一个简单的词,如“table ”,将是大量的数据,可以用文字(数百页,解释表格是什么)或潜在表格类型的几个图像呈现给读者。
自人工智能兴起以来,哲学一直是与技术不断融合的领域。将现象学引入 AI 可以帮助我们更好地理解如何建模和存储数据。人类的感知是如何脱离人工智能的,以及如何被调和。现象学是一个奇妙的领域,肯定会成为人工智能的基础部分。
【1】PHILO-notes——Youtube
【2】韦氏词典
Photoshop 神经化了
Adobe 的 8 种神经过滤器及其替代品。
图片(截图)作者。
Update v 22.0 for Photoshop(2020 年 10 月 20 日)具有历史意义。它不仅仅是一些 bug 修复或 UI 增强。此次更新包括神经过滤器——基于 GAN 和神经网络的图像处理工具。最后,不仅数据科学家和人工智能研究人员可以从视觉领域的机器学习中受益,视觉艺术家、创意机构和所有与图像打交道的人也可以受益。
但是有这么闪亮吗?即使 Adobe 已经在其 Sensei 框架中使用了 ML 的强大功能(在内容感知缩放或变形剪切等方面)。),但是这个创意垄断者能让我们相信他们的神经工具的质量吗?让我们先看看它,不带偏见,已经准备好了,因为我们同时了解并使用了许多基于 GAN 的解决方案。我们是谁可以比较。
工具种类
正如你在标题中看到的,Adobe 正在推出越来越多的过滤器。目前,大约有一半是可用的,这可以通过智能人像来补偿,有许多参数可以尝试。您可以声明其余每个产品的测试状态:
图片(截图)作者
所以让我们开始吧!
特色过滤器
01.皮肤光滑
Adobe 神经过滤器的主要目标群体是创意机构,他们希望在效率方面受益于 ML。
免责声明:对于本出版物中的肖像实验,我使用的是由art breader基于 StyleGAN/StyleGAN2 生成的图像。我尝试了“人体摄影”,结果也令人信服。出于版权和隐私的考虑,这里使用了人工智能生成的图像。
滤镜“皮肤平滑”(特色滤镜)肯定会受到摄影师的欢迎,他们希望以数字方式“修饰”他们的模型(我在这里保持中立,即使我更喜欢自然的面部特征,而不是大量修饰)。
皮肤平滑滤波器
你可以控制照片的模糊和平滑度。如你所见,每一个过滤器都经过评估——Adobe 正在收集所有过滤器的反馈,倾听用户的意见。协作开发是一件好事(我希望他们能实现用户的想法)。
这里有一个女性面孔的例子:
左:原始(卡夫卡少女,用美术师生成),中:应用“皮肤平滑”,右:滤镜层//作者图像
男脸也行。
左:原始(用 Artbreeder 生成),中:应用“皮肤平滑”,右:过滤层//作者的图像
围绕这样的相面神经实验经常有争议。这个系统经常有偏差,对有色人种的图像效果不好。问题是在这些 ML 模型被训练的数据集中缺少多样性。这是最关键的问题之一,尤其是现在,因为我们正在为未来的人工智能系统建立训练基础。
Adobe 意识到了这些问题。亚历山大·科斯丁, Adobe 负责创意云的工程副总裁在接受 The Verge 采访时表示:
“我们面临的最大挑战之一是保持肤色,[……]这是一个非常敏感的领域。[……]如果它在太多的白人脸上训练它的算法[……]它的神经过滤器可能最终会将人工智能编辑的肖像推向更白的肤色”( The Verge )
在我的实验中,我没有观察到这样的问题——要么它已经被修复,要么我们需要测试更多的图像。(在“固定”下,我将理解为在具有更大多样性的人脸数据集上对系统进行重新训练,这需要时间,但对于让 AI 更少偏见来说非常重要)。
左:原始(用 Artbreeder 生成),中:“皮肤平滑”应用//作者的图像
优点 *快速简单的增加面部光滑度的方法。
缺点 *最终,偏颇的网络,有待更多的考验。
02.风格转移
另一款精选滤镜是一款好旧风格转移。
这个过滤器在本地 GPU 上运行。这里你有多种风格,都是预先设定好的。我当时数了 52 种风格。
当前更新于 2020 年 10 月 22 日测试//图片由作者提供
您还可以控制对目标图像的效果影响。
使用来自 good old(2018)Style Transfer Colab Notebook的图像,我试图将梵高的《星夜》的风格转移到伦敦的照片上。
下面是原始 Colab 笔记本的结果供参考,该笔记本使用 Tensorflow 的 Lucid :
来自风格转移的令人信服的结果。大本钟图片:(tensor flow Lucid Repository
对于我的 Photoshop 实验,我使用了相同风格的转移源图像(梵高的《星夜》)。幸运的是,它在提供的样式集合中可用——这里我们有第一个缺点:除了前面提到的 Colab 笔记本,Adobe Photoshop 不允许您上传您的图像以供样式转换参考(至少在当前版本中)。Adobe 的结果相当发人深省。
由 Adobe CC 进行的样式转换(具有不同的样式强度/画笔大小),图像由作者提供
以下是其他一些尝试:
分别是:《黑客帝国》、《火影忍者》和《康定斯基》
在这个滤镜的特性中,你会发现风格强度和笔刷大小的调整。您可以模糊背景,甚至在样式转换期间保留目标图像的颜色。风格转移过滤器很有前途,提供了有用的功能,但是它的结果和你在“风格转移”下理解的相差甚远。有趣的是,几年前的 Lucid 表现更好。
优点
*简易概念滤镜
*各种风格参考图片(目前:52 张)
*风格力度、保色等可调功能。
缺点
*没有真正令人信服的结果
*没有可能使用您的图像作为风格参考
到目前为止,这些是特色滤镜,但 Photoshop Neural Filter collection 提供了更多——其他滤镜处于开发状态“测试版”,但提供了一些有趣和令人惊讶的效果。
另类: 风格转移 Colab 笔记本(根据 TensorFlow 的 Lucid 改编)。免费且效果不错,即使没有调节功能。
贝塔过滤器
03.智能肖像
这可能是目前 Photoshop 中最有趣的神经过滤器之一。
这里我们有一个基于 GAN 的面部图像中各种元素的处理。通过控制定义的层,你可以改变肖像的许多特征。对于熟悉 GAN 基应用的人来说,像 ArtBreeder 这样的层调整是众所周知的。最新版本的 Artbreeder 主要运行在 StyleGAN 上,甚至提供了更多不同的功能。
截图来自art breader,(图片由作者提供)
对于我的进一步实验,我将使用 GAN 生成的图像(KafkaGirl)和一个现有人物的照片。
左图:卡夫卡少女(图片由作者提供),右图:维姬·赫拉迪尼斯的照片
亚历山德鲁·科斯丁在推特上提到了原因:
正如我们将在下面看到的,的确智能人像在原始照片人像(Photoshop 主要用户的实际兴趣领域)上效果更好。
凝视
在 Photoshop 中,你可以校正人像的凝视,效果惊人的好。(恰恰这个功能在 Artbreeder 中还是缺失的)。
左:原始图像,右:凝视调整图像//作者提供的图像
最大限度地凝视原始照片。我想,你可以用不同的图像获得不同的结果。照片由维姬·赫拉迪内斯
改变表情特征似乎还是有发展能力的。比如“幸福”。
幸福
虽然 art breader(style gan 2)稍微改变了面部的其他特征,并生成了和谐而真实的表情,但 Adobe 的神经过滤器几乎将微笑放在了一张具有逼真牙齿的脸上,但面部的其他部分仍然保持不变。
左:原创,中:Photoshop“快乐”,右:Artbreeder“快乐”
在原始照片的情况下,效果更好:
照片由维姬·赫拉迪内斯拍摄
原因已经在上面提到了:“面部保留”是智能肖像的主要焦点,因为使用 Photoshop,创意人员应该改变面部的一些特征,但不是面部
愤怒的
由于跨层注意力,你输入的音量越大,ArtBreeder 的图像就越容易从原始图像中替换出来。然而,您可以通过重新调整其他图层来抵消这些意外的变化。“愤怒”似乎更复杂,难以修正(可能是因为 FFHQ,Flickr-Faces-HQ 数据集,StyleGAN2 上缺乏“愤怒”的图像)。
左:原创,中:Photoshop“生气”,右:Artbreeder“生气”
尽管如此,Artbreeder 在 StyleGAN 生成的图像上用情感力量做得更好。
这是人类的照片:
照片由维姬·赫拉迪内斯拍摄
面部年龄。
这是 Artbreeder 仍然是赢家,至少关于合成 GAN 创造的脸。如果你把 Photoshop 中的调节器“面部年龄”移动到右边的极限,你会得到一个稍微老一点的女孩,有着几乎看不见的双下巴和旧的脖子皮肤。极限艺术培育者创造奇迹。
左:原创,中:Photoshop“面部年龄”,右:Artbreeder“年龄”
Photoshop 智能人像中的面部年龄处理确实比原始人类照片更好:
左:原文,中:年龄="-50% ",右:年龄= "+50% ",照片由 Vicky Hladynets 拍摄
头部方向
这个功能在 Artbreeder 中仍然没有,可能是 Photoshop 的一个亮点,但它仍然可以改进。在这种情况下,我结合了头部方向和凝视:
左:原创,右:Photoshop“头部方向:50”+“凝视:50”。
改变后的图像有问题,比例或构图不太对。
使用人类照片可以获得更好的效果:
光线方向
这个功能是必不可少的,尤其是在拼贴画或把一个人放在另一张照片中的情况下。唉,这个功能目前有问题:随机像素跳出了构图。
左:原文,右:Photoshop“头部方向:50”+“凝视:50”+“光线方向:50”。
尽管如此,这个功能对 Photoshop 来说是非常重要的(可能是非常需要的),我确信,这个功能还会改进。
这种错误在人类照片上是看不到的:
照片由维姬·赫拉迪尼斯拍摄
其他功能
智能肖像还有一些其他功能,在特定情况下很有用:
头发厚度让头发以非常自然的方式生长。
放置在背景之前移动头部——缺失的背景被内容敏感地填充(可能是修复)。
总而言之,智能肖像是使用神经网络的一个伟大开端,只是它仍然需要在各种情况下进行改进。
优点
*凝视、头部方向、位置、听觉厚度效果非常好——
- Photoshop 可以受益于人工智能驱动的面部调整,这种趋势很有前途
缺点
*仍有很大的改进余地
*功能上的变化很小
备选 : 艺术育种,肯定。即使它是由 StyleGAN 驱动的,但你可以上传现有的图像,并用所有 StyleGAN 生成的面部进行更改。唯一的问题是:有时上传的图像看起来与原始图像不同,我想这是由于与潜在空间的对齐。StyleGAn 似乎在“纠正”图像。
04.化妆转移
如果您想要将相同的化妆设置应用于不同的面部(反之亦然),此功能非常有用。
左:目标图像,艺术培育者,中:化妆源图像,由安德烈·萨金塞夫拍摄/右:艺术培育者
在这个实验中,颜色并不真正对应于化妆源图像,可能还取决于源图像和目标图像。
05.深度感知薄雾
这个过滤器可以检测图像的遮挡,效果惊人的好。尤其是有透视效果的照片,在消失点附近播放,这样的效果更有表现力。
左:原始图像,右:作者在最大//图像上的深度感知模糊
在下一张剧院舞台模型的图片中,你甚至可以看到前景中结构的对比:
左:原始图像,右:作者在最大//图像上的深度感知模糊
在此图像中,您可以更好地区分背景和前景,并强调前面的对象:
左:原件,右:
作者照片。
不过,我更喜欢深度感知模糊而不是薄雾,以获得更好的照片表现力,这一功能已经在许多智能手机相机中使用。使用 Photoshop,你也可以做到这一点,但有几种变通方法(例如“场模糊工具”),但上面的这种方法可以非常有效地支持这种功能。
我想知道,人工智能驱动的 3D 本·伯恩斯效果或 3D 摄影修复何时将由 Photoshop 实现——或者也由 After Effects 实现?
06.使…变成彩色
如果你正在寻找由 Jason Antic 实现的著名的 DeOldify 版本,你可能会失望。
杰森本人最近表示:
的确,Adobe 没有使用 DeOldify。回到我的 DeOldify 实验,我被颜色的想象力震惊了。
为了比较,这里有一张黑白照片,#去彩色化,#彩色化。
左:原件(照片:弗拉基米尔·佩雷尔曼),中:去彩色化,右:土坯彩色化
即使在 Adobe Colorize 的情况下,您可以调整颜色平衡,甚至定义焦点颜色和场景颜色,质量仍然有点暗淡。只需比较一下 DeOldify 赋予生命的花朵颜色的丰富程度——在 Photoshop 中,它仍然是一种简单的棕褐色。
替代:deol dify(GitHub/Colab Notebook)或其在 MyHeritage 中的实现。
07.超级变焦
“增强!”——我猜想德卡德用的是 Photoshop 的 SuperZoom。因为这种品质令人信服。
除了缩放功能,您还可以增强图像细节并消除 JPEG 伪像(如果您放大的是小的 JPG 低质量图像)。你也可以减少噪音和锐化图像。
如果你比较原始的“放大”图像及其所有像素伪像和增强的超放大图像,你可能会同意我的观点。
左:原创,Richt: SuperZoom //作者图片。
当然,仍然有新的神器,但锐度和形状都拍得非常好。
它还可以增强文本:
上图:原始照片,左:原始缩放,右:超级缩放(图片由作者提供)
08.JPEG 伪像消除
万一你要恢复数字图像,尤其是 jpg 文件,因为压缩过重而产生了伪像。Photoshop 在这里扮演着一个专业工具的角色——正如人们应该假设的那样。
比如我拍了这张照片,压缩到 50%:
这是工件的压缩缩放视图:
JPG 文物
没有 JPG 文物
如你所见,小故障已经消失,我们有适当的对比和模式。Here works ML for Adobe。
即将推出的过滤器
神经过滤器的其余部分仍在构建中——有几个非常有前途的功能,如
- 照片修复(去除噪声,带来清晰度和对比度)
- 灰尘、划痕和噪音减少
- 照片到草图
- 素描到肖像(使素描成为照片般逼真的图像)——在这两种情况下,它都可能基于 CycleGAN 。
- 仅提及一些不可用但已宣布的神经过滤器。
结论
Photoshop 作为专业人员和创意人员的图像处理工具,开始直接使用 ML 模型和人工智能的力量。这仍然是一条很长的路,而且非常麻烦(甚至用户界面也有奇怪的行为)。但这是一个很好的开始。尽管如此,人工智能社区现有的实现和实验在质量上已经超过了 Photoshop 看看 StyleGAN2、art breader、 DeOldify 、 RunwayML 套件。
人工智能世界正在走向创意词,创意词也越来越接近人工智能世界——两者之间从来没有边界。
机器学习和艺术、创造力和人工智能的共存激发并展示了我们未来充满希望的前景,这已经开始了。
更新日期:2020 年 10 月 23 日。在亚历山德鲁·科斯丁发表了一些评论和信息后,这篇文章被更新了。
Python 中的 NLP:从对话中提取意图
自然语言处理
我遇到的 python 最重要和最有用的特性之一是它的 Lambda 函数。就 Lambda 函数的使用而言,所有的在线课程都只是皮毛。然而,真实世界的用例需要对其特性的高级理解和实现。
在本文中,我们将看到 lambda 函数如何应用于包含复杂数据类型的列。我们将从基本操作开始,转到使用 lambda 函数的更高级的方法。我将给出一些例子,在这些例子中,我使用了 lambda 函数来处理一个数据帧,该数据帧由使用列表和元组的文本数据组成。
我将对从 Kaggle 的一个竞赛中获得的文本数据进行词性标记,以演示实现。
让我们开始吧。
基本面
重要的事情先来。我快速复习了 python 中的列表、元组和 lambda 函数。
Python 中的列表
List 是 python 中的一个集合。它允许你有重复的条目,它是有序的和可变的。python 中一个简单的列表例子。
mylist = [‘R’,’Python’,’Scala’,’Java’,’Go’,’SQL’]mylist[0:2]
#OR
mylist[:2]
***Out[1]:['R', 'Python']***# Negative Indexing
mylist[-1]
***Out[2]:'SQL'***
列表还有其他一些操作,但是这超出了本文的范围。您可以删除、添加或更改项目。您可以创建一个重复的列表或合并两个列表。如果你想学习更多关于这些函数的知识,有很好的在线教程。
Python 中的元组
元组是一系列不可变的 Python 对象。元组和列表的主要区别在于元组是不可变的,并且使用圆括号而不是方括号。
mytuple = ('R','Python','Scala','Java','Go','SQL')mytuple[0:2]
#OR
mytuple[:2]***Out[3]:['R', 'Python']***mytuple[0] = 'Julia'
**TypeError: 'tuple' object does not support item assignment**
Python 中的 Lambda
lambda 函数是一个小型匿名函数,它可以接受任意数量的参数,但只能有一个表达式。
语法:λ参数:表达式
下面是一个使用 lambda 函数将两个数相加的简单例子。如您所见,它极大地简化了为每个操作创建独立函数的操作。当处理大型数据集,并且您希望对数据集的整列应用函数时,它变得非常有用。
## Function to add 2 numbers **without LAMBDA** ##
def addition(a,b):
return a+baddition(3,5)
Out【6】:8
## Function to add 2 numbers **with** **LAMBDA** ##
addition = lambda a,b:a+baddition(3,5)
Out【7】:8
深潜
现在我们已经对列表、元组和 lambda 函数有了基本的了解。让我们来做一个更真实的练习。
我已经从这个链接中提取了文本数据来做这个练习。最初的竞赛要求参与者对文本数据中的简短答案(EssayText)进行评分。下面是数据集的前 5 行。它由 17207 行数据组成。
短文文本数据( Kaggle )
在我们的案例中,我们不打算为论文评分建立一个机器学习模型。我们将在‘essay text’列上构建一个词性(POS)提取器函数。我们将在这些列上运行一系列操作,目标如下-
- 了解数据框上的列表和元组操作
- 用于数据帧的文本列的标记化和词性标注
- 使用 lambda 函数进行涉及列表和元组的复杂运算
- python 中带有 FOR 循环和条件 IF 的嵌套 lambda 函数
- 分词 —第一步也是最重要的一步是将文本数据分词成单个单词。幸运的是,我们这里的数据已经是一个干净的文本,不像真实世界的 NLP 用例。这为我们节省了一些时间。我应用了两种类型的记号化-
句子标记化—它分隔文本中的各个句子。我使用了' \n '作为分隔符。
单词标记化——它将句子分成单个单词。
下面是使用 Lambda 函数进行标记化的代码:
*## Tokenize Sentences based on \n character ##*
*textdata[“tokenized_sents”] = textdata[“EssayText”].apply(lambda row: row.splitlines())
## Tokenize Words ##
textdata[“tokenized_words”] = textdata[“tokenized_sents”].apply(lambda row: [nltk.word_tokenize(i) for i in row])**## Remove empty elements in list of list ##
textdata[“tokenized_words”] = textdata[“tokenized_words”].apply(lambda row: [x for x in row if x])**textdata.head()*
以上 3 个操作很好地利用了 lambda 函数,将函数应用于整个列。句子标记化根据' \n '字符分隔文本。
为了对单词进行标记,我们使用 NLTK 库中的 word_tokenize 函数。每行有多个句子,因此,FOR 循环用于迭代一行中的每个句子。
第三个操作从列表中删除空元素,该列表是作为句子和单词标记化的一部分生成的。
这是我们的数据框在标记化后的样子。我有意删除了未使用的列,如 Score 和 Essayset
句子和单词标记化后的数据框架—作者提供的图像
基于' \n '进行标记很容易,因为我们有一个现成的 python 函数 splitlines()。然而,在现实世界的场景中,你可能想要分割其他一些没有功能的角色。例如一个点或一个逗号。lambda 函数仍然使它变得简单。这是我如何用点符号做的-
*## Tokenize Sentences based on ‘.’ character ##
textdata[“tokenized_sents”] = textdata[“tokenized_sents”].apply(lambda row: [i.split(“.”) for i in row])*
这里我们使用嵌套迭代,其中对于 essaytext 列中已经在' \n '字符上标记化的每个值,将在'.'上进一步标记化性格。
**练习—你能通过使用 lambda 函数而不是 FOR 语句来尝试上面的方法吗?
滚动前先试一试。
*## Tokenize Sentences based on ‘.’ character ##
textdata[“tokenized_sents”] = textdata[“tokenized_sents”].apply(lambda row: list(map(lambda m: m.split('.'), row)))*
嵌套的 Lambda 函数。让我们把这个留到博客的后半部分。
2。词性标注 —为了进行词性标注,我们将使用 NLTK 库提供的词性标注器。
*## Part-of-Speech Tagging for each word in the corpus ##
textdata[“POS_tags”] = textdata[“tokenized_words”].apply(lambda row: [nltk.pos_tag(i) for i in row])*
代码解释——它将 pos_tag 函数应用于每个单词,以确定其对应的词性。POS 标签作为元组列表的 列表存储在我们感兴趣的列中。对于这个特定的数据,元组列表就足够了,因为每个短文文本都是一个句子。然而,在你为每个文本选择多个句子的情况下,它将需要被存储为元组列表的列表。
为文章文本中的每个单词生成的词性标签—按作者分类的图片
让我们仔细看看为第一篇短文文本生成的位置标签
[[('Some', 'DT'),
('additional', 'JJ'),
('information', 'NN'),
('that', 'IN'),
('we', 'PRP'),
('would', 'MD'),
('need', 'VB'),
('to', 'TO'),
('replicate', 'VB'),
('the', 'DT'),
('experiment', 'NN'),
('is', 'VBZ'),
('how', 'WRB'),
('much', 'JJ'),
('vinegar', 'NN'),
('should', 'MD'),
('be', 'VB'),
('placed', 'VBN'),
('in', 'IN'),
('each', 'DT'),
('identical', 'JJ'),
('container', 'NN'),
(',', ','),
('how', 'WRB'),
('or', 'CC'),
('what', 'WP'),
('tool', 'NN'),
('to', 'TO'),
('use', 'VB'),
('to', 'TO'),
('measure', 'VB'),
('the', 'DT'),
('mass', 'NN'),
('of', 'IN'),
('the', 'DT'),
('four', 'CD'),
('different', 'JJ'),
('samples', 'NNS'),
('and', 'CC'),
('how', 'WRB'),
('much', 'JJ'),
('distilled', 'JJ'),
('water', 'NN'),
('to', 'TO'),
('use', 'VB'),
('to', 'TO'),
('rinse', 'VB'),
('the', 'DT'),
('four', 'CD'),
('samples', 'NNS'),
('after', 'IN'),
('taking', 'VBG'),
('them', 'PRP'),
('out', 'IN'),
('of', 'IN'),
('the', 'DT'),
('vinegar', 'NN'),
('.', '.')]]
该库在识别每个单词的词性方面做得很好。接下来,我们将对这个元组列表执行一些复杂的操作,以利用列表和元组上的 lambda 函数。
3。过滤 —假设我们只对词类中包含名词、形容词和限定词的单词感兴趣。我们可以这样做-
## PoS of interest ##
pos_tags = [‘DT’,’NN’,’NNS’,’NNP’,’NNPS’,’JJ’,’JJR’,’JJS’]## Filter words for which the POS belongs to the ‘pos_tags’ list
textdata[“POS_tags”] = textdata[“POS_tags”].apply(lambda row: list(map(lambda m: [(x,y) for (x,y) in m if y in pos_tags], row)))
代码解释-pos _ tags列表包含我们想要过滤的所有 POS 代码。lambda 函数迭代每一行,内部 lambda 函数迭代每一行的多个句子,对于一个句子的每个元组列表(word,POSTag ), FOR 循环迭代检查元组中的第二个元素是否出现在 pos_tags 列表中。唷!
再读一遍,推荐。
详细的位置标签列表-
- JJ 形容词“小”
- JJR 形容词,比较级“较小”
- JJS 形容词,最高级“最小的”
- 名词,单数“椅子”
- NNS 名词复数“椅子”
- NNP 专有名词,单数‘Boris’
- NNPS 专有名词,复数“英国人”
- DT 限定词
现在让我们检查一下第一篇文章的位置标签
[[('Some', 'DT'),
('additional', 'JJ'),
('information', 'NN'),
('the', 'DT'),
('experiment', 'NN'),
('much', 'JJ'),
('vinegar', 'NN'),
('each', 'DT'),
('identical', 'JJ'),
('container', 'NN'),
('tool', 'NN'),
('the', 'DT'),
('mass', 'NN'),
('the', 'DT'),
('different', 'JJ'),
('samples', 'NNS'),
('much', 'JJ'),
('distilled', 'JJ'),
('water', 'NN'),
('the', 'DT'),
('samples', 'NNS'),
('the', 'DT'),
('vinegar', 'NN')]]
它的工作,只有一行代码!难以置信!
4。整理 —我想尝试的最后一个操作是根据单词的词性标签将它们组合在一起。也就是说,所有不同形式的名词将表示为“NN ”,所有不同形式的形容词将表示为“JJ ”,如下所示
[’NN’,’NNS’,’NNP’,’NNPS’] --> 'NN'
[’JJ’,’JJR’,’JJS’] --> 'JJ'
下面是我如何使用嵌套 Lamda 函数实现它的
## Types of NN and JJ tags ##
noun_tags = [‘NN’,’NNS’,’NNP’,’NNPS’]
adjective_tags = [‘JJ’,’JJR’,’JJS’]textdata[“POS_tags”] = textdata[“POS_tags”].apply(lambda row: list(map(lambda m: [(k,’NN’) if (v in noun_tags) else (k,v) for (k,v) in m], row)))textdata[“POS_tags”] = textdata[“POS_tags”].apply(lambda row: list(map(lambda m: [(k,’JJ’) if (v in adjective_tags) else (k,v) for (k,v) in m], row)))
代码解释- 所以,迭代数据帧中的每一行(使用外部 lambda),然后迭代每一个句子(使用内部 lambda),然后迭代每一个句子中的元组(使用 FOR loop),如果元组的第二个元素出现在 noun _tags/形容词 _ tags 列表中,那么将第二个元素改为' NN'/'JJ '。图解-
校对的示意图(W = word)-按作者排序的图像
让我们检查一下第一篇文章的结果。
[[('Some', 'DT'),
('additional', 'JJ'),
('information', 'NN'),
('the', 'DT'),
('experiment', 'NN'),
('much', 'JJ'),
('vinegar', 'NN'),
('each', 'DT'),
('identical', 'JJ'),
('container', 'NN'),
('tool', 'NN'),
('the', 'DT'),
('mass', 'NN'),
('the', 'DT'),
('different', 'JJ'),
('samples', 'NN'),
('much', 'JJ'),
('distilled', 'JJ'),
('water', 'NN'),
('the', 'DT'),
('samples', 'NN'),
('the', 'DT'),
('vinegar', 'NN')]]
结果在意料之中。令人惊讶的是,如果我们试图将以 DT 开头、以 NN 结尾的单词作为词性标签进行分组,我们会得到一个有意义的短语。就像第一篇文章,我们可以提取-
*‘Some additional information’**‘each identical container’* *‘the vinegar’*
我就纳闷了,这能用在哪里!
智力的物理理论
1.0 版
摘要
多年来,我一直在寻找一个能统一我对智力和相关概念的理解的理论。在我遇到的这些框架中,大多数都有一些共同的核心思想,所以我一直试图将它们合并和提炼成一个更加正式和通用的框架。在这里,我想提出 PTI 来试图描述理解智力所需的基本块,在此基础上你可以描述相关的现象。
相关研究
让我们先来回顾一下不同领域是如何描述智力的。
在心理学中,可能最著名的理论是斯皮尔曼的一般智力或 g 因素。基于多项测试的因素分析,他得出结论,这些测试的分数非常相似。从那以后,心理学家试图描述和测量这一普遍因素,并创造了一系列智商测试来测量广泛的抽象认知能力。虽然这些测试现在被广泛使用,但它们薄弱的理论基础经常导致对结果的误解。
另一方面,一些心理学家试图将智力描述为一个由不同感觉模式、抽象层次或另一种划分心理能力的方式组成的组成系统,如 Thurstone 的初级心理能力、Gardner 的多元智能、Sternberg 的三元模型等。这些通常更具描述性,很少导致可测试的预测或实际测试,但它们捕捉了我们大脑更广泛的能力。
在哲学中,对意识的研究在相关概念中最受关注,但这种意识往往被归因于心理学家所描述的各种认知能力。在我看来,过去几十年中最重要的发展是从笛卡尔剧场/单演员描述模型到分布式计算系统的转变。
在神经科学中,在研究人员发现大脑由通过尖峰信号定期交流的单个神经元组成后,计算模型的发展成为一种趋势。我们已经很好地理解了神经元中尖峰脉冲是如何产生的,以及它们是如何传播的,但对于这种神经模型的回路如何能够代表我们的行为,我们仍然知之甚少。一些理论,如分级时间记忆,使用神经元网络作为基本元素,但这使多尺度分析和实验变得更加复杂。
在神经科学和哲学的边缘,你可以找到像信息整合理论和信息封闭理论这样的模型。IIT——在计算上很难,但对意识的医学研究有着有趣的预测和有希望的近似。ICT 在许多方面都很相似,简化了理论基础和计算,但它相当新,还没有得到很大的发展。
在经济学中,人类通常被建模为使用决策理论的理性主体。这些模型,像功能决策理论一样,非常善于描述代理人应该如何行动以最大化他们的报酬。然而,影响我们行为和个人偏好的大量因素使得很难将这些模型应用到现实世界的场景中。
所有这些研究激发了工程师们去制造能够独立思考的机器。如今,第一台计算机可能看起来像笨拙嘈杂的计算器,但很长一段时间,人们认为做代数需要大量的智力。人工智能工程的现代方法,如人工神经网络和决策树,在各种行业都有更广泛的应用范围,并且在十年前还被认为极其困难的问题上有望取得成果。虽然在严格的规则下通过图灵测试仍然是一个困难的问题,但人工智能生成的内容已经在一些领域与人类竞争,这一方向的研究经常会揭示令人兴奋的发现。
正如你可能看到的,关于智力的问题出现在许多领域,但不幸的是这项研究很少交叉。我相信一个简单的公共理论框架可以帮助解决这个问题。
代理人的边界
先说可观测宇宙和某智能体a⊆u。一般来说,边界的选择是任意的,但人类、大脑、神经元、硬件和软件最受关注。 的补码 a 是一个环境 E ,它可以发送输入 i 并从 agent 接收输出 o 。此外,在某些情况下,出于研究或工程目的,构建抽象代理(如数学模型)是有意义的。
在最基本的层面上,输入和输出被表示为基本粒子,但在大多数情况下,将它们近似为化学物质、符号、电信号、单词或其他方式更有意义。
在某些情况下,定义智能体的边界可能相当简单,就像神经元有细胞膜或独立的芯片一样。在其他情况下,就像人类每天使用的所有设备塑造了我们相互交流的方式一样,有人触摸屏幕或发送信息之间出现了一个重要的区别,特别是在所有关于直接脑机接口的研究正在进行的情况下。软件和所有与大多数生产应用程序相关的系统也是如此,细胞、社会团体,甚至是量子态中的基本粒子,它们的边界可能还没有被完全理解。
此外,在大多数情况下,跟踪代理的所有输入和输出实际上是不可能的,因为每秒钟都有大量的信息以光子、声波、皮肤压力和其他方式出现。我想强调的是,我们通常不知道代理的完整内部状态,而只知道它发出的一些输出。因此,我们必须意识到干净的实验研究很少是可能的,并据此解释结果。
代理的功能
假设代理的输出不是完全随机的,并且依赖于输入。然后,我们可以将代理的行为建模为一个策略函数a(I)=o。此外,大多数代理根据其先前的状态而随时间变化,因此我们可以将它们表示为a(Iᵗ)=fᵃ(Iᵗ,a(**
最简单的智能体我可以想象是一根抽象的理想电线,谁的策略是一个恒等式函数 一个(I)=I。
我们非常清楚基本粒子和最小原子的行为,它们通常用波函数来描述。对于许多人工的和抽象的代理来说也是如此。然而,更大的分子、细胞、植物、动物和社会代理的政策要复杂得多,所以我们必须处理不同的模型和近似值。即使对代理的功能有一个完整的描述,分析也可能非常困难,就像拥有数十亿参数的现代人工神经网络一样。
代理人的智能
假设在我们观察I/o时遇到了一些问题,那么我们就可以用一些性能函数p(I, o 来估计它做得有多好。在许多情况下,测量值会随时间变化或具有随机性,因此通过一组观察值来计算性能更有意义。此外,在某些情况下,我们真正感兴趣的是相对分数,比如与人类相比,人工智能代理的智商或性能。
然后,我们可以将智力的大多数定义描述为一些性能函数。
也许最著名的智力指标是智商或智商,它是用来评估人类相对于同龄同龄人的认知表现的。在过去的一百年里,人们开发了一系列智商测试,并进行了大量实验来探索分数与我们生活不同方面之间的相关性。然而,尽管在某些情况下具有统计学意义,这些测试通常测量抽象问题的表现,这使得将结果外推至现实世界问题的空间非常小。此外,在公共领域对智商分数的误解导致了这种测试的声誉不佳,因此大多数心理学家逐渐转向开发更专业的测试。
无论如何,智商不是唯一的选择,我们还有各种各样的其他面向人类的测试,用于营销,招聘,医疗,教育和其他目的。一个特别有趣的例子是基于 IIT 的大脑活动意识水平测量。然而,即使是智商也相当不为人所知,而其他测试通常背后的研究就更少了。
在更大的范围内,从团体、公司到国家,社会代理人的绩效函数大多来自经济学,如利润、GDP 等。然而,基于成员数量、平均寿命、抑郁率、群体智商和其他社会因素的其他选项也存在。
对于人工代理,性能测量问题和解决方案在许多方面都非常相似。对于通常的硬件和软件,第一次测试通常只给出一个二进制输出,不管它是否工作。然后,子系统可能被一个接一个地检查,代理可能被输入过载或者得到特别棘手的输入,以及测试执行速度。一些开发人员甚至将测试作为软件工程的核心。
此外,自从晶体管和现代计算机出现以来,人们就一直在思考,如何才能让计算机达到人类水平的智能。最著名的是图灵测试,它主要测量计算机是否可以在书面对话中模仿随机的人类。但是,事实证明,对于短对话来说,这是一项相当容易的任务,随着人工代理能力的增长,开发出了更严格的版本。
特别是随着差分编程、统计建模和深度学习的兴起,许多新的性能指标被开发出来,以处理不确定的输出,并使用这些信息来训练这些代理。此外,许多人工智能研究人员正在对每个模型进行多重测试,以确保它们具有尽可能广泛的能力。总的来说,随着人工智能体处理越来越多的人类问题,它们的性能函数变得越来越相似。
公共交通信息技术的问题
首先,当我试图把这个理论建立在物理学的基础上时,意识和智力的形而上学方面被完全抛弃了。此外,还有更难衡量的东西,比如艺术带来的审美愉悦、社交技能、道德判断的质量等等。
另一个常见的主题是一般智力。虽然心理学家试图提取和解释导致智力测试中许多变化的因素,人工智能工程师也试图建立更好的概括措施甚至人工一般智能,但我认为这最终是一个毫无意义的问题,因为智能体的表现与其环境中的特定问题密切相关。
医生对聊天机器人和健康机器人的看法
医生对医学中聊天机器人的正面和负面看法。
emojoez/Depositphotos.com 提供
聊天机器人的发展是人工智能和机器学习最重要的成就之一。虽然一些研究人员已经从理论上阐述了聊天机器人或健康机器人的潜在好处,但医疗保健领域最近发生的事情表明,他们正在如何将这些理论变成现实。
简介—聊天机器人市场
健康机器人市场正在经历投资者的涌入,这都要归功于它能给医生和病人带来的好处。全球医疗聊天机器人市场预计在 2024 年达到 4.1222 亿美元的规模。这一预测的市场规模具有 24%的 CAGR,表明健康聊天机器人的高性能和越来越多的公众采用。
在撰写本文时,我们已经讨论了不同健康机器人在连接患者和医疗保健提供者方面的作用,从而使健康机器人成为现实成就。
健康机器人的推出开创了以患者为导向的医疗保健服务的新水平,患者与医疗保健提供商的联系比以往任何时候都更紧密。
围绕聊天机器人的安全性和医生评估的聊天机器人可能带来的危险存在争议。这些观点来自于这些虚拟助手或聊天机器人在医疗保健领域的实际应用,他们在医疗领域拥有多年的经验。这些观点(积极的和消极的)发表在一项基于网络的横断面调查中,该调查是关于医生对医疗保健中聊天机器人的看法。1
积极的观点
就像医疗保健中人工智能和机器学习的其他领域一样,聊天机器人可以帮助医生、护士、患者及其家人提高服务水平。这是通过在紧急情况下提供帮助、管理药物、开发患者组织途径等等来实现的。这些活动对医疗保健提供者来说是一种负担。聊天机器人在医学领域的当前应用和积极前景包括:
ValeriHadeev/Depositphotos.com 提供
a.安排医生预约
聊天机器人可以在缓解医院排队等候拥挤方面发挥不可估量的作用。使用聊天机器人可以让病人预约医生,而不必去医院。
有报道称聊天机器人可以作为医生就诊的替代解决方案。这是因为聊天机器人可能更适合满足患者的需求,因为它们对种族、年龄、生物性别或肤色没有偏见。他们可以夜以继日地工作而不感到疲倦。这使得它们对那些即使在医生或医院不在附近也可能有一些医疗问题的患者很有用。他们用不同语言交流的能力在满足患者需求方面也至关重要。
在互动方面,患者向计算机透露的医疗细节比人类参与者多。他们更真诚地与聊天机器人互动,因为机器在交易中不会评判或公正。
一些患者认为,通过与聊天机器人互动,他们可以让摆脱可能与他们的医疗状况相关的污名。像 OneRemission 这样的聊天机器人在帮助癌症患者与他们的肿瘤医生持续互动方面已经派上了用场。
b.定位健康诊所
聊天机器人的第一个案例是为了帮助客户找到满足他们需求的服务而开发的。大多数诊所现在都在网站上安装了聊天机器人,帮助访客预约医生,并获得诊所的方向。聊天机器人可以增加医生网站的潜在客户,并为这些访问者提供顶级的健康信息。
c.提供药物信息
获取医疗信息不再局限于医院的四面墙。患者现在可以通过与聊天机器人互动来获得关于他们医疗状况的足够信息。这些机器人可以与病人互动,获得有关他们疾病的必要信息,缩小到特定的疾病状况,并在你的智能设备上提供最佳建议。你可以通过聊天机器人在线检查你的症状,比如 Buoy Health 和 Babylon Health 。像 Safedrugbot 这样的机器人可以为可能需要母乳喂养期间药物使用适当数据的卫生专业人员和医生提供类似助理的支持。
消极的观点
即使聊天机器人取得了成功,如果未来的应用必须是可能的,也有一些灰色区域需要修正。医生确定的这些区域包括但不限于以下方面:
Viacheslav Besputin/deposit photos . com 提供
a.不能有效地满足所有患者的需求
人工智能(不仅仅是聊天机器人)无法满足所有患者的需求,因此需要医疗保健人员来补充他们的活动。创造聊天机器人的唯一目的是给病人一个合适的选择,而不是把医生排除在外。聊天机器人被编程为只响应特定的算法,除此之外的任何东西都可能产生很少或没有结果。
b.无法表现人类的情感
这一直是辩论的基础,即聊天机器人和其他基于人工智能的过程是否可以在医疗保健服务中取代人类。计算机无法显示人类的情感,这也是为什么比起与医生交流,病人更愿意透露更多关于他们病史和症状的信息的原因之一。
c.无法提供详细的诊断和治疗
虽然聊天机器人确实可以与患者互动,并将他们的症状缩小到特定的疾病状况,但这些诊断的有效性水平还不得而知。一些医疗条件可能需要授权的实验室结果才能开始治疗。医疗诊断需要由人工护理人员进行监督,而不应该仅仅依赖于从以前的数据中做出推断的算法。
d.自我诊断和自我治疗的风险增加
医疗保健在对抗自我诊断和自我治疗方面取得了长足的进步。聊天机器人越来越多地被采用,似乎正把我们带回到战争开始的地方。有了健康机器人,患者现在可以购买药物或诊断自己的病情,而无需与合格人员进行身体咨询以获得授权。
见解
事情是这样的。根据几项研究和试验,有精神健康问题的人对聊天机器人反应良好。【3】
这个概念就是人们不喜欢被评判。
当与精神病学家或心理学家交谈时,“被评判”的想法仍然存在,即使他们的目的是在情感上不做评判。研究表明,人们在与聊天机器人或机器人交谈时不会退缩。用户很容易泄露他们最黑暗和最深的秘密,而不用担心他们的秘密会离开那个对话。
你能想象有心理健康问题的病人总是和聊天机器人在一起的情景吗?
医生们知道他们的病人永远不会孤单,会感到轻松一些。聊天机器人可以被编程为识别特定的关键字,以提醒医生或当局患者或患者周围的其他人即将面临的危险。
现在,你可能已经听说了像脸书和 Instagram 这样的社交媒体平台创造了检测自杀和其他精神健康问题(如抑郁症)的算法。通过这种方式,聊天机器人可以使用从与患者的大量对话中获得的文本数据来提供类似的见解。当然,获取如此多的数据需要时间,但首先,运动需要启动。
聊天机器人允许医生通过持续的检查来跟踪病人的进展。
一个例子可能是患有物质滥用障碍的患者。聊天机器人可以询问他们那天是否使用了 T1,可以判断病人的情绪,并询问病人他或她的环境以暗示外部影响。
有了这些信息,医生可以绘制出患者每日和一段时间内的精神健康进展情况。跟踪患者的进展将有助于向医生提供重要信息,如 SSRIs 的功效或它们何时开始工作。医生将能够实时了解处方的药物剂量是否适当。精神药物通常需要几周时间才能生效,选择正确的剂量是另一项措施,必须根据每个患者的具体情况进行滴定。
聊天机器人是一个有用的工具,我们需要利用它们的真正力量尽可能地帮助病人。
尤里·塔兰/Depositphotos.com 提供
结论
聊天机器人将继续存在,并准备好为我们的医疗保健服务水平带来全面提升。虽然他们诊断疾病和为患者推荐治疗方案的能力可能受到挑战,但他们通过连接医生和患者并提供急救护理,成为医学研究的宝贵工具。
聊天机器人在今天的许多领域都是适用和有用的,尤其是医疗保健。我非常欣赏它们为医生减轻的工作量。这样就有更多的时间和病人在一起,而花在管理工作上的时间就少了。
参考文献
1 Palanica A,Flaschner P,Thommandram A,Li M,Fossat Y .医生对医疗保健中聊天机器人的看法:基于网络的横断面调查。医学网络研究中心。2019;21(4).doi:10.2196/12887
[2]菲茨帕特里克·KK,达西·A,维耶希尔·m .使用全自动对话代理(Woebot)对有抑郁和焦虑症状的年轻人进行认知行为治疗:一项随机对照试验。JMIR Ment Heal2017;4: e19。
3 Tombs M. 与机器人分享秘密。AACE,http://www . editlib . org/p/147797/http://curve . Coventry . AC . uk/open(2014 年,2020 年 2 月 22 日访问)。
请留下您对聊天机器人的看法,或者如果您对聊天机器人在医学上的应用有任何疑问,请联系我。
本文原帖www . aim blog . io。如果你想了解更多关于人工智能、医学和新兴技术的知识,可以看看我的其他帖子。
🎲从非均匀分布中选取随机样本
随机抽样教程:
如何在 python,java,javascript 解决的非均匀分布中挑选随机样本?
我打赌你一定遇到过这样的情况,你必须从一组物品中随机挑选一件?这有一个非常简单的解决方案,因为所有物品都有相同的概率被选中。
但是,如果每件物品被选中的概率不同,换句话说,这种分布是不均匀的。这怎么解决?这不是一个很难解决的问题,但是您可能不会发现自己在日常编程工作中经常遇到这个问题。
让我们来看一个例子,并使用 java、python 和 javascript 来解决它。
由“ mynamepong 制作的 flaticon 的图标
🐟我们去钓鱼吧
当你去钓鱼的时候——什么鱼上钩是随机的。做些笔记,你可能会看到某些种类的鱼比其他种类的鱼出现的次数更多——这意味着得到某些种类的鱼的概率比其他种类的鱼高。
今天我们将一起去我们想象中的地方钓鱼,在那里你会发现三种鱼:鳕鱼、大比目鱼和鲭鱼。根据我最近的 50 次捕获,我捕获了 15 条鳕鱼、5 条大比目鱼和 30 条鲭鱼。根据观察,钓到鲭鱼的可能性是 60%,钓到鳕鱼的可能性是 30%,钓到大比目鱼的可能性只有 10%。
halibut: 10% (5)
cod: 30% (15)
mackerel: 60% (30)
🎣钓鱼模拟器
让我们做一个“钓鱼模拟器”来模拟我们的钓鱼之旅。
如果我们有同样的概率得到任何一种鱼,我们可以运行一个随机生成器,给出一个介于 1-3 之间的数字,告诉我们得到的是哪种鱼。Tada!不幸的是——世界并不是这样运转的。那么,我们如何使用非均匀概率分布来模拟鳕鱼、大比目鱼和鲭鱼的捕鱼体验呢?
解决方法是“聚合概率”。如果你想中彩票,你就给自己买尽可能多的彩票。因为当他们叫到一个号码时,你更有可能是赢家。我们会用同样的方式在非均匀分布中选择一个随机数!
因此,如果我们有 10 张票,鳕鱼将得到 1 张票,大比目鱼将得到 3 张票,鲭鱼将得到 6 张票。
halibut: 10% (5) - 10 tickets --> ticket #1
cod: 30% (15) - 30 tickets --> ticket #2 - #4
mackerel: 60% (30) - 60 tickets --> ticket #5 - #10
现在,为了试试运气,我们可以运行一个随机生成器,给出一个 1 到 10 之间的数字,告诉我们得到了什么鱼🎉由于鲭鱼有更多的门票(如有更高的可能性被抓获),它将有更多的机会被选中。
👩💻所以让我们来看看一些代码
你可能有同样的问题想要解决,所以我用不同的语言写了一些不同的解决方案。这只是解决问题的一种方式,每种语言可能都有许多不同的简洁的解决方案。如果你有更好的解决方法,请在评论中与我们分享🙌
Java 语言(一种计算机语言,尤用于创建网站)
在 Java 中,你可以用一个TreeMap
让你很容易地抓到一条随机的鱼。将所有的鱼加在一起,并给出总的概率(例如给出“门票”)。TreeMap
有一个内置的higherEntry
,它将挑选聚合概率严格大于给定随机数的项目。
这里,大比目鱼的价格高达 0.1 英镑,鳕鱼的价格从 0.1 英镑到 0.4 英镑,鲭鱼的价格从 0.4 英镑到 1 英镑。
TreeMap<Double, Fish> fishes = new TreeMap<>();
movies.put(0.1, new Movie("Halibut")); //0.1
movies.put(0.4, new Movie("Cod")); //0.1 + 0.3 --> 0.4
movies.put(1.0, new Movie("Mackerel")); //0.4 + 0.6 --> 1Fish catched = fishes.higherEntry(Math.random()).getValue();
Java Script 语言
在 javascript 中,我们首先通过聚合概率使用map
函数给鱼分配它们的票。然后我们使用find
来寻找最接近随机聚集概率的鱼。
const fishes = [
{type: "halibut", prob: 0.1},
{type: "cod", prob: 0.3},
{type: "mackerel", prob: 0.6}
];const random = Math.random();
let aggregated_probability = 0;const randomFish = fishes.map((fish) => {
aggregated_probability += fish.prob;
return { type: fish.type, prob: aggregated_probability };
}).find((fish) => fish.prob >= random);// outputs random fish
在fishes
上使用地图时,它会给我们一个新的数组,如下所示:
[{type: "halibut", prob: 0.1}, {type: "cod", prob: 0.4}, {type: "mackerel", prob: 1.0}]
find 将在它们的抽奖范围内寻找具有随机抽奖的鱼。例如,如果Math.random()
的结果是0.678
,那么鲭鱼就被捕获了。如果Math.random()
结果为0.0923
,则一条比目鱼被捕获。
计算机编程语言
对于 python,NumPy 已经用“随机选择”为我们解决了这个问题。你输入你的元素fishes
,你要“画”/鱼的次数1
,概率分布[0.1, 0.3, 0.6]
:
import numpy as npfish = ['halibut', 'cod', 'mackerel']
np.random.choice(fish, 1, p=[0.1, 0.3, 0.6])# outputs random selected fish [<fish>]
快乐编码🙌
为地理空间数据丰富选择合适的工具(第 1 部分)
作者图片
随着人类产生的数据呈指数级增长,我们看到越来越多的数据带有地理背景。本文比较了处理和丰富地理空间数据的现有工具和技术。
这篇文章分为两部分。第一部分是对手头问题的介绍。
第 2 部分 重点介绍将要使用的工具,并解释这些工具如何用于解决我们的地理空间数据处理/丰富任务。
从一开始就应该提到的是,我们并不是在寻找像 ArcGIS 这样成熟的地理空间分析平台。相反,对我们来说重要的是:
- 能够导入/加载各种类型的地理文件。
- 解决多边形中的点等问题,例如将用户的地理位置映射到城市的区域。
- 计算各种形状之间的距离,例如从一个位置到最近的购物中心的距离。
- 能够大规模实施。
- 易于与现代数据科学管道集成的工具。
一般来说,我们的任务是面向数据处理/丰富,而不是复杂的分析。
注意:我将跳过任何光栅(如卫星图像)数据相关分析的比较,仅关注矢量数据。
我们应该关注什么标准?
- 您要处理/丰富的数据量。
- 您运行流程的频率。
- 设置所需工具或一般基础设施的难易程度。
- 您现有的基础架构是什么。
- 您的地理数据转换有多复杂。
- 您选择的工具支持哪种空间操作。
- 加载、处理和存储结果有多容易。
- 在您的基础架构中设置和运行作业的相关成本是多少?
现在,为了对以上所有观点给出详尽的解释,我可能会写一本书,所以让我们把重点放在与简单案例最相关的子集上。
我们要看哪些工具?
- Geopandas /带 Dask 的 Geopandas
- PostGIS—PostgreSQL 数据库的空间插件
- 谷歌大查询的地理空间功能
这个列表还远未完成,因为每个主要的参与者都像微软、亚马逊、IBM、ArcGIS 等。拥有地理空间产品。我选择的工具要么是完全开源的,要么(比如 BigQuery)具有特殊的超能力,我稍后会谈到。此外,我们也将跳过类似 Spark 的工具(如 GeoSpark ),因为它们值得单独的文章。
在我们开始之前,如果你是 GIS 世界的新手,你应该阅读下面的部分。
如果没有,可以放心跳过,继续阅读 第二部 。
有史以来最简短的地理信息系统简介
让我们熟悉一些重要的概念,这将有助于我们更好地理解我们面临的任务。请注意,这里做了一些简化,以保持本节简短。
1。空间对象类型
首先,您可能想知道地理空间数据是如何存储在数据库中的。这很简单,GIS 感知工具支持额外的列类型,如点、线串、多边形。这些名字是不言自明的(边注:多边形内部可以有“洞”)。
例如,想象一下地图上的一个点。在数据库中,它可以存储在一个单独的列中,并且“看起来像”:
POINT ( 10 20 )
您还可以存储(在单个数据库单元中)多点、多线、多多边形等对象集合,或者使用更通用的类型,如几何和多重几何。并非所有工具都支持所有类型。
这些六边形中的每一个都是数据库/数据集中的一个独立单元(图片由作者提供)
2。空间参考系统
尽量保持简短,我会说你需要知道有许多平面投影,或者换句话说,你可以将 3D 数据(地球是不均匀的椭球体)叠加到一个平面上。请记住,它们通常不会覆盖整个地球。
你已经知道一个叫做 WGS84(世界大地测量系统)的空间参考系统(它正在被使用,例如 GPS ),并且使用度作为单位。
实际上,你需要意识到:
- 您将使用的地理空间文件(例如,包含美国各州形状的文件)可能使用不同的空间参考系统,并且必须转换到相同的系统中,然后才能将其与您的数据合并。
- 每个空间参考系统都有一个名为 SRID(空间参考标识符)的 ID。例如,WGS84 使用 SRID 4326。您将在工具中使用此 ID 来让“工具引擎”知道您的数据当前使用的投影。
- 对于大多数简单的情况,将所有内容转换为 SRID 4326 应该足够了。
3。几何和地理格式
- 地理表示球体上的数据和平面上的几何数据。
- 您的数据可能是这些格式中的一种,有时您需要在它们之间进行转换,因为您无法测量几何和地理类型数据之间的距离。
- 有些工具只支持其中一种类型的操作(例如,BigQuery 只支持地理)
- 如果您的数据属于几何类型,您需要将其转换为地理类型,以便能够获得有意义的结果,例如,以米(而不是度)为单位的对象之间的距离。
- 几何运算速度更快,但如果您的测量必须非常精确(或运算距离更长),请使用地理。
- 幸运的是,转换很容易,当然如果你的工具支持的话。
- 一般来说,在发现性能问题之前,尽量坚持地理位置。
4。常见数据格式
GIS 数据可以以多种格式存储,因此让我们提及最常用的格式(对于我们的数据丰富/转换案例):
- 当从互联网上获取数据时,你会经常发现。shp (Shapefile)。它还可以存储非 gis 数据(即每个地理形状的属性)。提示:在网上寻找数据时,在你的查询中添加“shp 文件”。
- 熟知文本(WKT)是一种可用于在不同类型的数据库/数据帧之间安全传输数据的格式。简单地说,它是序列化为文本格式的地理数据。请记住,SRID 不在 WKT 记录中。
- GeoJSON 是标准化格式的 JSON,可以在单个文件中存储 gis 和非 gis 数据。
# GeoJSON representation of a point with properties{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2.2945, 48.8584]
},
"properties": {
"name": "Eiffel Tower"
}
}
5。空间数据索引
如果没有空间索引,您的查询可能会慢 100 倍!一定要记住创建它们,尤其是当你要把它和其他表/数据框架合并的时候。
如果使用 PostGIS,您需要手动创建它们。在 Geopandas 中,您可以通过在数据帧上调用 df.sindex 来预先生成它们,在某些情况下,Geopandas 会自动生成它们。
在 BigQuery 中不需要担心索引问题。
同样,这一介绍只是触及了表面(我希望 GIS 专家原谅我的所有简化),但应该足以让您入门。
继续阅读— 第二部分。
为地理空间数据丰富选择合适的工具(第 2 部分)
作者图片
本文是系列文章的第二部分。它重点介绍了这些工具,解释了它们如何被用来解决我们的地理空间数据处理/丰富。
如果没看过介绍,可以在 part 1 中找到。
提醒一下,本系列第 1 部分中选择的工具是:Geopandas、PostGIS 和 BigQuery。我们来说说它们的利弊。
地质公园
- 熊猫的地理空间扩展使用 Shapely (Python 库来操作和分析几何对象)。
- 支持最重要的空间功能,足以解决非常复杂的问题。
- 如果你熟悉熊猫,相对容易掌握。
- 比波斯特吉斯慢。
- 与 Pandas 类似,在单核上运行,但与 Dask(dask.org)一起可以并行化。
- 内置绘图功能(直接在 jupyter 笔记本中),对于非常简单的数据可视化/验证案例来说已经足够。
- 支持最常见的输入/输出格式。
PostGIS
- PostGIS 用空间函数扩展了标准(PostgreSQL) SQL 语言。
- 熟悉一些概念需要时间,但是编写实际的查询非常简单。
- 可以解决几乎任何类似地理空间的问题,甚至支持像最近邻的功能。
- 许多数据加载和转换功能。
- 快速,但查询执行仅限于一个内核。
- 像任何 RDB 一样,可以处理比内存查询更大的数据,但在处理真正的“大数据”时,这可能还不够。
- 有许多相关的开源工具,可以帮助你,例如可视化你的数据(QGIS 是我的最爱)。
- 伟大的文档和在线社区。
- 当与 PostgreSQL 过程语言(PL/pgSQL)连接在一起时,提供了一个非常强大的管道来处理查询,而不会“离开”数据库。
BigQuery 的地理空间功能
- 在谷歌云中处理查询,如果你懂 SQL,你会有宾至如归的感觉。
- 谷歌云生态系统的一部分,可以使用各种 API 轻松加载、转换和导出数据。
- 可以在几秒钟内处理数十亿字节的数据。
- 支持的输入格式:包含 WKT 格式的地理空间列和 JSON 格式的 GeoJSON 列的 CSV(换行符分隔)。
- 内置绘图工具,大数据就绪。
- 按数据存储和查询运行时间收费,这可以大大降低您的成本,即使您正在处理数 TB 的数据。
- 在单个查询中支持 BigQuery 数据和 Cloud SQL(也可以使用 PostgreSQL 作为引擎)数据之间的连接。
- GIS 后的功能较少,但所有必要的操作和分析相关的功能已经存在。
- 没有像 PL/pgSQL 这样的过程语言,但是(一般的 BigQuery)可以在某种程度上使用(SQL) WITH function 来模拟它。
TLDR?
选择 Geopandas if:
- 你对熊猫很熟悉,而 SQL 不是你的强项。
- 想要快速浏览/预处理地理空间数据,并且不打算进行复杂的转换。
- 您的地理空间数据框架不会包含数百万条记录(Geopandas 可以处理“大数据”,但您将等待结果;Dask 可以在某种程度上缓解该问题)。
- 您的用例相当简单,并且您希望尽快投入使用。
选择波斯特吉斯如果:
- Geopandas 还不够。要么是因为你的问题对 Geopandas 来说太难了,要么是它让你等了太久才得到结果。
- 您最终可能会操作更多(大于内存)的记录。
- 你需要一套完整的地理空间功能。
- 您有更多的时间来熟悉比 Geopandas 更复杂的工具。
如果 …您必须处理大数据,请选择 BigQuery!🙂
好了,我们终于准备好做一些真正的工作了。想象一下下面的问题:
你的公司有一个应用,每天生成大量用户与之交互的位置(经度和纬度)。给了你三个不同的案子。
给定用户坐标:
1。分配到全市的区
2。查找到最近的购物中心、公交车站和医院的距离(单位:米)(直线距离)。生成汇总统计数据,例如每个地区的应用交互次数
我们的数据处理/浓缩任务
地理数据的大小
请注意,在任务 1 中,形状(城市区域)的数量可能不会超过数据库中的 100 条记录。
第二个任务是第一个任务的派生物,其中计算的输出具有与城市地区数量一样多的记录。
至于第三项任务,你可能需要对照用户的位置检查超过 100,000 个兴趣点(POI ),但实际的 POI 不会经常改变,可以每周更新一次。
所有这些实际上都是好消息,因为这意味着地理空间数据预处理可以直接在笔记本电脑上使用 Geopandas 或 PostGIS 进行。
地理数据加载和预处理
在与我们的用户位置合并之前,需要什么样的预处理步骤?当然,这很大程度上取决于你的情况,但让我们谈谈最常见的。
首先,你需要获取实际的地理数据(例如代表城市区域的 shapefile 或 GeoJSON ),然后将文件导入到你的工具中(假设它在你的笔记本电脑上)。使用 Geopandas 是一行程序:
注意,在上面的例子中,我使用了 bbox(边界框)参数来“丢弃”定义区域之外的所有数据。函数 read_file()可以做更多的事情,例如直接从互联网上加载 ZIP 格式的文件(详情见https://geopandas.org/io.html)。
下一步是清理数据和改变 SRID 如果需要。
# Changing SRID (projection) **in Geopandas**
# (where "df" is you geodataframe)
df.to_crs("EPSG:4326")# Changing SRID **in PostGIS** *UPDATE my_table
SET geometry=ST_Transform(geometry, 4326)*
然后,一个好的实践(特别是如果数据是从互联网上下载的)是检查形状是否有效(例如,线不与自身相交的多边形)。
# Checking if shapes are valid **in Geopandas**
# (where "geometry" is your geometry column)
df.geometry.is_valid.all()# If it's invalid, you can try following fix:
df.geometry.buffer(0.00001)# Checking if shapes are valid **in PostGIS** # Returns number of valid and invalid geometries
*SELECT count(1), ST_IsValid(geometry)
FROM my_table
GROUP BY ST_IsValid(geometry)*
有时缓冲功能不会修复它,或者您不能使用它,因为您的多边形/多重多边形内部是中空的(使用缓冲功能会删除它们)。在这种情况下,您需要采取额外的步骤,例如导出到 PostGIS 并尝试修复:
# Geopandas supports saving geometry directly to PostGIS but from my experience this is safer solution:
**# 1\. Create additional column with geometry converted to WKT**
df['geom_wkt'] = df.geometry.apply(lambda x: x.wkt)**# 2\. Save dataframe to table "my_table" but export geometry in WKT format instead of actual "geometry" column**
connection_str='postgres://<username>:<password>@localhost:<port>/<db_name>'
df.drop('geometry', axis=1).to_sql(
'my_table', con=connection_str, if_exists='replace',
dtype={'geometry': Geometry})**# 3\. (Inside PostreSQL database) fix the geometry** # Function ST_GeomFromEWKT translates WKT back to geometry
# Function ST_MakeValid fixes geometry
*INSERT INTO my_table_fixed
SELECT ST_MakeValid(ST_GeomFromEWKT(geom_wkt)), <other columns>
FROM my_table***# 4\. Load it back into Geopandas dataframe**
sql = 'SELECT * FROM my_table_fixed'
new_df = pd.read_postgis(sql=sql, con=connection_str)
除了学习如何修复几何图形,现在您知道在这两个系统之间移动数据有多快多容易了。因此,在任何情况下,如果您在 Geopandas 中遗漏了某个函数,您都可以在 PostGIS 中这样做。
最后是问答过程,你可以用可视化数据来检查是否一切正常。在 Geopandas 中,您可以简单地使用。绘图() :
如果这还不够,您可能需要将数据保存回 SHP 或 GeoJSON,并使用 QGIS 等外部应用程序加载它们。
合并数据
在 Geopandas 中,我们需要将用户数据框架转换为地理数据框架:
import geopandas as gpd**# Load users data into Pandas dataframe**
users_df = <function that loads the data>**# Convert to geodataframe**
users_gdf = gpd.GeoDataFrame(users_df,
geometry=gpd.points_from_xy(
users_df.longitude, users_df.latitude))
现在,数据已经准备好与您的用户位置合并。
对于我们的任务 1 来说,简单来说就是:
**# Perform spatial join**
gpd.sjoin(users_gdf, geo_dataframe, how="inner", op='intersects')
PostGIS 中的类似操作(任务 1 ):
*INSERT INTO merged_table
SELECT * FROM geo_table g, users_table u
ST_Intersects(
ST_SetSRID(ST_MakePoint(u.longitude, u.latitude), 4326),
g.geometry
)*# In this line :# *ST_SetSRID(ST_MakePoint(u.longitude, u.latitude), 4326)*
# we are converting our users coordinates to POINT and setting SRID
任务 2 同样简单:
*INSERT INTO count_interactions_by_districts_table
SELECT COUNT(1) user_count, geo_table.district_name
FROM geo_table g, users_table u
ST_Intersects(
ST_SetSRID(ST_MakePoint(u.longitude, u.latitude), 4326),
g.geometry
)
GROUP BY geo_table.district_name*
任务 3 更具挑战性:
*WITH poi_with_point as (
SELECT *, ST_SetSRID(
ST_MakePoint(
u.longitude, u.latitude), 4326)::geography AS geog
FROM poi
WHERE p.poi_type='shopping mall'
)* ***SELECT p.id, p.longitude, p.latitude,
min(ST_Distance(p.geog, u.geog)) shopping_mall_min_distance
FROM users u, poi_with_point p
WHERE ST_DWithin(p.geog, u.geog, 8000)
GROUP by p.id, p.longitude, p.latitude***
其中:
- 带有的顶部查询部分正在生成名为 poi_with_point 的“临时视图”。它通过添加名为“geog”的点列来扩展原始表 poi(包含我们所有的 POI 位置)。它还会过滤掉所有不是商场的兴趣点。
- ST_DWithin 可以显著加快查询的执行速度。它通过增加每个用户与每个购物中心之间的最大距离(任意)8 公里来防止检查每个用户的位置:
用户数据库的大小
当我们的用户数据库的大小是千兆字节时,事情就变得有趣了。
使用 GeoPandas (默认情况下在单线程上)将会花费很长时间,如果数据集超出了您的工作内存,您需要批量处理它。如果您碰巧有 Dask 集群运行在多个 workers 上,那么您可能会考虑使用它,但它可能仍然不是最佳解决方案。
旁注:我已经在我的另一篇文章“丰富你的熊猫数据框架的最简单的方法”中解释了批处理和在集群上运行,这两种解决方案都只需要几行代码。
提示:对于 Dask 集群中的批处理,使用 map_partitions 函数,并在每个分区中分别:加载您的地理空间数据,并对“映射”的用户数据块执行空间连接操作。
PostGIS 将能够处理数据,但执行时间可能不是最优的。如果您的 PostGIS 服务器不是生产环境的一部分,并且您不介意等待,那么您应该使用它。如果没有,我们最后的选择是…大查询
用 BigQuery 做大
首先我们将做几个假设:
1。您已经注册了谷歌云账户并激活了谷歌云存储(GCS)
2。您的用户数据已经加载到 BigQuery 表
3 中。您已经在笔记本电脑上安装了 Google“tool belt ”,命令“gsutil”和“bq”在您的终端
4 中可用。您拥有在 BigQuery
5 中创建数据集和表的权限。您已经设置了(在终端中,使用 Google“tool belt”)您的默认项目
提示:将 GCS 和所有 BigQuery 数据集保存在同一区域,否则您可能无法加载它或执行 JOIN 操作。
正如我们已经知道的,BigQuery 可以导入 GeoJSON 或 WKT 格式的地理数据。我们已经学习了如何将它转换为 WKT,所以让我们将它保存为 CSV 格式并传输到 BigQuery。首先,我们将它保存为 CSV 格式:
# Assuming your geography is already in WKT
df.to_csv('output_file.csv')
从笔记本电脑直接加载到 BigQuery 是可能的,但有一些限制,所以我们将首先把它发送到 GCS:
# (In your terminal)
gsutil cp output_file.csv gs://your_bucket_name_in_gcs
创建数据集,即存储数据并从 GCS 填充数据的表:
# (In your terminal**)
# Create dataset**
bq --location=US mk --dataset your_dataset**# Create table**
bq mk --table your_dataset.geo_table \
id:INTEGER,geom_wkt:STRING,*<other columns>***# Load CSV into BigQuery table**
bq load \
--source_format=CSV \
your_dataset.geo_table \
gs://your_bucket_name_in_gcs/output_file.csv \
id:INTEGER,geom_wkt:STRING,*<other columns>*
BigQuery 语法与 PostGIS 几乎相同:
***# Task 1 in BigQuery*** *INSERT INTO merged_table
SELECT * FROM* your_users_dataset*.users_table u,
your_dataset.*geo*_table g
WHERE ST_Intersects(* ST_GEOGPOINT*(u.longitude, u.latitude),
g.geometry
)*
任务 2 在 BigQuery 和 PostGIS 中几乎相同,所以我将跳过这个片段。
至于我们的第三个任务,请注意得到的行数非常少。不超过区的数量。在这种情况下(或类似情况下,您知道数据将适合您的计算机的工作内存),您可以使用 BigQuery Python 客户端并将结果直接下载到 Pandas dataframe:
from google.cloud import bigquery
client = bigquery.Client()sql = """
*SELECT COUNT(1) as user_count, g.district FROM
**your_users_dataset**.users_table u,
your_dataset.**geo**_table g* *WHERE* *ST_Intersects(* ST_GEOGPOINT*(u.longitude, u.latitude),
g.geometry
)* *GROUP BY g.district* """df = client.query(sql).to_dataframe()
本系列的目标是展示如何使用外部地理空间信息处理或丰富您的数据。我想向您展示如何混合和搭配可用的工具,轻松地在它们之间传输数据,以及哪一个对快速完成您的工作最有用。
AWS 数据分析:数量
图片来自皮克斯拜
查看我以前的文章《AWS 数据分析简介》
本文是我将介绍使用 AWS 进行数据科学和分析的系列文章的一部分。到最后…
towardsdatascience.com](/data-analytics-with-aws-introduction-975143d11ce4)
大数据中有大
有人会说,数据无论大小都是数据,所以人们称之为大数据肯定有充分的理由!好吧,去喝杯茶/咖啡放松一下,因为我们将看到一些令人头疼的数字。
https://www.bondcap.com/report/itr19/#view/151
我们今天所知的数字世界正在产生海量数据,人类已知数据的 90%是在过去三年中产生的。这些数据被称为全球数据圈,并且正在迅速扩大。有机构预测将从 2018 年的 33 Zettabytes (ZB)增长到 2025 年的 175 ZB 。
这一切怎么可能?我们是如何产生这些数据的?
下图显示了数据传播模式。外圈是网络边缘的端点,如人们的设备、移动设备、物联网、行业传感器……边缘圈代表本地数据收集器和存储,如企业服务器和小型数据中心。这种模式的核心是大型数据中心和云。
我想就公共云(即 AWS、Azure、GCP 等云提供商)说几句话,以强调它的重要性。数据存储越来越多地向核心转移,到 2024 年,预测表明存储在云中的数据将是存储在终端中的数据的两倍(现在是 50-50 倍)。因此,个人和企业需要熟悉最终将存储所有数据的云。
这个产生数据的怪物必须用适当的工具来满足和照顾,否则它会生气并破坏一切!这就是我们接下来要探索的。
数据驱动的世界将永远在线、永远跟踪、永远监控、永远倾听、永远观察,因为它将永远在学习,IDC。
****数据量可能是当今数据最重要的特征之一,因为数据量塑造了我们今天所知的许多 IT 和云解决方案。我们在 90 年代处理的信息系统问题与我们在过去十年中面临的数据量激增的问题完全不同。
机器学习系统的存储、数据查询、分析、大规模并行计算和数据消耗(仅举几例)都应该以不同的方式建模。
既然我们已经了解了大数据系统与其前身有着天壤之别,那么让我们来探索类似大数据系统的数据分析解决方案的主干,并与 AWS 解决方案进行对比。
数据湖
数据可以分为不同的类别:非结构化、结构化和半结构化。生成的大部分数据是非结构化的,约占 80%,而结构化和半结构化数据各占 10%(这一主题将在另一篇文章中深入讨论)。
企业必须拥有一个能够接收、管理和分析大规模数据的数据管理平台。这就是为什么数据湖的诞生是为了解决企业在组织不断增长的数据量时所面临的一些问题。
那么什么是数据湖呢?
这是 AWS 的定义。
数据湖是一个集中式存储库,允许您存储任意规模的结构化、半结构化和非结构化数据。
数据湖(DL)是一个集中存放所有数据的地方,可以是任何形状、任何规模,并允许您轻松地处理这些数据。这是一个架构概念,通过一套工具来帮助管理您的数据生态系统。
一个合适的 DL 应该具有以下特征:
- 真相的单一来源:进入你的数字图书馆的数据结构必须有某种组织
- 存储任何类型的数据:存储在 DL 中的数据必须用于为企业带来价值,必须培训用户将 DL 的使用集成到他们的工作流程中,并且必须定期更新数据
- 数据持久性:即使发生灾难(例如数据中心被洪水淹没),DL 也应该能够存储和保存数据。SLA(服务水平协议)应该接近 100%,否则为什么要在您不确定要保存数据的 DL 上工作
- ****轻松分析您的数据:集中您的数据的目的是通过应用数据分析和机器学习来检索业务洞察力。
为了在确保上述特征的规模上操作,实现这一点的技术挑战是相当重要的。想象一下,有数百万个 CSV 文件,您想对这些数据进行聚合?嗯,这不是一个简单的任务,你可能要等几个小时,甚至几天,才能得到结果。这就是像集成在 DL 中的分布式系统这样的技术可以提供帮助的地方。
因此,让我们探索一些有助于实现这一目标的基础技术。
分布式架构
分布式体系结构管理一个资源池,并将负载分布在每个资源上,以便更快地大规模执行。
通过并行执行任务来分配负载,图片由作者提供
为了说明分布的威力,在上图中,我们使用了一组工作人员并行执行一项任务(将数据存储在数据库中)。因此,我们没有让一个工人来完成任务并等待,而是同时使用了 8 个工人,我们几乎在瞬间就得到结果。这种架构的好处是,如果我们需要增加更多的工人来承担更多的负载,这就像添加一个乐高积木一样简单。
分布式架构的工作方式是一样的,有一个主节点发出执行某个计划的命令,从节点并行执行这项工作,分担负载。每个节点管理一小部分负载,在执行结束时,收集和汇总结果。
我们可以将这些系统比作蜂箱(蜂箱是 Hadoop 生态系统工具的名称并非巧合),由女王和一群并行工作的工人管理。
一个非常著名的分布式系统是 2006 年发布的开源项目 Hadoop。最初由 Google (Google 文件系统项目)开发,然后开源,它便于使用计算机网络来处理大量数据。简而言之,当你在 HDFS(HadoopDdistributedFfileSsystem)中存储一个文件(或其他形式的数据)时,系统会将它分成小块,并通过复制将它分布在集群的所有节点上,这允许跨节点分布计算。
AWS 上的数据湖
AWS 建立了一个生态系统,使企业能够创建和管理数据湖和数据仓库。让我们探索一下 AWS 生态系统的选项。
亚马逊 S3
第一批 AWS 服务之一是名为“简单存储服务”或 S3 的存储服务。这是一个可以存储任何规模的半结构化和非结构化数据的地方。S3 将数据作为对象存储在桶中。
把 S3 想象成 Dropbox 或者 google drive,但是有非常高的可用性和额外的工具(REST API,元数据目录..).
S3 就像一个乐高盒子,你可以在房子的不同房间里有多个盒子,里面装着不同形状(数据格式)和不同颜色的乐高玩具(元数据)。当你想用所有这些组装一个乐高房子时,你必须抓住正确的盒子,寻找正确的积木。
S3 的不同之处在于,你可以准确地知道每块乐高积木在哪里(通过它的键和元数据),这样你就可以立即组装你的乐高房子。此外,S3 永远不会失去你的乐高,因为 AWS 可以坚持。
一个对象由一个文件和任何与该文件相关联的元数据(例如,允许我们组织和查找对象的标签)组成。它可以是 CSV、pdf、视频、音频或二进制文件,基本上是你能想到的任何格式的数字数据。S3 中的每个对象都有一个键,它是访问对象的 URL。
****桶是保存对象的容器,基本上就像操作系统中的文件夹。创建存储桶时,我们可以指定许多选项:
- 将托管存储桶的地理区域
- 谁(个人或服务)可以访问、创建和删除对象
- 管理对象生命周期和版本
S3 的主要优势:
- 将存储与计算和数据处理相分离:这意味着数据的存储和消费是在不同的位置完成的。为什么会有意思呢?它使数据能够由不同的工具处理,如果计算服务器遭到黑客攻击,它们将无法直接访问数据,存储和计算可以独立扩展(成本和速度优化)。
- 集中式数据架构:促进数据治理并降低成本。
- 与其他 AWS 服务的集成:计算、业务分析和机器学习应用程序可以轻松访问 S3 数据,无需第三方连接器。
- 标准化应用程序编程接口(API):可以通过不同编程语言的 REST APIs 通过代码访问和使用数据。
S3 的主要劣势
- 根据使用情况,该服务可能会很贵(尤其是大数据传输)
- 它必须与其他 AWS 服务一起使用来查询、可视化和处理数据。所以作为一个独立的解决方案,它是不够的。
那么,有了所有的功能和优势,S3 是一个数据湖吗?
S3 是数字图书馆的核心组成部分,但它本身并不是数字图书馆。记住我们在本文前面说过的,一个合适的 DL 是一组支持数据接收、存储和转换的工具。S3 必须与其他 AWS 服务相结合,以形成一个 DL,如用于接收数据的 Kinesis、用于转换数据的 Glue 或 EMR 以及用于查询数据的 Athena。
数据仓库
数据仓库是一种非常流行的数据存储方法。它由一个中央存储库组成,该存储库包含已经清理、转换和聚合的结构化数据,可用于业务分析和报告。
它主要关注来自不同来源的结构化数据,然后在存储到 DW 之前经过 ETL 管道(提取转换加载)。数据的结构是严格的,这意味着在接收之前必须定义一个模式。
****为什么它不同于数据湖?数据仓库仅存储用于业务分析的结构化数据。它通常用作存储所有原始数据的数据湖的补充解决方案。
大多数商业智能工具,如 Tableau、Qlik view、Power BI……都是由后端的数据仓库提供支持的。
数据集市
存储在 DW 中的数据可能非常大,大到有时我们需要等待几个小时才能得到一个查询的结果。
想象一下,一家银行在全球范围内存储跨国数据数年,而业务分析师仅频繁使用某个国家去年的数据。让这些分析师能够访问和分析全球数据的某些部分是有意义的。这就是数据集市(DM)发挥作用的地方,它们是主 DW 中的表的子集,支持对特定主题进行快速而经济的查询。
作者照片, Smashicons 、 Phatplus 设计的图标来自 Flaticon
现在我们已经了解了什么是 DL 和 DW,让我们来看看 AWS 必须提供什么来实现这样的系统。
AWS 上的数据仓库
AWS 已经开发了自己的 DW 解决方案,作为一个完全托管的、Pb 级的数据仓库服务,在云中被称为 Amazon Redshift 。这意味着建立一个数据仓库从未如此简单,不需要管理和维护服务器,AWS 做了一切。它有起有落,但是能够在几分钟内建立一个完全可操作的 DW 显然是一个优势。
AWS 生态系统提供了很大的灵活性,可以将 Redshift 与 S3、Lambda 函数和计算实例等其他服务集成在一起。基于这些服务,可以快速构建和定制完整的商业智能解决方案。
赞成的意见
- 易于设置、部署和管理
- 安全的
- 快速扩展以满足您的需求
骗局
- 根据企业的规模,费用可能会很高
- 亚马逊声称它比其他数据仓库解决方案快 3 倍,但没有明确的基准可以找到。性能更多地取决于您如何配置集群
- 与其他数据仓库解决方案(如 Google 的 Bigquery)相比,建立和优化数据仓库的学习曲线相当陡峭。它并不像人们想象的那样管理完善。
AWS 提供了一个优秀的生态系统,可以快速开始数据分析。他们不断创新和添加新工具,显然是云计算领域的领导者。随着数据实践变得越来越大众化,如果没有公共云提供商,企业很难寄希望于这一趋势,因为公共云提供商可以轻松部署现成的解决方案。问题在于包括 AWS 在内的云提供商,它们拥有锁定客户的封闭系统,一旦做出选择,就很难切换到其他系统。但毫无疑问,云提供商加快了数据领域的创新速度。
现在,我们已经讨论了 AWS 中的数据量和存储解决方案,在下一篇文章中,我们将讨论如何消费所有这些数据来做出有意义的决策,这一特征被称为数据的速度,或数据处理。
所以,敬请期待下一篇文章。
参考
- https://www . Seagate . com/www-content/our-story/trends/files/IDC-Seagate-data age-white paper . pdf
- **【https://www.bondcap.com/report/itr19/#view/151 **
- https://www . data mation . com/big-data/structured-vs-unstructured-data . html
- https://www.aws.training/
想象我在 4 分钟内和熊猫一起打滚
对 Pandas DataFrame.rolling()的简短介绍,它提供了一种执行滚动窗口计算的简单方法。
几个星期以来,我一直在研究一个用于机器学习的足球数据集。我的特色之一是比赛日统计,比如射门、角球和控球。当然,我不能得到相应比赛的数据来预测它,因为它只能在赛后得到。所以我想用前三个比赛日的平均值来预测下一个比赛日。我的第一个想法是编写一个循环来聚合数据,但后来我发现了更好的东西:pandas.DataFrame.rolling.
问题
我在介绍中已经说过,我正在研究一个足球预测模型。想象以下数据帧:
我有匹配统计数据,这是我的特征,也是我想用算法分类的目标。但是我不能使用这种形式的比赛统计,因为我在比赛前没有这种信息。因此,我想使用最近 3 个游戏日的平均值作为一个特征。
一个可能的解决方案
我的第一个意图是用以下逻辑编写一个循环:对于行 x ,我们对行 x-3 到 x-1 求和,并将结果除以 3:
正如你所看到的,这个循环花了 2.64 秒完成了 31 行。由于我的数据集包含大约 9000 个游戏,这个解决方案对我来说太耗时了。不管怎样,我至少得到了想要的结果:
更好的解决方案
我做了一些调查,发现了熊猫。DataFrame.rolling 这应该是我要求的完美解决方案。它提供了滚动窗口计算的选项。滚动窗口背后的基本概念是定义一个特定大小的窗口,在其中执行特定的计算。它常用于时间序列分析。在 Pandas 文档中,您会发现以下参数:
为了重现第一次循环的结果,我们只需要定义窗口的大小。我们还需要添加 shift (1 ),以便以 1:
df_chelsea['Goals Team 1_avg'] = df_chelsea['Goals Team 1'].shift(1).rolling(3).mean()df_chelsea['Goals Team 2_avg'] = df_chelsea['Goals Team 2'].shift(1).rolling(3).mean()df_chelsea['Shots Team 1_avg'] = df_chelsea['Shots Team 1'].shift(1).rolling(3).mean()df_chelsea['Shots Team 2_avg'] = df_chelsea['Shots Team 2'].shift(1).rolling(3).mean()
结果与上一节完全相同:
执行只用了 19 毫秒,比之前的循环少了很多。如果我们定义了 min_periods 参数,我们还可以填充一些“NaN ”:
df_chelsea['Goals Team 1_avg'] = df_chelsea['Goals Team 1'].shift(1).rolling(3,min_periods=1).mean()df_chelsea['Goals Team 2_avg'] = df_chelsea['Goals Team 2'].shift(1).rolling(3,min_periods=1).mean()df_chelsea['Shots Team 1_avg'] = df_chelsea['Shots Team 1'].shift(1).rolling(3,min_periods=1).mean()df_chelsea['Shots Team 2_avg'] = df_chelsea['Shots Team 2'].shift(1).rolling(3,min_periods=1).mean()
结论
如果您觉得用 Python 分析数据花费的时间太长,通常有一种更有效的方法。这里的情况也是如此。Pandas 提供了一个内置函数来执行滚动窗口计算,而不是一个简单的循环,这要舒服得多,并提供了许多附加功能。它也比循环耗时少,执行时间只占循环的 0.7%。
如果您喜欢中级和高级数据科学,并且还没有注册,请随时使用我的推荐链接加入社区。
图片文本:文本的交互式视觉效果
厌倦了单词云?
PictureText 用最少的代码将简短文档的列表转换成交互式树形图
可用的和与我们相关的书面文本的数量是惊人的,并且像这些天的大多数事情一样,呈指数增长。然而,我们用来阅读的工具基本上保持不变。
当研究一个话题、阅读新闻或试图获得一个事件的最新消息时,我们倾向于遵循一个 T 形过程。
我们想从大量的文献中了解:
- 他们对最感兴趣的是什么?一般是怎么回事?
- 什么是的主旋律?
- 在粗略的描述之后,我们倾向于迅速深入我们感兴趣的东西。
- 理想情况下,记录我们读到的内容,我们努力保持在正确的轨道上。
- 拉起,潜 再进入一个狭窄的话题。
- 重复…
相当于信息收集的狩猎采集者。
解决这个问题将是我们消费信息的一个巨大进步,而我在这篇文章结束时肯定无法解决这个问题。然而,我的目标是提出一种向前迈出一小步的方法。
对此我们能做些什么
我在这里提出的是一个实用工具,它可以帮助我们快速浏览大量的文本。我称之为 PictureText(傻名字但是 WIP)。
PictureText 用最少的代码将简短文档的列表转换成交互式树形图
给定一组短文档(比如新闻标题),它可以将它们分成语义上属于一起的层次组。交互式树形图允许读者通过更深入地研究层次结构来更详细地探索每个组,并在需要时动态地抽出。
该方法旨在对大量非特定领域短文本进行分组。例如:新闻标题、自然语言问题和社交媒体帖子都是不错的选择。
这在很大程度上是混合使用三种工具的结果,我将在后面详细讨论。这主要归功于:SBERT 、 plotly 和 fastcluster 团队,因为我只是简单地将这些碎片拼接在一起。
查看 colab notebook 和 GitHub 获得文章中看到的结果和示例。
一种动机…各种各样的…
我记得几十年前,当我第一次去健身房的时候,教练会告诉我如何举起东西,这几乎是所有的理论。他们大多从他们的老师和个人经历中知道这些答案。显然,其中一些是错误的,并会产生意想不到的后果。
顺便说一句,那时,我经常去健身房,因为我认为那是让女孩更感兴趣的方法。现在,我知道写好代码是实现这一目标的方法,所以我不再去健身房了。
无论如何,至少,那时你会有一个关于做什么和如何做的有限的讨论,并且可以转移到实际的练习。现在,有博客和不同的度量标准和理论等等。它真的永远不会结束。这是…伟大的:进步和所有这一切,我喜欢它,不要误解我。(不想激怒数字大神不然他们会把我放在无尽卷轴的最底层!)
好吧,回到书呆子的话题。重点是:太多的文本,我们想要更少的文本,但保留质量和理解。例如,到现在为止,Medium 已经知道我想读什么,并鼓励我去读。但仅此而已吗?是大部分吗?还是只是冰山一角?无从得知。我最想知道的是什么?难道我甚至知道吗?(数字神显然知道,但我也想知道)
简单的例子
首先,我将“借用”一下 SBERT 文档中的集群示例。考虑这些句子
[
‘The cat sits outside’,
‘A man is playing guitar’,
‘I love pasta’,
‘The new movie is awesome’,
‘The cat plays in the garden’,
‘A woman watches TV’,
‘The new movie is so great’,
‘Do you like pizza?’,
‘Burgers are my favorite’,
‘I like chips’,
‘I will have french fries with my burger’
]
他们代表了我们讨论的问题的一个最小的例子。我们可以看到肯定有一个“食物”话题,一个“电影”话题和一些“其他”话题。我还用一些我最喜欢的快餐的例子稍微扩展了例子。
使用 ML 和 NLP,有许多方法可以对它们进行分组。例如,通过无监督学习,我们可以根据某种表示将它们分配到相似的组中(这里使用 SBERT )。如下图所示,每个方框的大小反映了每个组的大小。每一组都用属于它的一个句子来表示(后面会详细介绍)
在上述示例中使用最近邻聚类的树形图结果
这样的分组已经需要一些假设,而且可能会出很大的差错。例如,颜色编码旨在表明摘要标题与每个组的匹配程度,蓝色表示良好,红色表示不太好(更多细节请见下文),您可以看到,对于较大的组,很难获得良好的匹配,摘要也很难提供良好的代表性。
我们没有看到的是,提到的比萨饼和意大利面也是“汉堡”组的一部分。另外,“女人看电视”被错误地分配到右边的“猫”主题,但是我们看不到那个。在现实世界中,我们每一组都有成千上万个例子,所以要弄清楚一些提到的“猫”和看电视的女人有什么关系就更加困难了。
层次凝聚聚类
大多数常见的聚类方法都将到此为止。然而,如果我们能够深入到一个兴趣组,并看到一些更细粒度的细分,将会有所帮助。这就是 HAC 可以提供帮助的方式。它使我们能够迭代地将每个观察结果与其最接近的组分组,参见下图通过 HAC 获得的相同示例。
例句的 HAC 树状图
这非常有用,因为我们现在不仅知道句子是如何组合在一起的,还知道“汉堡”的句子和“电影”的句子在某种程度上更接近。“汉堡”首先与它们联系在一起,然后与比萨饼和意大利面食关系最密切。我们越向右移动,事物之间的距离就越远,也就是说,根据 HAC(你知道,常识),顶部提到的两部电影之间的联系比不同汉堡之间的联系更明显——电影之间的联系基本上是等同的,而后者谈论食物,但却是非常不同的陈述。
请注意,我们不再有任何特定的群集,在每一步,我们只是通过将两个最接近的组或单独的文本片段相互连接,将整体组的数量减少一个。没有1 级、2 级等的明确概念。
我们可能会尝试总结一些章节,但不清楚具体使用哪一个来这样做。
输入图片文本
考虑一下使用 PictureText 会是什么样子。
它使用之前的树状图(也称为树状图),并且将一个层分配给数据的一些固定数量的分割。例如,如果我们希望我们的数据在每一层中分成三个一组,我们将从从右到左沿着树状图开始,直到我们找到第一个好的三分之一(如果三分之一很小,例如,只包含一个句子,我们可能会继续分成 4 个或 5 个,直到我们有三个适当大小的分)。接下来,我们简单地在我们找到的每个组中重复这个步骤,直到我们希望这个分裂继续下去,或者直到我们用完所有的句子(实际上,我发现大约 6 次分裂耗尽了我的注意力范围,并且得到了结构良好的小组。请注意,由于这里的幂律增长,在 6 次拆分后,我们将有大约 700 个 3⁶集群,这是…相当多的一张图片。
每组由上图中的一个方框和一个标题表示。在内,我们将有下一组 3-5 个分割。
- 注意颜色编码。范围在深红和深蓝之间,其中强度代表群体内部一致的质量。非常红表示不好,平淡无奇的颜色表示一般,非常蓝表示非常好。
- 每个组的大小与每个级别的成员数量成正比。未来的一个选择可能是添加提供您自己的重量的功能。
正如你可能想象的那样,这个组合远非完美的。因为我们没有以任何明确的方式告诉算法如何对标题进行分组,所以我们只是使用文本表示提供给我们的语言和语义知识来进行分组。有用的是看什么时候我们对分组很有信心,什么时候不是。在无监督的情况下,我们可以使用聚类成员的相似性。如果平均来说所有成员都非常类似于摘要,则摘要也是该群体的良好代表,反之亦然。
方法概述
这部分比较专业,如果你想看一些例子,你可以跳到下一部分。
图片文本管道
- 执行任何必要的预处理,以获得文档字符串列表
- 使用选择的方法嵌入/编码所有文档,默认情况下我使用 SBERT
- 使用 HAC 获得每个点到其余数据的分层分配的“链接”表。这里我使用 fastcluster ,默认为病房联动。
- 通过为每个层选择一个粗略设置的分割数,迭代地创建“层”
- 为每一层生成摘要。在默认设置中,我使用最接近集群平均值的点。使用聚类的平均值来表示其质心用于许多少数镜头、无监督的设置,特别是在( Snell 等人,2017 )中
- 使用 plotly 的树形图进行交互式可视化
代码库使用一些合理的默认设置。“自带”电池选项允许您使用自己的方法来表示文本(即文本嵌入),以及总结文本的方式,并调整您看到的层和每层的聚类数。github repo 中的更多示例。
一些结果
希望在这个阶段这一切仍然有意义。我们现在将做同样的事情,但是使用一些真实的数据。幸运的是, tensorflow datasets 为我们提供了大量的数据集,让我们快速入门。
可视化的方法在对大量非特定领域的短文本进行分组时效果很好。例如:新闻标题、自然语言问题和社交媒体帖子都是不错的选择。
我将在这里分别使用来自数据集的示例:
- CNN-DailyMail 数据集——可视化每篇文章的摘要
- 小队数据集——可视化问题
查看 colab notebook 和 github 的结果和例子,你可以跟着做
新闻
CNN-DailyMail 是一个经过充分研究的摘要数据集,由新闻文章和“亮点”文本组成,作为摘要基准,通常由 1-3 句话组成。这是我们想要做的一个很好的候选。
我从数据中提取了前 10k 个“亮点”,只选择了它们的第一句话,然后在上面运行 PictureText。
顶级分割显示大多数部分为浅琥珀色,这意味着低于平均组内相似性。右下角的小文档似乎更有希望,有大约 500 个(约 5%)关于“体育”主题的文档。
放大到“体育”主题,我们看到更多关于足球(真实的足球)和网球等的比赛结果。
一般来说,颜色编码蓝色应该工作得很好。
很难从最大的主题中提取意义,因为它们目前用一句话概括了一大块 1-2k 的文章。然而,挑中间的一个,里面的内容有一些相似之处。黑客和骗局的主题似乎出现了:
- 黑客组织称获取了 snapchat 用户的信息
- 公司使用诡计获得额外的税收减免
- 脸书聊天工具可以让你插入朋友的面孔
- 等
也有很多情况下,事情没有什么意义。同一张图片的中间部分混合了奥兰多公园袭击事件、英国烘焙大赛等新闻。
一般来说,处理这类问题的方法是更深入地研究一个组,直到出现一个更“蓝色”的标题集合,这一个更有意义。
问题回答
小队数据集是评价问答模型质量的标准之一。然而,很难很好地理解我们作为一个整体在处理什么样的问题,什么行得通,什么行不通。我们可以对个别问题进行抽样,或者做一些单词分布等。但是没有什么能简单地给出一个好的概述。
类似的结果可以从问题中看出。在顶层,根据前三个桶中包含的问题来判断,这种划分没有什么意义。
然而,深入其中一个“哪个时代利用了希腊理论”,我们看到了许多关于历史时期的问题。更深入地挖掘,似乎也有一些更狭窄的主题。例如,你可以关注斯拉夫/俄罗斯的时间线和事件。然而,在另一条线索中,我们从历史话题跳到关于电视历史的问题,比如“第五季是什么时候播出的?”…所以要小心…
(自我)批评
总体来说,我真的很喜欢这个结果,这个工具用几行代码就完成了很多工作。我希望它能对可视化、数据探索甚至一些关于数据内容的讨论有用。然而,它显然有其局限性,这里有一些使用时要记住的事情。
领域特异性
一般的基于 BERT 的模型倾向于在来自互联网的数据上进行训练,因此它们并不适合特定领域的使用。我做的一个实验是用 CORD 数据集,一个新冠肺炎研究论文的数据集。起初,结果似乎非常好,因为许多集群的相似性平均超过 80%,这表明集群内的一致性非常好。
使用来自脐带数据集的清理过的 bioarhiv 标题的 PictureText。总的来说,很多标题使用了很强的技术术语,这使得使用起来非常不可靠。例如,领域特定的嵌入是可用的,它可能被用来获得更好的结果。
然而,从实际结果来看,这些文章似乎没有什么共同点。脐带数据集显然集中在医学领域,即使在这一领域,它也非常局限于病毒学。我推测该算法发现了许多未知的标记,这可能会导致误导性的结果。
技术的
数据科学部分会一如既往地出错。我已经包含了非常基本的默认方法,并认为这些方法可以根据需要替换为更复杂的方法
- 短文本。设计工作使用的工具是句子级的(SBERT),而且,BERT 模型很快淡化了更长系列的意义。我预计,随着文本变长,这个工具的有用性会很快下降。它主要用于单句文本,如:自然问题、新闻标题、推文等。
- 总结。默认采用小组总结简单但有局限性。那里有各种各样的方法,一般打着多文档摘要的旗号。让我非常高兴的是,我在解决方案中有一个明确的位置来使用它们,所以可以在那里做很多实验。特别令人感兴趣的是还可以提供对概要质量的评估的方法,因为这些方法很好地符合按质量对瓷砖进行颜色编码的解决方案。我对 SUPERT 感到非常兴奋——它也利用了 SBERT 并提供了自己的摘要质量指标,然而,它似乎在计算时间方面有一些开销。
用法/UX 批评
- 这可能会非常糟糕。在每一步,该工具都显示了从全部文档中抽取的一小组示例。在大多数情况下,我们只会看到层次结构中的 3 段文本。顶部的集群是我们首先看到的,并产生第一印象。同时,尽管这些是质量最差的。最高级别的总结肯定是对其内容的歪曲,因此深入挖掘是一个先决条件。
- 可引起头痛。在使用它一段时间后,动态地上下移动层次结构确实有点令人着迷。然而,我很快就会忘记类似的物体在哪里。明确地说,例如,当阅读一个“体育”话题时,很可能会发现一小群政治话题,反之亦然。事实上,一个主题的良好一致性仅仅始于 80-90%的内在相似性
未来的工作
为了解决上述问题,以下是一些可能的方向:
- 确定有意义的衡量标准,看起来什么样的“好”适合层的选择。我一直在寻找一种方法来捕获每层的合理数量的簇,即最好是 3–6 个。从视觉角度来看,这可能行得通,但对质量可能是有害的。其他常见的方法要么使用我们上面看到的树状图的高度,要么寻找一个与 HAC 相当的肘状。
- 速度——我只想说:在(免费的)谷歌 colab GPU 上,10k 个句子需要 30 秒,100k 个句子就打破了 colab 的 RAM。这不是处理 twitter 的方法,但对于一天的新闻标题来说会很好。
- 评估不同聚类方法的影响。班布里克等人(2020) 使用完全连接,而不是我在这里使用的沃德连接。在我考虑的大多数情况下,后者似乎更好,然而,我的目标是提出比“眼球”分析更正式的东西
- 能够设置树形图大小——以反映用户的偏好,而不仅仅是文章的数量
- 文本大小对于较长的文本来说很糟糕,plotly 没有文本换行设置,并且一直在缩小文本,所以它很快变得不可读。互动有助于解决一些问题,但还可以做得更多
结论
我开发了一个简单的工具来产生文本的层次聚类,在分组的每个级别上生成简单的摘要,并利用树形图工具来可视化结果。我认为这是一个强大的方法,无论是对数据科学家,还是对任何想要快速探索大量文本集合而不需要太多定制的人。开放工具,如通过 SBERT 的上下文文本表示,通过 fastcluster 的层次聚类,以及 plotly 驱动的交互工具。
查看 colab notebook 和 GitHub 中的结果和例子。
任何这类工具都有许多限制。特别是,开始时生成的一些聚类可能会完全混淆。
然而,我希望它对许多人有用,因为它是由我的一个长期任务驱动的——阅读时点击东西……
特别感谢 Rich Knuszka 的宝贵反馈。
希望这是有用的或…好奇的(好的或坏的好奇)。感谢您的阅读。如果你想打招呼,请通过 LinkedIn 联系
通过 NLP 和数据可视化描绘特朗普的弹劾
一个故事分析器仪表板,显示故事的主要人物及其互动
欢迎来到 Story Analyzer ,这是一个将自然语言处理与数据可视化相结合的应用程序,它展示了这样一句格言:“一张图胜过千言万语。”
我是一名大学教授,也是一名程序员。我教计算机信息系统,主要是数据库、编程和商业智能。在过去五年的研究中,我一直在开发一个软件应用程序,我称之为故事分析器,它使用自然语言处理(NLP)和数据可视化来展示“一图胜千言”这句格言。自去年夏天以来,我已经将 Story Analyzer 应用于几份公开的政府文件,这些文件讲述了与特朗普总统被弹劾有关的事件。通过这一努力,并在学生研究助理的帮助下,我为穆勒报告、众议院弹劾报告、霍洛维茨报告、几位弹劾证人的开场陈述以及政府提供的其他公开可用的弹劾相关叙述和文字记录生成了几十个仪表板。在这篇文章中,我想分享和解释这些仪表板,它们可以在http://storyanalyzer.org/找到。
我刚接触媒体和数据科学,我很高兴有机会宣传 Story Analyzer 和这些仪表板。我在詹姆斯·麦迪逊大学教书,那里的使命包括公民参与,我希望我的努力能效仿这一理想。我邀请你来看看我的网站,玩玩仪表盘。
术语“故事”是指涉及人、团体、组织或其他实体(主体)执行可以影响其他人、组织或实体(对象)的行为的叙事。这些事件发生在特定的地点和特定的时间,并且它们可能包括其他感兴趣的上下文特征。术语“故事分析”与识别故事的这些关键元素(主题、对象、动作、时间、地点和其他上下文)有关,此外还代表故事中发生的每个事件的这些元素之间的关系。Story Analyzer 有助于直观、交互式地回答以下问题:谁对谁做了什么,在何时何地发生的,以及当时还发生了什么?
事情发生在哪里?
使用 Story Analyzer 仪表板非常简单。将鼠标悬停在元素上,或者单击。通过这种方式,你可以浏览整个故事,关注一个人或一群人,一个地点或时间,互动和关键词。由 NLP 过程和复杂图形构成的丰富信息内容有助于用户理解冗长而复杂的叙述。
“了解全局”的概览仪表板
下图简要描述了这款 app 涉及的算法和流程。我的软件位于斯坦福大学 CoreNLP 的肩膀上,还有来自 Google 和 D3 的可视化库。下图显示了 Story Analyzer 的总体架构。你可以从网站上看到关于我的软件的更多细节。
故事分析器系统架构
我希望你喜欢玩我的仪表板。我也希望他们提供有见地的信息,帮助你理解这个非常复杂的故事,这个故事可能会在华盛顿特区继续发展
饼图被认为是有害的
数据可视化
有没有更好的数据呈现方式?
好的图表能让读者更容易理解数据。饼状图经常不能通过这个最简单的测试。
事实上,耶鲁大学的统计学家、艺术家和名誉教授爱德华·塔夫特曾说过,比饼图更糟糕的设计只有几个,并认为在大多数情况下简单的表格更好。
让我们看一个例子。下面的饼图代表了一年中每个月的降雨量。
12 个月的降雨量
哪个月雨量最多?三月,对吗?然后是 11 月,还是 12 月?四月和五月哪个时间段更长?琼出了什么事?到底下了多少厘米的雨?
我是否因为歪曲了可怜的旧饼状图而有罪?它可以画得更好。例如,它可以有标签来显示每个月的实际降雨量。
但是这样不是更好吗:
12 个月的降雨量
一个简单的柱形图:底部是月份,y 轴是厘米数。诚然,它并不丰富多彩,但是您可以很容易地看到每个月的数值以及一年中每个部分的相对降雨量。
饼状图的问题在于,我们更容易注意到条形长度的差异,而不是看到圆形各部分的差异。解释条形图中的数据比解释饼图要简单得多。
不过,有一件事饼状图相当擅长。如果您有少量相当不同的值,饼图可以很好地显示差异。
这是三个政党在过去 6 个月里的一组完全虚构的民调数字。
这是一张代表一月份数据的饼状图。这是一个不错的代表,你可以清楚地看到他们之间的差异,但没有显示实际数字的标签,你只能猜测投票给每个政党的选民比例。
下面是绘制相同数据的柱形图。它同样清晰,但有一个优点,即条形是并排的,因此您可以更容易地看到它们之间的差异。它还有一个优点是有一个清晰的比例,这样你就可以估计出打算为每个政党投票的实际人数。
现在让我们把事情复杂化一点。在下面的表格中,我们有另外两个类别,一个代表第四个政党,另一个代表其他较小政党的选民。
这是新数据的饼图。还是不错的。很难区分最后两组选民,但他们之间没有太大的区别,所以这没什么大不了的。
那么,让我们来看看等效条形图。
我认为这是一场平局。他们之间没什么可选择的。
但是,如果我们想要表示整个表— 6 个月的数据,该怎么办呢?
我们将返回到第三方数据,使事情变得简单一点。这是一组代表六个月数据的饼图。你能看到趋势吗?
如果你集中精力,你可以看到这些变化,但这些变化在柱状图中更容易看到,如下图所示。你可以清楚地看到,黄党的选票份额变化不大,而蓝党在 3 月和 4 月获得了选票,但在 5 月和 6 月又失去了选票。红色政党的投票模式正好相反,表明选民们在两党之间交换了他们的忠诚。
另一种简单且易于解释的表示是折线图。在下图中,您可以看到与条形图相同的趋势。
我是不是对饼状图太苛刻了?我举的例子是不是对它有偏见?也许吧。我并不是建议我们禁止饼图,因为我们已经看到它们在一些受限的情况下是有用的。但是值得考虑替代方案:条形图和折线图通常是更好的替代方案。
参考
- 《定量信息的视觉展示》,爱德华·塔夫特,图形出版社,2001 年
Python 中的饼图
用 Python 创建饼图
饼图是一种数据可视化类型,用于说明数据中的数字比例。python 库“matplotlib”为创建漂亮的可视化效果提供了许多有用的工具,包括饼状图。在这篇文章中,我们将讨论如何使用“matplotlib”在 python 中创建饼状图。
我们开始吧!
出于我们的目的,我们将使用网飞电影和电视节目数据集,可以在这里找到。
首先,让我们将数据读入熊猫数据框;
import pandas as pd
df = pd.read_csv("netflix_titles.csv")
接下来,让我们使用。head()'方法:
print(df.head())
正如我们所看到的,数据包含具有各种分类值的列。饼图通常显示数据集中不同类别的相对比例。对于我们的饼图可视化,“评级”、“国家”和“类型”列是我们可以分组和可视化的带有分类值的数据的很好的例子。
为了了解这些列的分类值的分布情况,我们可以使用 collections 模块中的“Counter”方法。让我们将“计数器”方法应用于“类型”列:
from collections import Counter
print(Counter(df['type']))
让我们看看“评级”栏:
print(Counter(df['rating']))
现在,让我们按照每个“类型”类别的值的数量对数据进行分组:
title_type = df.groupby('type').agg('count')
print(title_type)
接下来,让我们从“matplotlib”导入几个模块:
import matplotlib.ticker as ticker
import matplotlib.cm as cm
import matplotlib as mpl
from matplotlib.gridspec import GridSpec
import matplotlib.pyplot as plt
现在,让我们对聚合类型的指数和计数进行排序:
type_labels = title_type.show_id.sort_values().index
type_counts = title_type.show_id.sort_values()
然后,我们指定图形细节:
plt.figure(1, figsize=(20,10))
the_grid = GridSpec(2, 2)
并指定颜色映射信息:
cmap = plt.get_cmap('Spectral')
colors = [cmap(i) for i in np.linspace(0, 1, 8)]
最后,让我们绘制饼图:
plt.subplot(the_grid[0, 1], aspect=1, title='Types of Netflix Titles')
type_show_ids = plt.pie(type_counts, labels=type_labels, autopct='%1.1f%%', shadow=True, colors=colors)
plt.show()
我们看到这个数据集中的大多数网飞电影都是电影。我们还可以为评级生成一个饼图。让我们按评级汇总:
title_rating = df.groupby('rating').agg('count')
rating_labels = title_rating.show_id.sort_values().index
rating_counts = title_rating.show_id.sort_values()
绘制我们的图表:
plt.figure(1, figsize=(40,20))
the_grid = GridSpec(2, 2)
cmap = plt.get_cmap('Spectral')
colors = [cmap(i) for i in np.linspace(0, 1, 8)]plt.subplot(the_grid[0, 1], aspect=1, title='Ratings of Netflix Titles')
type_show_ids = plt.pie(rating_counts, labels=rating_labels, autopct='%1.1f%%', shadow=True, colors=colors)
plt.show()
我们看到“TV-14”和“TV-MA”是数据中最常见的收视率。饼图的一个缺点是,一列中出现的类别越多,它们的用处就越小。例如,在我们的评级饼图中,“G”、“NC-17”和“ur”评级重叠,这在视觉上看起来并不令人愉快。你可以想象,随着类别数量的增加,情况会变得更糟。为了解决这个问题,让我们定义一个函数来计算第 75 个百分位数,并将较少的计数组合在“其他”类别中:
def group_lower_ranking_values(column):
rating_counts = df.groupby(column).agg('count')
pct_value = rating_counts[lambda x: x.columns[0]].quantile(.75)
values_below_pct_value = rating_counts[lambda x: x.columns[0]].loc[lambda s: s < pct_value].index.values
def fix_values(row):
if row[column] in values_below_pct_value:
row[column] = 'Other'
return row
rating_grouped = df.apply(fix_values, axis=1).groupby(column).agg('count')
return rating_grouped
我们现在可以使用这个函数来聚集我们的数据:
rating_grouped = group_lower_ranking_values('rating')
rating_labels = rating_grouped.show_id.sort_values().index
rating_counts = rating_grouped.show_id.sort_values()
并绘制出结果:
plt.subplot(the_grid[0, 1], aspect=1, title='Rating of Netflix Titles')
type_show_ids = plt.pie(rating_counts, labels=rating_labels, autopct='%1.1f%%', shadow=True, colors=colors)
plt.show()
我们看到这看起来比以前的情节好多了。让我们为 country 列做同样的事情。这里,我们将考虑第 99 百分位:
def group_lower_ranking_values(column):
...
pct_value = rating_counts[lambda x: x.columns[0]].**quantile(.99)**
...
return rating_groupedcountry_grouped = group_lower_ranking_values('country')
country_labels = country_grouped.show_id.sort_values().index
country_counts = country_grouped.show_id.sort_values()plt.subplot(the_grid[0, 1], aspect=1, title='Country of Netflix Titles')
type_show_ids = plt.pie(country_counts, labels=country_labels, autopct='%1.1f%%', shadow=True, colors=colors)
plt.show()
我就讲到这里,但是您可以自己随意摆弄代码。
结论
总之,在这篇文章中,我们讨论了如何用 python 创建饼状图。我们表明,代表相应的标题类型,评级和网飞标题国家类别的比例。我们还展示了如何通过计算排名最高的类别并将排名较低的值分组到一个“其他”组中,来解决可视化具有太多类别类型的列的问题。我希望你觉得这篇文章有趣/有用。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!
基于 Pillar 的三维点云物体检测在 Waymo 开放数据集上的实现
在 Amazon Sage Maker 上实现基于支柱的对象检测深度神经网络的教程。这可以推广到任何云实例,甚至是本地环境。
介绍
这篇文章是关于在 AWS 上实现和运行 3D 点云物体检测深度神经网络的。激光雷达(3D 点云)物体检测已经成为自动驾驶领域中的重要研究领域。由于自动驾驶汽车使用激光雷达来检测道路上的物体,因此在道路上检测这些物体并预测它们的运动以做出明智的驾驶决策非常重要。
有许多用于道路上物体检测和跟踪的开放数据集可供您学习。我在下面列出了几个受欢迎的:
- Waymo 开放数据集(https://waymo.com/open/)
- 基蒂数据集(【http://www.cvlibs.net/datasets/kitti/】T2
- Lyft 级数据集(【https://self-driving.lyft.com/level5/】T4)
一旦你知道要处理什么样的数据集,接下来就是对这些数据表现良好的神经网络。考虑到这种数据可以用于例如对象检测、分割、对象跟踪等的不同应用。有不同的网络。请参考这篇文章,阅读更多关于 3D 点云数据及其应用的信息,以及哪些网络最适合每种应用。为了这篇文章的目的,我们把重点放在物体检测和最近发表的基于柱子的方法1来解决这个问题。
先决条件:
- 克隆 GitHub repo:https://github.com/tyagi-iiitv/pillar-od。
- 访问 Waymo 数据集(https://waymo.com/open/download/)。访问他们的谷歌云存储大约需要 2 天时间,看起来像下面的图片。我们将使用它,这样我们就可以用终端直接在云上下载数据。
谷歌云上的 Waymo 数据集回购
实施步骤
启动云实例
因为我使用的是 Amazon SageMaker,所以我们用大约 2TB 的空间和一个 GPU 启动了一个笔记本实例。
在 Amazon SageMaker 上创建一个笔记本实例。
然后我们启动我们的云实例并打开 JupyterLab,接着在我们的实例上打开一个新的终端。
为您的云实例打开一个终端。
获取数据
使用以下命令从 google cloud bucket 获取数据(记住要将 gcloud 添加到路径中):
curl [https://sdk.cloud.google.com](https://sdk.cloud.google.com) | bash
exec -l $SHELL
gcloud init
如果gcloud
仍然无法识别,使用cat /home/ec2-user/.bashrc
查看.bashrc
文件的内容,并验证 gcloud 的链接,可能在该文件的最后两行。使用下面的命令将它们手动添加到您的路径中。这应该可以让 gcloud 命令在您的终端上工作。
source /home/ec2-user/google-cloud-sdk/path.bash.inc
source /home/ec2-user/google-cloud-sdk/completion.bash.inc
gcloud init
现在,最后,获取训练和验证数据,并将其分别存储在train_data
和validation_data
目录中。将数据从 google cloud bucket 复制到您的实例的命令是gsutil -m cp -r gs://waymo_open_dataset_v_xxx /path/to/my/data
找到 waymo 存储库的确切名称,打开 waymo 开放数据集的 google 云存储目录,并复制存储库的名称。如果你下载了一个.tar
文件,用命令tar -xvf training_xxxx.tar
提取它。这将为场景中的各个片段提供数据文件。
设置代码
- 克隆 GitHub repo
git clone https://github.com/tyagi-iiitv/pillar-od.git
- 创建一个虚拟工作环境
conda create -p ./env anaconda python=3.7
这将安装一个初始的、完整的 Anaconda 环境和必要的包。 - 激活 conda 环境
source activate ./env
- 安装附加库
conda install absl-py tensorflow-gpu tensorflow-datasets
- 安装 Waymo 开放数据集包装库
rm -rf waymo-od > /dev/null
git clone https://github.com/waymo-research/waymo-open-dataset.git waymo-od
cd waymo-od && git branch -a
git checkout remotes/origin/master
pip install --upgrade pip
pip install waymo-open-dataset-tf-2-1-0==1.2.0
- 为模型准备数据集。更改文件
pillar-od/data/generate_waymo_dataset.sh
中的源和目标目录。现在运行文件从下载的数据中读取帧。这将需要一段时间,取决于您下载的数据大小。
cd pillar-od/data
chmod +x generate_waymo_dataset.sh
./generate_waymo_dataset.sh
模特培训
在我们运行pillar-od
目录中的 train.py 文件之前,请确保在config.py
文件中更改数据集和其他配置参数的路径。完成后,让我们安装几个库来开始培训。
pip install lingvo tensorflow-addons
最后,模型准备好为汽车(类= 1)/行人(类=2)进行训练:
python train.py --class_id=1 --nms_iou_threshold=0.7 --pillar_map_size=256python train.py --class_id=2 --nms_iou_threshold=0.7 --pillar_map_size=512
模型评估
模型评估的过程相同,使用以下命令:
python eval.py --class_id=1 --nms_iou_threshold=0.7 --pillar_map_size=256 --ckpt_path=/path/to/checkpoints --data_path=/path/to/data --model_dir=/path/to/resultspython eval.py --class_id=2 --nms_iou_threshold=0.2 --pillar_map_size=512 --ckpt_path=/path/to/checkpoints --data_path=/path/to/data --model_dir=/path/to/results
参考
1基于立柱的自动驾驶目标检测,王等。艾尔。ECCV 2020
PIMA 印度糖尿病预测
预测糖尿病的发病
糖尿病是一种慢性疾病,身体对胰岛素产生耐药性,胰岛素是一种将食物转化为葡萄糖的激素。糖尿病影响全世界许多人,通常分为 1 型和二型糖尿病。两者各有特点。本文旨在 PIMA Indian 糖尿病数据集上分析并创建一个模型,以预测在给定独立因素的情况下,某一特定观察值是否有发展为糖尿病的风险。本文包含创建合适模型的方法,包括 EDA 和模型。
数据集
数据集可以在 Kaggle 网站上找到。该数据集最初来自美国国家糖尿病、消化和肾脏疾病研究所,可用于根据某些诊断因素预测患者是否患有糖尿病。首先,我使用 Python 3.3 来实现这个模型。执行一些基本分析以获得数据集的整体概念是很重要的。
#Importing basic packages
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from pandas_profiling import ProfileReport#Importing the Dataset
diabetes = pd.read_csv(“diabetes.csv”)
dataset = diabetes#EDA using Pandas Profiling
file = ProfileReport(dataset)
file.to_file(output_file=’output.html’)
Pandas profiling 是获得关于数据集和其中变量的全面和深入信息的有效方法。然而,如果数据集非常大,就要小心了,因为大熊猫特征分析非常耗时。因为数据集只有 768 个观察值和 9 列,所以我们使用这个函数。输出作为一个 HTML 报告保存在工作目录中。
数据集概述
我们可以看到数据集的基本信息,如大小、缺失值等。在右上角,我们看到 8 个数字列和 1 个布尔列(这是我们的因变量)。在下面的面板中,每一列都给出了 0 的(%),这将是我们以后的有用信息。我们没有任何分类变量作为自变量。
探索性数据分析
观察了数据集的基本特征后,我们现在继续观察研究中涉及的变量的特征。又一次,熊猫侧写来拯救我们了。同一个 HTML 报告给出了变量的信息。
自变量:年龄
我们来看看年龄。通过快速查看右上方的直方图,我们可以观察到许多特征。变量不遵循正态分布,因为它向右倾斜(或正倾斜)。平均年龄是 33 岁,而平均年龄是 29 岁。这进一步证实了我们的分析(在正态分布的情况下,平均值应该近似等于中位数)。最重要的是,没有一个值看起来是异常的,也就是说,最小年龄 21 岁和最大年龄 81 岁对于该研究是可能的。让我们看看其他变量。
自变量:血压
对血压做同样的处理,我们可以看到变量可以近似为正态分布。然而,我们无法从视觉上确认。因此,我们进行了夏皮罗-维尔克正态性检验。零假设( Ho) 是数据正态。
from scipy.stats import shapirostat, p = shapiro(dataset['BloodPressure'])
print('Statistics=%.3f, p=%.3f' % (stat, p))Statistics=0.819, p=0.000
p 值小于 0.001,意味着在 5%的显著性水平(LOS),我们拒绝了我们的零假设(Ho)。因此,变量不遵循正态分布。
其次,如果我们观察血压的最小值,它被给定为 0(这是不可能的)。因此,给出了不正确的信息。我们现在有两个选择。一种是放弃这样的观察值(这会导致 4.6%的数据丢失),或者我们可以用中值(估算)代替这样的值。我更喜欢估算,因为我们有一个小数据集(只有 768 个观察)。因此,每个信息都很重要。
我们可以简单地用中值替换零值,或者我们可以根据结果对变量进行分类,然后分别替换这两个值。后者似乎更有效率。我们画一个小提琴图来看看这个行为。
#Classifying the Blood Pressure based on class
ax = sns.violinplot(x=”Outcome”, y=”BloodPressure”, data=dataset, palette=”muted”, split=True)
我们得到了上面的图表。如果我们仔细观察,我们可以看到 1(糖尿病患者)在小提琴内的箱形图比 0(非糖尿病患者)的箱形图稍微偏离水平轴。这意味着糖尿病患者似乎比非糖尿病患者血压更高。小提琴的底部尾部*表示我们需要替换的零值。我们将用 1 的中值替换 1 的零,对于 0 也是如此。*
#Replacing the zero-values for Blood Pressure
df1 = dataset.loc[dataset['Outcome'] == 1]
df2 = dataset.loc[dataset['Outcome'] == 0]df1 = df1.replace({'BloodPressure':0}, np.median(df1['BloodPressure']))
df2 = df2.replace({'BloodPressure':0}, np.median(df2['BloodPressure']))dataframe = [df1, df2]
dataset = pd.concat(dataframe)
贴上后血压栏不会有任何零值。让我们转到下一个变量。
自变量:身体质量指数
由于平均值和中值大致相等,变量似乎紧密遵循正态分布。但是,它面临着和以前一样的问题,即零值的存在。让我们为这个变量画一个小提琴图。
我们可以观察到 1 服从正态分布,而 0 不服从正态分布。此外,糖尿病患者的身体质量指数高于非糖尿病患者的身体质量指数(可以使用箱线图观察)。在身体质量指数,我们对血压零值的处理方法与我们对血压零值的处理方法相同。在这种情况下,我们也可以用平均值来代替,但是,我将坚持使用中位数。在更改列名之后,我们运行相同的代码。继续下一个变量。
自变量:糖尿病
糖尿病谱系函数是一个正偏变量,没有零值。我们用同样的小提琴剧情来观察特点。
可以形成相同的假设。糖尿病患者似乎比非糖尿病患者有更高的谱系功能。继续第五个独立变量。
自变量:葡萄糖
分析葡萄糖,我们观察到变量不遵循正态分布。在这种情况下,我们也会遇到零值。有 5 个这样的值需要治疗。我们像以前一样执行相同的处理,用中位数代替(按类)。
观察小提琴图,我们看到糖尿病患者和非糖尿病患者的箱线图之间有巨大的垂直距离。这表明葡萄糖可能是建模中非常重要的变量。处理完零值后,我们转到下一个变量。
自变量:胰岛素
如数据集可用的数据字典中所述,胰岛素是 2 小时血清胰岛素(μU/ml)。变量是正偏的。然而,在这种情况下,零值的出现率很高,占数据的 48.7%。这个数据必须进行估算。我们先治疗这些。
在处理了零值之后,我们在这个图上推导出。我们仍然可以将 0 视为糖尿病患者胰岛素的中位数。然而对于非糖尿病人来说,胰岛素稍高。可以粗略假设,糖尿病患者的胰岛素低于非糖尿病患者。继续怀孕。
独立变量:怀孕
变量正偏,零值为 14.5%。我们不需要将零值视为异常现象。然而,鉴于 85.5%的数值为非零,我们可以推断这项研究只针对女性。使用小提琴图观察行为,我们观察到糖尿病妇女比非糖尿病妇女有更多的怀孕。
自变量:皮肤厚度
转到我们的最后一个自变量,我们观察到与前面大多数变量相同的模式。数据正偏,有 29.6%的零值。治疗之后,我们用小提琴的情节来观察一段粗糙的关系。
糖尿病患者的皮肤厚度大于非糖尿病患者。
我们的因变量‘Outcome’取布尔值 0 和 1。0 表示非糖尿病,1 表示糖尿病。为了检查出现的情况,我们使用一个简单的条形图
结果中 0 和 1 的频率
可以清楚地看到数据中的不平衡,其中 0(非糖尿病)是模态类别。我们将在稍后的过程中处理这种不平衡。
检查多重共线性
下面的相关矩阵用皮尔逊相关系数来说明变量之间的关系。从图中可以观察到怀孕和年龄之间的显著相关性。为了进一步证实,我们计算相关系数。
from scipy.stats import pearsonrcorr, _ = pearsonr(dataset[‘Age’], dataset[‘Pregnancies’])
print(‘Pearsons correlation: %.3f’ % corr)
Pearsons correlation: 0.544
相关系数(r)为 0.544。根据经验法则,如果 r 高于 0.70 ,预计会出现多重共线性。因此,没有观察到多重共线性的显著情况。
处理异常值和非正态性
离群值是数据集中存在的极值。如果对数据集应用了基于距离的算法(逻辑回归、SVM 等),则有必要处理异常值。离群值不会影响基于树的算法。由于我们将使用距离和基于树的算法,我们将缩放我们的数据来处理异常值。我们使用标准定标器进行处理。标准缩放器通过减去平均值并除以标准差来变换特征。这样,特征也接近均值为 0 的标准正态分布。
#Splitting the data into dependent and independent variables
Y = dataset.Outcome
x = dataset.drop(‘Outcome’, axis = 1)
columns = x.columnsfrom sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(x)data_x = pd.DataFrame(X, columns = columns)
我们已经调整了 X 值。
将数据集分割成训练和测试数据
现在,我们将处理过的数据集分成训练数据和测试数据。测试数据大小为整个数据的 15%(这意味着 115 次观察),模型将在 653 次观察的基础上进行训练。
#Splitting the data into training and test
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data_x, Y, test_size = 0.15, random_state = 45)
显示 Y 系列中 0 和 1 的频率的图
在 y_train 值中可以观察到巨大的不平衡。为了解决这个问题,我们使用 SMOTE 技术。
合成少数过采样技术(SMOTE)用于通过使用当前数据创建样本来消除训练数据中的不平衡。它不会创建副本。请记住,它总是在培训日期完成,而不是在原始数据上完成,因为测试数据应该只包含真实值,而不是合成样本。
from imblearn.over_sampling import SMOTEsmt = SMOTE()x_train, y_train = smt.fit_sample(x_train, y_train)np.bincount(y_train)
Out[74]: array([430, 430])
我们现在有一个平衡的训练数据。
我们的数据现在已经准备好符合一个模型
模型拟合:逻辑回归
我们对训练数据拟合的第一个模型是逻辑回归。
from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression()
logreg.fit(x_train, y_train)y_pred = logreg.predict(x_test)
print('Accuracy of logistic regression classifier on test set: {:.2f}'.format(logreg.score(x_test, y_test)))Out[76]: Accuracy of logistic regression classifier on test set: **0.73**
我们在测试数据上得到了 73%的准确度分数。
print(f1_score(y_test, y_pred, average=”macro”))
print(precision_score(y_test, y_pred, average=”macro”))
print(recall_score(y_test, y_pred, average=”macro”))
0.723703419131771
0.7220530003045994
0.7263975155279503
我们的模型精度为 0.722。这表明,当患者实际上具有患糖尿病的高风险时,我们的模型在 72%的情况下将患者分类为高风险类别。
召回率/敏感度是 0.726,这意味着 72%的情况下实际上具有高风险的人被我们的模型正确分类。
模型拟合:支持向量机(核:rbf)
我们对训练数据拟合的第一个模型是支持向量机(SVM)。SVM 使用许多内核对数据进行分类。我们使用RBF/高斯核来拟合第一个模型。
from sklearn.svm import SVCclassifier_rbf = SVC(kernel = ‘rbf’)
classifier_rbf.fit(x_train, y_train)y_pred = classifier_rbf.predict(x_test)print('Accuracy of SVC (RBF) classifier on test set: {:.2f}'.format(classifier_rbf.score(x_test, y_test)))
Out[76]: Accuracy of SVC (RBF) classifier on test set: **0.75**print(f1_score(y_test, y_pred, average="macro"))
print(precision_score(y_test, y_pred, average="macro"))
print(recall_score(y_test, y_pred, average="macro"))
0.7431080565101182
0.7410256410256411
0.7481366459627329
我们使用 rbf 核的 SVM 提高了精度。模型准确率达到 75%,与逻辑回归相比,精度和召回值都有所提高。
模型拟合:随机森林
我们使用随机森林分类器,用 300 棵树(在调整模型后获得)来拟合数据上的模型。
from sklearn.ensemble import RandomForestClassifiermodel = RandomForestClassifier(n_estimators=300, bootstrap = True, max_features = ‘sqrt’)model.fit(x_train, y_train)y_pred = model.predict(x_test)
print('Accuracy of Random Forest on test set: {:.2f}'.format(model.score(x_test, y_test)))Out[95]: Accuracy of Random Forest on test set: 0.88print(f1_score(y_test, y_pred, average="macro"))
print(precision_score(y_test, y_pred, average="macro"))
print(recall_score(y_test, y_pred, average="macro"))
0.8729264475743349
0.8762626262626263
0.8701863354037267
随机森林的准确率最高,达到了 88%。这意味着,我们的模型预测正确分类的次数为 88%。
精度得分为 0.876,这意味着我们的模型正确地将高风险的观察结果分类为泰晤士报的高风险类别 87.6% 。召回站在 0.870 。
我们的 F1 分数也是 0.872。F1 分数是精确度和召回率的调和平均值。它对这两个指标赋予相同的权重。然而,对于我们的分析,该模型具有低假阴性病例相对更重要(因为将高风险患者分类到低风险类别中是危险的)。所以我们单独看精度和召回率。
上图显示了特征的相对重要性及其对模型的贡献。因为它是一个包含较少列的小数据集,所以我没有使用 PCA 等特征选择技术。
结论
因此,由于高准确度、精确度和召回分数,我们选择随机森林分类器作为正确的模型。随机森林分类器表现出改进的性能的一个原因是因为离群值的存在。如前所述,由于随机森林不是基于距离的算法,它不会受到离群值的太大影响,而基于距离的算法,如逻辑回归和支持向量,表现出较低的性能。
基于特征重要性:
- 葡萄糖是决定糖尿病发病的最重要因素,其次是身体质量指数和年龄。
- 其他因素如糖尿病谱系功能、怀孕、血压、皮肤厚度和胰岛素也有助于预测。
正如我们所看到的,从特征重要性得出的结果是有意义的,因为在高风险患者中实际监控的第一件事是血糖水平。身体质量指数升高也可能表明有患 II 型糖尿病的风险。通常情况下,特别是在 II 型糖尿病的情况下,随着年龄的增长(考虑到其他因素),患糖尿病的风险很高。
我们现在到了项目的尾声。我没有深入研究我所使用的技术。然而,有一些非常好的文章帮助我做到了这一点。
一如既往,我欢迎建设性的批评、反馈和讨论。可以通过 Gmail 联系到我:icy.algorithms@gmail.com
参考
数据集和数据信息:https://www.kaggle.com/uciml/pima-indians-diabetes-database
小提琴剧情:https://seaborn . pydata . org/generated/seaborn . violin plot . html
夏皮罗-维尔克正态性检验:https://machine learning mastery . com/a-gentle-introduction-to-Normality-tests-in-python/
异常值处理:https://towards data science . com/ways-to-detect-and-remove-the-outliers-404d 16608 DBA
SMOTE:https://machine learning mastery . com/SMOTE-over sampling-for-unbalanced-class ification/
精度和召回:https://towards data science . com/beyond-accuracy-precision-and-Recall-3da 06 bea 9 f6c
皮马印第安人糖尿病-预测和 KNN 可视化
使用 KNN 算法的预测具有 74%的接收器操作特征准确度
迈肯齐·约翰逊在 Unsplash 上拍摄的照片
在印度,糖尿病是一个主要问题。从 1971 年到 2000 年,糖尿病的发病率上升了十倍,从 1.2%上升到 12.1%。据估计,印度有 6130 万 20-79 岁的糖尿病患者(2011 年的预期)。预计到 2030 年,这一数字将上升至 1.012 亿。据报道,印度有 7720 万糖尿病前期患者。2012 年,印度有近 100 万人死于糖尿病。居住在钦奈城市贫民窟的每 4 个人中就有 1 人患有糖尿病,约为 7 %,是全国平均水平的 3 倍。印度三分之一的死亡涉及 60 岁以下的非传染性疾病患者。印度人患糖尿病的平均时间比西方人早 10 年。生活方式的改变导致体力下降,脂肪、糖和活动量增加,卡路里和胰岛素皮质醇水平升高,肥胖和脆弱。2011 年,印度每年因糖尿病花费约 380 亿美元。
由 Hardik 编码
链接 Colab 笔记本:https://Colab . research . Google . com/drive/1n 4 fngk 0 dwt k 0 qalowcywjbtklxjgs-S?usp =共享
数据集:
皮马印第安人糖尿病数据库(根据诊断方法预测糖尿病的发病)
数据集源:
https://www.kaggle.com/uciml/pima-indians-diabetes-database UCI 机器学习库:
语境
这个数据集来自美国国家糖尿病、消化和肾脏疾病研究所。该数据集的目的是根据数据集中的某些诊断方法来诊断患者是否患有糖尿病。从较大的数据库中选择这些实例受到一些限制。所有患者都是至少 21 岁的皮马印度后裔妇女。
内容
数据集包括医学预测值的几个变量和一个目标变量Outcome
。预测变量包括患者的怀孕次数、身体质量指数水平、胰岛素水平、年龄等。
承认
史密斯、J.W .、埃弗哈特、J.E .、迪克森、W.C .、诺尔勒、W.C .、约翰尼斯、R.S. (1988)。利用 ADAP 学习算法预测糖尿病的发病。《计算机应用与医疗研讨会论文集(第 261-265 页)。IEEE 计算机学会出版社。
灵感
你能建立一个机器学习模型来准确预测数据集中的患者是否患有糖尿病吗?
1 导入和加载数据集
进口
import numpy as np
import pandas as pd# Visualization imports
import matplotlib.pyplot as plt
import seaborn as sns# plotly import for Colab
def configure_plotly_browser_state():
import IPython
display(IPython.core.display.HTML('''
<script src="/static/components/requirejs/require.js"></script>
<script>
requirejs.config({
paths: {
base: '/static/base',
plotly: '[https://cdn.plot.ly/plotly-latest.min.js?noext'](https://cdn.plot.ly/plotly-latest.min.js?noext'),
},
});
</script>
'''))
# plotly import
import plotly.express as pxfrom plotly import __version__
import cufflinks as cf
from plotly.offline import download_plotlyjs,init_notebook_mode,plot,iplot
init_notebook_mode(connected=True)
cf.go_offline()import IPythonIPython.get_ipython().events.register('pre_run_cell', configure_plotly_browser_state)
数据集
# Loading Dataset
df = pd.read_csv('/content/drive/My Drive/dataset/knn/datasets_228_482_diabetes.csv')
df.head()
2 数据清理
以下各列中' 0 '值没有意义。因此使他们成为 NaN。
df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']] = df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']].replace(0,np.NaN)df.isnull().sum()
import missingno as msno
p = msno.bar(df)
使用 k-NN 通过输入填充空值
!pip install impyuteimport sys
from impyute.imputation.cs import fast_knn
sys.setrecursionlimit(100000) #Increase the recursion limit of the OS
# start the KNN training
imputed_training=fast_knn(df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']].values, k=30)df_t1 = pd.DataFrame(imputed_training,columns=['Glucose','BloodPressure','SkinThickness','Insulin','BMI'])
df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']] = df_t1[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']]df.isnull().sum()
3 数据描述和可视化
df.info()
df.describe()
sns.heatmap(df.corr(),annot=True)
p = df[df['Outcome']==1].hist(figsize = (20,20))
plt.title('Diabetes Patient')
KNN 可视化所有功能用Outcome
X = df[[‘Pregnancies’, ‘Glucose’, ‘BloodPressure’, ‘SkinThickness’, ‘Insulin’,’BMI’, ‘DiabetesPedigreeFunction’, ‘Age’]]
y = df[‘Outcome’]
注:*0 — Non Diabetic Patient and 1 — Diabetic Patient*
from sklearn.decomposition import PCA
from mlxtend.plotting import plot_decision_regions
from sklearn.svm import SVCclf = SVC(C=100,gamma=0.0001)
pca = PCA(n_components = 2)
X_train2 = pca.fit_transform(X)
clf.fit(X_train2, df['Outcome'].astype(int).values)
plot_decision_regions(X_train2, df['Outcome'].astype(int).values, clf=clf, legend=2)
KNN 的特点是互相可视化:
from sklearn import datasets, neighbors
from mlxtend.plotting import plot_decision_regionsdef ok(X,Y):
x = df[[X,Y]].values
y = df['Outcome'].astype(int).values
clf = neighbors.KNeighborsClassifier(n_neighbors=9)
clf.fit(x, y)
# Plotting decision region
plot_decision_regions(x, y, clf=clf, legend=2)
# Adding axes annotations
plt.xlabel(X)
plt.ylabel(Y)
plt.title('Knn with K='+ str(9))
plt.show()tt = ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin','BMI', 'DiabetesPedigreeFunction', 'Age']
ll = len(tt)for i in range(0,ll):
for j in range(i+1,ll):
ok(tt[i],tt[j])
注意:*0 — Non Diabetic and 1 — Diabetic*
4 数据预处理
数据 Z 重新调整为 μ = 0 和 ρ = 1 ,应用此表:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()scaler.fit(df.drop('Outcome', axis = 1))scaler_features = scaler.transform(df.drop('Outcome', axis = 1))df_feat = pd.DataFrame(scaler_features, columns = df.columns[:-1])# appending the outcome feature
df_feat['Outcome'] = df['Outcome'].astype(int)df = df_feat.copy()
df.head()
# to reverse scaler transformation#s = scaler.inverse_transform(df_feat)
#df_feat = pd.DataFrame(s, columns = df.columns[:-1])
5 KNN
裂开
X = df.drop('Outcome', axis = 1)
y = df['Outcome']from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0)
通过获得从 1 到 100 范围内的每个 K 的接收器操作特性精度来检查最佳 K 值
import sklearntt = {}
il = []
ac=[]
for i in range(1,100):
from sklearn.neighbors import KNeighborsClassifierknn = KNeighborsClassifier(n_neighbors=i)knn.fit(X_train,y_train)y_pred = knn.predict(X_test)from sklearn.metrics import accuracy_score
il.append(i)
ac.append( sklearn.metrics.roc_auc_score(y_test,y_pred) )tt.update({'K':il})
tt.update({'ROC_ACC':ac})vv = pd.DataFrame(tt)
vv.sort_values('ROC_ACC',ascending=False,inplace=True,ignore_index=True)
vv.head(10)
对应 K 值的前 10 个最佳 ROC 准确度
选择“k = 9”
from sklearn.neighbors import KNeighborsClassifierknn = KNeighborsClassifier(n_neighbors=9)knn.fit(X_train,y_train)y_pred = knn.predict(X_test)from sklearn.metrics import classification_report
print(classification_report(y_test,y_pred))
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test,y_pred))
sns.heatmap(confusion_matrix(y_test,y_pred),annot=True)
from sklearn.metrics import roc_curve
plt.figure(dpi=100)
fpr, tpr, thresholds = roc_curve(y_test, y_pred)
plt.plot(fpr,tpr,label = "%.2f" %sklearn.metrics.roc_auc_score(y_test,y_pred))
plt.legend(loc = 'lower right')plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.title('ROC curve for Diabetes classifier')
plt.xlabel('False positive rate (1-Specificity)')
plt.ylabel('True positive rate (Sensitivity)')plt.grid(True)
import sklearn
sklearn.metrics.roc_auc_score(y_test,y_pred)
0.7399724565329662
data = {'test':y_test.values.ravel(),'pred':y_pred.ravel(),'number':np.arange(0,len(y_test))}pt = pd.DataFrame(data)pt.iplot(
kind='scatter',
x='number',
y=['test','pred'],
color=['white','yellow'],
theme='solar',
mode='lines+markers'
)
解释了管道、列转换器和功能联合
他们做什么,何时使用
这三个强大的工具,对于任何想掌握使用 sklearn 的人来说,都是必须知道的。因此,在建立机器学习模型时,学会如何有效地使用这些方法至关重要。
在我们开始之前,让我们首先在两个术语上达成一致:
- Transformer: 一个 transformer 是指用
[fit](https://scikit-learn.org/stable/data_transforms.html)()
和[transform](https://scikit-learn.org/stable/data_transforms.html)()
方法清理、缩小、扩展或生成特征的对象。简而言之,转换器可以帮助您将数据转换为机器学习模型所需的格式。OneHotEncoder
和MinMaxScaler
就是变形金刚的例子。 - 估计器: 一个 估计器是指一个机器学习模型。是用
fit()
和predict()
方法的对象。在这篇文章中,我们将交替使用评估者和模型。这里有一些估计量的例子。
马丁·桑切斯在 Unsplash 上拍摄的照片
0.设置
如果你想在你的电脑上跟着代码走,确保你已经安装了 pandas,seaborn 和 sklearn。我已经在 Jupyter Notebook 中使用并测试了 Python 3.7.1 中的脚本。
让我们导入所需的包和餐馆小费的数据集。关于这个数据集的细节,包括数据字典,可以在这里找到(这个源实际上是针对 R 的,但它似乎指的是同一个底层数据集)。
使用少量的记录可以很容易地监控每一步的输入和输出。因此,我们将只使用数据集中的 5 条记录作为样本。
1.管道
假设我们想要使用吸烟者、日和时间列来预测总账单。我们将删除 size 列,并首先对数据进行分区:
通常,原始数据不处于我们可以直接将其输入机器学习模型的状态。因此,将数据转换为模型可接受且有用的状态成为建模的必要前提。让我们做以下转换作为准备:
- 用' missing '估算缺失值
- 一次性编码它们
有一种方法可以完成这两个步骤:
您可能已经注意到,当映射回测试数据集的列名时,我们使用了来自训练数据集的列名。这是因为我更喜欢使用数据中的列名,这些数据是转换器被训练的数据。然而,如果我们使用测试数据集,它会给出相同的结果。
对于每个数据集,我们首先看到原始数据,然后是插补后的输出,最后是编码后的输出。这种方法可以完成工作。然而,我们手动地将上一步的输出作为输入提供给下一步,并且有多个中间输出。我们还必须对测试数据重复每一步。随着步骤数量的增加,维护起来会变得更加繁琐,也更容易出错。我们可以用Pipeline
编写更精简的代码:
使用Pipeline
时,每一步都将其输出作为输入传递给下一步。因此,我们不必手动跟踪数据的不同版本。这种方法为我们提供了完全相同的最终输出,但代码更加优雅。
查看了转换后的数据后,是时候在我们的示例中添加一个模型了。让我们调整代码,为第一种方法添加一个简单的模型:
我们将对管道方法做同样的事情:
你可能已经注意到,一旦我们训练了一条管道,做预测是多么简单。调用一行代码:pipe.predict(X)
对原始数据进行转换,然后返回一个预测。也很容易看出步骤的顺序。让我们直观地总结一下这两种方法:
作者图片
使用Pipeline
不仅可以组织和简化你的代码,还有很多其他的好处,下面是其中的一些:
- 微调管道的能力:在构建模型时,您是否曾经不得不后退一步,尝试不同的方法来预处理数据并再次运行模型,以查看预处理步骤中的调整是否提高了模型的适用性?优化模型时,cogs 不仅存在于模型超参数中,还存在于预处理步骤的实现中。考虑到这一点,当我们有一个统一变压器和估计器的单一管道对象时,我们能够微调整个管道的超参数,包括变压器和具有
GridSearchCV
或RandomizedSearchCV
的估计器。 - 更容易部署:在训练模型时用于准备数据的所有转换步骤也应该应用于生产环境中进行预测时的数据。当我们训练一个
Pipeline
时,我们训练一个包含数据转换器和模型的单一对象。一旦经过训练,这个Pipeline
对象可以用于更平滑的部署。
2.列变压器()
在前面的例子中,我们以同样的方式估算和编码所有的列。然而,我们经常需要对不同的列组应用不同的变压器组。例如,我们希望将OneHotEncoder
仅应用于分类列,而不是数字列。这就是ColumnTransformer
的用武之地。这一次,我们将对数据集进行分区,保留所有列,这样我们既有数字特征又有分类特征。
我们已经根据数据类型将特性分成了两组。可以根据数据的适当情况进行列分组。例如,如果不同的预处理管道更适合分类列,那么分类列可以进一步分成多个组。
上一节的代码现在不再有效,因为我们有多种数据类型。让我们来看一个例子,在这个例子中,我们使用ColumnTransformer
和Pipeline
在存在多种数据类型的情况下进行与之前相同的转换。
分类列的输出与上一节的输出相同。唯一不同的是这个版本多了一列:大小。我们已经将cat_pipe
(之前在第 1 节中称为pipe
)传递给ColumnTransformer
来转换分类列,并指定remainder='passthrough'
保持剩余的列不变。
如果也转换数字列不是很好吗?特别是,让我们用中值大小估算缺失值,并在 0 和 1 之间缩放:
现在所有的列都是估算的,范围在 0 和 1 之间。使用ColumnTransformer
和Pipeline
,我们将数据分成两组,对每组应用不同的管道和不同的变压器组,然后将结果粘贴在一起:
作者图片
尽管在我们的示例中,数字管道和分类管道中有相同数量的步骤,但是管道中可以有任意数量的步骤,因为它们彼此独立。现在,让我们在示例中添加一个模型:
为了将ColumnTransformer
中指定的预处理步骤与模型放在一起,我们在外部使用了一个Pipeline
。这是它的视觉表现:
作者图片
当我们需要对不同的列子集进行不同的操作时,ColumnTransformer
很好地补充了Pipeline
。
3.功能联盟
本节中省略了代码后的输出,因为它们与第 2 节中的输出相同。列变压器。
FeatureUnion
是另一个有用的工具。它能够做ColumnTransformer
刚刚做的事情,但是方式更长:
我们可以认为FeatureUnion
创建了数据的副本,并行转换这些副本,然后将结果粘贴在一起。这里的术语“复制”与其说是一个技术参考,不如说是一个帮助概念化的类比。
在每个管道的开始,我们添加了一个额外的步骤,在那里我们使用一个定制的转换器选择相关的列:在第 14 行和第 19 行的ColumnSelector
。下面是我们如何直观地总结上面的脚本:
作者图片
现在,是时候向脚本添加一个模型了:
它看起来和我们用ColumnTransformer
做的非常相似。
作者图片
如本例所示,使用FeatureUnion
比使用ColumnTransformer
更加冗长。因此,在我看来,在类似的情况下最好使用ColumnTransformer
。不过,FeatureUnion
绝对有它的位置。如果你曾经需要以不同的方式转换相同的输入数据,并把它们作为特征使用,FeatureUnion
就是其中之一。例如,如果您正在处理一个文本数据,并且既想对数据进行 tf-idf 矢量化,又想提取文本长度,FeatureUnion
是一个完美的工具。这里有一篇专门的文章: FeatureUnion,ColumnTransformer & Pipeline,用于预处理文本数据
4.摘要
你可能已经注意到了,Pipeline
是超级巨星。ColumnTransformer
和FeatureUnion
是与Pipeline
一起使用的附加工具。ColumnTransformer
更适合我们并行分而治之,而FeatureUnion
允许我们对相同的输入数据并行应用多个转换器。下面是一个简单的总结:
作者图片
您想要访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。
谢谢你看我的帖子。希望这篇文章能帮助你更多地了解这些有用的工具。我希望你能在你的数据科学项目中使用它们。如果你感兴趣,这里有我的一些帖子的链接:
◼ FeatureUnion,ColumnTransformer &用于预处理文本数据的管道
◼ 用 Scikit 创建自定义变形器的两种方法——学习
◼python 中的探索性文本分析
◼python 中的预处理文本
◼python 中的情感分类
◼ 给熊猫用户的 5 个提示
️◼ 熊猫中数据聚合的 5 个提示
再见🏃💨
用于文本数据预处理的流水线
使用 Python 简化使用文本数据建模的所有准备工作
迈克·本纳在 Unsplash 上的照片
GitHub 链接
介绍
如果你对这个比了解其背后的快速动机更感兴趣,请跳到实际的管道部分:文本预处理管道(博客中途)。
我一直在研究对文本数据执行机器学习,但有一些数据预处理步骤是我不习惯的文本数据所特有的。因此,我的 Python 代码包含了许多转换步骤,在这些步骤中,我会与数据争论,进行转换,然后转换训练数据,转换测试数据,然后对我想要进行的每种类型的转换重复这个过程。我记得曾经读到过 Python 有一种包装转换的便捷方法,但在此之前从未有过深入研究的理由。通常,我会对数字数据进行标准化缩放,或者创建一些虚拟变量,仅此而已。现在我进入了一个对我来说有点新的领域,我想保持这些转变并学习一些新的东西。
导入必要的包
import numpy as np
import pandas as pd
from scipy import sparse as spfrom sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformerfrom sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoderfrom sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
如果你像我一年前一样是使用 NLTK 的新手,这个网站将帮助你启动并运行,并且被保存到我的收藏夹中,因为我在使用 NLTK 时经常使用它作为参考。
# Import Natural language Toolkit example data and 'stopwords' setfrom nltk.corpus import movie_reviews
from nltk.corpus import stopwords
导入示例“电影评论”数据集
“电影评论”数据有 2000 个电影评论,每个评论都有一个相应的类别(标签),用于表示它是负面的(“负面”)还是正面的(“正面”)。
docs = [(str(movie_reviews.raw(fileid)), category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)]
现在只是把“文档”改成熊猫数据框架,主要是因为我习惯了这种格式。数据转换管道不仅仅适用于熊猫数据帧。
reviews = pd.DataFrame(docs)
reviews.columns=('X','y')
电影评论的“类别”最初是“负面”或“正面”,在这里分别更改为 0 和 1:
bin_encoder=LabelEncoder()
reviews.y=bin_encoder.fit_transform(reviews.y)
此时,评审在一个数据框架中,其中 X 列作为评审文本, y 列作为目标变量,评审类别。这更符合我目前习惯的机器学习项目的风格。以下是前 5 个观察结果的视图:
reviews.head(5)
故事情节:两对青少年夫妇去参加一个教堂聚会,… 0 1 快乐私生子的快速电影评论\ndamn … 0 2 正是这样的电影使一部令人厌倦的电影… 0 3《寻找卡梅洛特》是华纳兄弟公司出品的
需要做的是将 X 列标记化,删除停用词,并将每个观察结果矢量化,以便机器学习如何区分正面和负面评论。
停用词
NLTK 和 Scikit-Learn 函数 CountVectorizer 都有内置的停用词集合或列表,它们基本上是一串我们不希望在数据中出现的词。像“a”、“of”和“the”这样的词通常是没有用的,它们在句子或段落中出现的频率比其他词高。不过,有时你想添加一些你自己的自定义停用词,所以我在这里创建了我的列表,custom_stopwords :
mystopwords = (stopwords.words())
custom_stopwords = ('the','an','a','my','0','''''','!','nt','?','??','?!','%','&','UTC','(UTC)')
现在,电影评论已经在一个数据帧中,并且有一个可以从电影评论中删除的停用词列表,我将展示我之前使用的文本预处理管道和我使用 Sci-kit Learn 的管道制作的管道。
文本预处理管道
用于文本转换的陈旧生锈的“管道”
这个想法是将所有的句子放入一个大矩阵中,矩阵中的列代表单个单词,术语频率或术语频率逆文档频率作为相应单词下的观察值。
通常,在机器学习项目中,你需要重复你对训练数据到测试数据所做的转换,所以我明确地保存了转换。这就是为什么我用一行来保存 TfidfTransform 和另一行来实际转换训练数据。后来,我在测试数据集上使用了那个 TfidfTransform 。我用计数矢量器做了同样的事情。
count_vect = CountVectorizer(stop_words=mystopwords,lowercase=True)
X_train_counts = count_vect.fit_transform(X)
tf_transformer = TfidfTransformer(use_idf=True).fit(X_train_counts)
X_train_tfidf = tf_transformer.transform(X_train_counts)
类似地,为了选择数据集的前 k 个特征(单词),我确保保存转换并将其应用于训练和测试数据集。
chi2(X_res,y_res)
k=1000
ch2_score = SelectKBest(chi2, k=k)
toxic_feature_tran = ch2_score.fit(X,y)
X_train_k = ch2_score.fit_transform(X, y)
X_test_k = ch2_score.transform(X_test)
这不仅是一个麻烦,而且如果我不得不在任何时间点重复这个过程,那么我将不得不创建新的变量,并确保所有这些步骤按顺序进行,不要覆盖任何内容。这不是好的编码。这就是管道进入并清理一切的地方。
新文本预处理转换
如果你的机器学习项目开始处理几个转换,这是你需要下载和利用的包。对于 Python 中的每一个具有' fit_transform()' 方法的转换(我在这里使用的所有方法都是如此),我们可以将它们包装在一个实际的管道中,该管道按顺序执行它们,甚至可以返回并查看每个转换的属性。此外,您可以为每个转换设置参数,语法在我刚刚分享的链接中。如果有什么不同的话,使用这个管道已经清理了我的代码,并且更好地组织了我的思维。
使用这三个转换:
- “计数矢量化器”:从句子转换为所有小写单词,删除停用词,矢量化
- 【chi 2s score】:基于卡方检验统计选择与目标相关的前 k 个特征的转换
- ‘TF _ transformer’:将顶部特征的向量转换为 tf-idf 表示的转换
管道看起来像这样:
# Using a variable for the top k features to be selected
top_k_features=1000text_processor = Pipeline([
('count vectorizer',CountVectorizer(stop_words=mystopwords,lowercase=True)),
('chi2score',SelectKBest(chi2,k=top_k_features)),
('tf_transformer',TfidfTransformer(use_idf=True))
])
现在,一旦这适合于训练数据, text_preprocessor 管道具有 transform 方法,该方法对数据执行所有三个包含的转换。
适应和转换
与使用 fit_transform() 方法的任何其他转换一样, text_processor 管道的转换是 fit,并且数据被转换。 proc_fit 可以用来以同样的方式转换测试数据。
proc_text = text_processor.fit_transform(reviews.X,reviews.y)
proc_fit = text_processor.fit(reviews.X,reviews.y)
检查变化
仍然可以通过以下两个步骤来返回特定评论的最后 1000 个单词中的原始单词:
- 查找从“chi2score”转换返回的前 1000 个功能的索引
- 找到“功能名称”,即原文中的单词
proc_fit.named_steps['chi2score'].get_support(indices=True)[616]
我从第一个变形的影评词中选出了数字 616。原来这对应于原始特征中的特征号 23078。(经过“chi2score”转换后,只剩下 1000 个特性,特性的数量也发生了变化。)
Out[49]:23078
返回的内容:单词“music”是对应于来自原始电影评论文本数据的特征号 23078 的单词,该文本数据被转换成计数向量。它现在是前 1000 个特性中的第 616 个特性。我们总是可以打印整个电影评论,看看这个词是否出现在那里。
proc_fit.named_steps['count vectorizer'].get_feature_names()[23078]
'music'
print(reviews.iloc[0,0])
检查第一篇评论,我们可以看到“音乐”这个词确实存在,而且这个词出现在整个电影评论语料库的前 1000 个精选特征中。
工作室从导演那里拿走了这部电影,他们自己把它切碎了,然后它就出现了。在这里的某个地方可能会有一部相当不错的青少年心灵操电影,但我想《西装革履》决定将它变成一部没有多少棱角的 音乐 视频,会更有意义。大部分演员都很出色,尽管韦斯·本特利似乎只是在扮演他在《美国丽人》中扮演的那个角色……
通常,不需要在原始数据中查找单个单词,并确保它在新的转换数据中具有特征。在这里,我花时间完成了这些步骤,以确保我构建的管道按预期工作,并展示了管道的所有部分如何显示它们的属性。
结论
非结构化文本数据在机器学习项目中非常有用。需要将文本数据转换为机器可以学习的更结构化的格式,通常使用预构建的转换,如删除停用词和 tf-idf 转换。可以构建数据需要经过的转换管道来简化代码,并为文本预处理阶段提供一致性。利用管道函数可以帮助将几个转换打包成一个。
参考
NLTK 是构建 Python 程序来处理人类语言数据的领先平台。它提供了易于使用的…
www.nltk.org](http://www.nltk.org/) [## Python 编程教程
欢迎来到自然语言处理教程系列,使用自然语言工具包(NLTK)模块…
pythonprogramming.net](https://pythonprogramming.net/tokenizing-words-sentences-nltk-tutorial/)
http://scikit learn . org/stable/modules/generated/sk learn . pipeline . pipeline . html
利用 Spark ML 进行面向管道的数据分析
作为一名机器学习顾问和敏捷顾问,我对提高大数据分析项目效率的不同方式很好奇。有许多方法可以优化它们,其中包括团队组成、流程、最佳实践、工具等。我想分享我使用机器学习管道的经验,以及它们如何改善机器学习工作流。
我们在各种框架中发现了机器学习管道,如 scikit-learn、Spark 或亚马逊 SageMaker。它们通常用于机器学习工作流程的后期阶段,包括特征编码、训练、预测和参数调整。我想展示如何将这种方法推广到早期阶段,例如特征工程、数据预处理、数据摄取和数据探索。
动机
我们见证了数据分析是如何经历巨大变化的。过去,通常是几个博士一起合作,向利益相关者提交一份包含商业见解的报告。今天,数据分析是多个角色的共同努力,例如分析翻译、数据科学家、机器学习工程师、数据工程师、软件工程师,他们更关心模型物流、敏捷、快速上市。从数据洞察到数据产品的整个过程需要适当的协调。当 it 效率低下时,将数据洞察力引入生产可能会有问题。让我们考虑一个有点夸张的例子。
想象一下这样一种情况,数据科学家将他们的新见解(比如 R 或 scikit-learn 脚本)交给数据工程师,让他们投入生产。数据科学家的编码标准不同于数据工程师在生产中使用的编码标准。虽然数据科学家专注于快速原型和可视化,但数据工程师更关注可伸缩性和可维护性。通常,为了让 data insights 投入生产,数据工程师必须使用生产工具(例如 Spark 或 TensorFlow)完全重写它们。当数据科学家审查由数据工程师产生的生产就绪代码时,他们经常发现,结果与他们预期的不同。发生这种情况有多种原因:同一方法在不同工具中的不同实现、沟通不畅或错误。通常,数据科学家必须带头解决这些问题,这可能导致代码再次失去生产准备。在数据科学家和数据工程师之间可能需要几次迭代的交接,直到代码最终可以投入生产并按预期工作。在迭代模型开发期间,同样的故事可能会重复:当模型不断变化,新版本必须经常投入生产时。
如果工作流的所有参与者都使用标准构件来描述他们的数据操作,那么移交过程将会更加顺利。这些构建模块必须能够投入生产,并且足够直观,可以用于实验。然后,机器学习工作流的所有参与者都可以使用它们来简化移交。
例子
我们将使用 Kaggle 的纽约出租车旅行时长挑战作为一个玩具示例来展示面向管道的方法。该挑战的目标是基于各种输入来预测出租车行程持续时间,如接送地理坐标、接送时间、乘客数量和其他一些信息。我们将使用 Spark 来实现机器学习工作流(项目代码可在这里)。Spark 具有内置的机器学习管道支持,它是生产就绪的,并且具有足够直观的 Python API 来进行实验。
地理位置在预测出租车行程持续时间中起着重要的作用。使用地理位置时,有时将其离散化并使用像元是有意义的。地理数据处理经常使用非本地库(比如我们的例子中的 s2sphere ),这使得相关的数据操作不如本地库有效。因此,通常预先计算一些地理位置相关特征值的查找表,以便能够在以后更快地使用它们。行程距离就是这样的特征之一。
让我们看看如何使用 Spark ML 管道来生成距离矩阵,该矩阵将用于快速查找两个单元之间的距离。为了构建这样一个矩阵,我们需要加载、预处理训练数据,并计算用于下车和上车地理位置的单元令牌。测试数据经过类似的处理。然后,在训练和测试数据集被联合之后,为唯一的拾取和放下单元标记对计算球面距离。
要使用 s2sphere 库和 PySpark 计算球面距离,我们需要定义两个用户定义的函数:cell_token_udf
将地理坐标映射到单元令牌,以及sphere_distance_udf
计算一对单元令牌之间的球面距离。
在我们可以使用cell_token_udf
生成单元令牌之前,我们需要首先从train.csv
加载训练数据,选择需要的列,重命名其中的一些列并规范化类型。
然后,对测试数据test.csv
重复相同的数据操作序列。
合并数据集后,选择唯一的像元标记对,使用前面介绍的sphere_distance_udf
来计算球体距离。结果会保存到拼花文件中。
距离矩阵生成的完整示例可在这里获得。
上面的代码有一些缺点。它不太容易维护:部分领域逻辑缺乏清晰的边界,并且分散在代码中。这个解决方案也不是非常可重用:想想它的一部分如何在不同的上下文中再次使用。此外,这段代码不容易测试,因为将它分解成可测试的单元不是很清楚。
面向管道的方法可以用来改善这些问题。
面向管道的方法
像面向对象范例中的对象或函数范例中的函数一样,面向管道方法的基本元素是管道阶段。这种方法基于两个基本原则,这两个原则定义了管道阶段以及如何组合它们:
流水线阶段
链接和嵌套
- 数据操作用两种流水线阶段来描述: 变换器 将数据帧映射到数据帧,以及 估计器 将数据帧映射到变换器。
- 管道阶段可以链接成管道,管道也是管道阶段。
让我们看看如何使用这个想法来改进前面提出的距离矩阵生成解决方案。
在前面的实现中有一个重复的逻辑,我们首先预处理训练数据集,然后用测试数据集重复它。我们可以将复制的逻辑实现为一个独立的管道,然后将其作为主管道中的一个阶段进行重用。
让我们定义一个函数,它基于输入参数(单元格大小指示器、要选择和重命名的列名以及要规范化的列类型)构建出行数据预处理管道。
现在,我们可以将上面定义的预处理管道嵌套在主管道中。
完整的例子在这里有。
这个实现产生的结果与前一个相同,但是它更紧凑。它由以下可重复使用的组件组成:
Pipe
是变形金刚的链条,也是变形金刚。它是pyspark.ml.PipelineModel
的简化实现,后者是一个只包含转换器(没有估算器)的管道。与PipelineModel
不同的是,Pipe
由于安全原因不能被持久化(这将在下面更详细地讨论)。SelectColumns
–是一个转换器(pyspark.ml.Transformer
),它根据给定的列名列表选择列。RenameColumns
–是一个转换器,它将给定的列字典中的旧列重命名为新名称。NormalizeColumnTypes
–是一个转换器,根据给定的列名字典将列类型转换为列类型。CellToken
–是一个转换器,它根据地理坐标、给定的像元大小级别(级别越高,生成的像元越小)、纬度和经度列名称以及包含结果的新列的名称来计算的像元标记。Union
–是一个将两个数据帧结合在一起的转换器。DropDuplicates
–是一个移除重复行的转换器。SphereDistance
–是一个 transformer,它添加了一个新列,该列具有基于 s2sphere 库为一对单元标记计算的球体距离,给出了拾取和放下单元标记的名称。SaveToParquet
–是一个将数据帧保存到拼花文件的转换器。CsvDataFrame
–是与csv
文件相关联的pyspark.sql.DataFrame
的实现。
后一种解决方案中的大部分工作都是由变压器完成的。下面列出了其中一个示例(完整的示例可在这里找到)。
范围狭窄的组件更容易测试。变压器测试示例如下所示(完整代码可在此处获得)。
如您所见,提议的面向管道的解决方案由以下组件组成:
- 范围狭窄的 —每个组件只负责一件事
- 模块化 —组件是松散耦合的
- 可测试的 —范围狭窄的组件易于测试
- 可维护的 —构建模块的变化对管道的其余部分有可预测的影响
- 可重用 —组件可以在不同的环境中轻松使用
- 实验就绪 —组件具有直观的 API,在实验过程中易于在 Jupyter 笔记本中使用
- 生产就绪 —组件是可扩展的,可以在生产环境中执行
限制
面向管道的实现也有局限性。我们来讨论其中的一些。
坚持。管道阶段以及管道本身在 Spark ML 中是持久的。例如,持久性允许在训练后保存拟合的管道及其所有参数,并在以后加载它们以服务于预测。持久性尤其是通过使用 Spark 的 Params API 实现的。Spark 支持许多可持久的参数类型,可以在可持久的管道阶段使用。我们在上面的转换器中使用了不支持的参数类型,比如字典(在RenameColumns
中)或者数据帧(在Union
中),这使得它们不可持久。我们引入了Pipe
,专门用来构建非持久化的变形器,并限制持久化的能力。为了使定制转换器持久化,还需要做一些额外的工作(持久化定制转换器的一个例子在这里的和这里的中可用)。但是,对于很多变形金刚来说,坚持是永远不需要的。
验证。上述实现中缺少的一个重要方面是管道阶段的模式验证。它可以极大地帮助早期识别管道缺陷,这在处理一系列耗时耗资源的大数据转换时会很有帮助。Spark native Scala API 提供了验证功能,但是我找不到通过 Python API 使用它们的简单方法。
副作用。在建议的实现中存在具有副作用的变压器,SaveToParquet
是其中之一。根据定义,转换器将数据帧映射到数据帧,这就很自然地将它们视为数据帧上的纯函数。然而,Spark 中的数据帧操作无论如何都是使用 SparkContext 执行的,这本身就自动暗示了副作用。
测试。正如我们所看到的,窄范围的转换器很容易被单元测试覆盖。但是在基础设施方面,这样的测试更类似于集成测试,因为它们使用 SparkContext 来运行转换。SparkContext 的初始化需要时间,与常规的单元测试相比,这使得此类测试明显变慢。为了使测试运行得更快,SparkContext 可以在测试之间共享,从测试的角度来看,这并不总是一个好主意,因为测试应该独立地运行。
通用。在创建自定义转换器时,面向管道的方法意味着一些额外的开销。这种方法不太适用于新业务领域中的小型单次工作项目。这种方法在共享的业务领域环境中变得有效,在这种环境中,不同的团队重用特定于领域的数据操作。面向管道的范例更有利于大型的长期运行的项目,其中多个角色在模型改进上迭代地工作,在这种情况下,模型经常被投入生产,并且上市时间是一个重要的因素。
结论
正如我们所见,面向管道的方法适用于机器学习工作流程的所有阶段。使用一组统一的构件使得工作流阶段之间的移交更加有效。记住它们的局限性,机器学习管道可以用来声明性地描述复杂的特定领域的数据操作。
我将在下一篇文章中继续探索面向管道的方法,我将很高兴知道你如何应对动机部分中描述的挑战。
参考
[2] Holder Karau,为定制车型扩展 Spark ML(2017)
使用 Spark ML 进行面向管道的数据分析。第二部分
我们继续探索基于 Spark ML 的面向管道的方法,这在之前的部分中有简要介绍。作为一个玩具示例,我们继续使用 Kaggle 挑战,其目标是根据接送时间、接送地理坐标、乘客数量和其他一些输入来预测出租车行程持续时间。我们的目标是在 toy ( github )项目上展示 ML 管道如何通过使用数量有限的生产就绪构建模块来提高机器学习工作流的效率,这些模块足够灵活,可以由不同的工作流参与者在不同的工作流阶段应用。
在我们继续之前,让我们回忆一下什么是面向管道的方法。
面向管道的数据分析
像 OOP 中的对象或 FP 中的函数一样,管道阶段是面向管道范例的核心元素。这种范式基于两个基本原则,一个是介绍构建模块,另一个是定义构建模块的方式,这些原则是:
- 数据操作用两种流水线阶段来描述: 变换器 将数据映射到数据,以及 估计器 将数据映射到变换器;
- 管道阶段可以链接成管道,管道也是管道阶段。
正如我们将看到的,这种简单的方法可以非常有表现力地描述模型生命周期各个阶段的数据操作。
用 ML 流水线进行数据预处理。
在简介部分中,我们展示了当我们必须构建距离矩阵时,如何在数据预处理阶段使用 ML 管道。这个距离矩阵应该用于快速行程距离查找,以避免昂贵的调用非本地的 Spark 外部的 s2sphere 库。
现在让我们看看如何在数据预处理期间使用 ML 管道从距离矩阵中查找行程距离(完整的示例可在此处获得)。
这里,首先,我们选择、重命名和归一化输入数据框的列,该数据框是从 csv 文件加载的。然后,我们添加具有 14 级拾取和放下单元标记的列(级别越高,单元的面积越小)。之后,生成的像元令牌用于将输入数据框与距离矩阵连接起来,距离矩阵存储在拼花文件中。最后,我们删除一些列,并将结果保存到 parquet 文件中。
如您所见,我们在之前的部分的矩阵生成示例中所熟悉的相同变压器被反复使用。通过重复使用变压器,我们的管道变得更加安全,因为我们使用经过测试、记录在案且可投入生产的组件。
面向管道方法的一些应用
在我们的例子中,我们展示了 ML 管道的各种应用。其中一些在之前的部分已经讨论过了。让我们简要地突出它们。
数据帧扩展。面向管道的方法基于一些参与者(数据帧、管道和管道阶段),我们不想通过添加更多参与者来使该模型变得复杂。因此一些重要的操作被实现为pyspark.sql.DatFrame
的扩展。其中:CsvDataFrame
—从 csv 文件中加载数据,ParquetDataFrame
—从 parquet 文件中加载数据,TempViewDataFrame
—使用临时视图(下面将详细介绍)。
日志记录、监控和其他副作用。具有副作用的 ML 管道在之前的部分中已经讨论过,以及一些关于其使用的问题。然而,当包装在身份转换器中时,各种副作用会非常有用。其中包括:持久性、日志记录、监控、数据验证、重新分区、检查点、缓存等。在正常情况下,它们中的每一个都会中断管道流,但是,作为管道阶段,它们自然可以用在 ML 管道中。在我们的例子中,我们使用SaveToParquet
转换器来保存数据帧。
超越线性。根据定义,ML 流水线是线性的,流水线阶段只接受一个输入数据帧,并且只产生一个结果数据帧或变换器。在我们的例子中,通过使用数据帧作为构造函数参数,我们能够在一个单独的转换器中组合几个数据帧(一些问题在之前的部分中已经讨论过)。我们之前用的Union
和Join
变压器就是那种变压器。
使用 ML 管道进行数据探索。我们看到了如何在数据预处理过程中使用面向管道的方法。尽管如此,没有什么能阻止我们在 Jupyter 笔记本的数据探索中使用相同的想法(这里有一些例子)。经过大量的聚合计算后,spark 数据帧可以转换为 pandas 数据帧,以便进一步可视化和分析。数据概要分析就是这样一种用例,尤其是在对新数据源重复执行大量标准数据质量检查时。我们可以想象变压器将数据帧映射到标准数据质量报告。它可能看起来像这样:
MissingValuesReport().transform(ParquetDataFrame(path, spark)) \
.toPandas().set_index(index_col).plot()
现在,让我们考虑一些面向管道方法的新用例,我们还没有遇到过。
条件 ML 管道
有时我们不得不中断管道的自然、逻辑流程,因为我们需要在一个 if-then-else 子句中检查某些条件。例如,机器学习有两个主要阶段:训练和预测。通常,相关的数据操作非常相似,但不完全相同。因此,将训练和预测管道放在一起以保持它们同步并避免代码重复是很方便的。
让我们考虑由三个步骤组成的特征提取流程:1)特征提取;2)标签提取(应该仅在训练期间发生,因为标签在预测期间不可用);3)清理并保存。
在这种情况下,ML 管道有助于减少代码重复。让我们首先考虑一种简单的方法,其中我们定义了对应于上述三个步骤的三个管道。稍后,当组装火车或预测主要特征工程管线的版本时,这些管线被用作管线阶段。如果标签列不可用,则在预测过程中装配特征工程管线时会省略标签提取管线。
由于管道嵌套,我们设法减少了代码重复。然而,与此同时,特征工程流程的自然逻辑被分成三部分,这使得流水线定义不太容易理解。此外,一旦训练流和预测流被分离,我们需要额外注意在每次改变它们中的任何一个时保持它们的同步。
我们能在不中断管道流的情况下达到同样的结果吗?下面的例子展示了如何在面向管道的方法中实现一个 if-then-else 子句,并将其用于特性工程(完整的例子)。
这里的[IF](https://github.com/bbiletskyy/pipeline-oriented-analytics/blob/master/src/pipeline_oriented_analytics/pipe/IF.py)
是一种特殊类型的流水线,在这里根据条件执行转换。条件被定义为谓词,[IF](https://github.com/bbiletskyy/pipeline-oriented-analytics/blob/master/src/pipeline_oriented_analytics/pipe/IF.py)
接受它作为构造函数的参数。它可以是任何类型的Callable[[DataFrame], bool]
谓词,在我们的例子中,in_train_phase
谓词检查流程是否处于训练阶段。与前一种情况类似,这是通过检查输入数据框列中是否存在标签列duration_sec
来实现的:
def in_training_phase(df: DataFrame) -> bool:
return 'duration_sec' in df.columns
基于IF
管道的实现看起来可读性更好,也更容易维护。完整的管道流是可见的,这使得管道定义更容易理解。训练流和预测流放在一起,这有助于保持它们的同步。
作为管道变量的数据帧
在某些情况下,我们需要将一个数据框与其先前的版本连接起来,只需进行几次变换。通常,这是通过引入临时变量来存储数据帧快照来实现的。但随之而来的是打破原有管道自然流动的代价。
上面特性工程管道中的RequestCount
变压器就是这样一个例子。这个转换器的目的是添加一个列,其中包含某个时间段内某个单元中的出租车请求数。让我们首先考虑一个简单的实现,其中对应的_transform
方法如下所示。
在这里,我们创建了几个临时的时间相关列,这些列稍后将与单元标记列一起用于分组和连接。然后,使用聚合进行分组,以计算请求数。最后,在连接中间结果之后,删除临时列。
如果转换器添加新列而不更改现有列中的数据,则链接转换器很容易。在使用聚合进行分组的情况下,情况并非如此,因为此类操作会显著改变原始数据框的结构。
下面的例子展示了实现RequestCount
转换器的另一种面向管道的方法。它利用 Spark 的临时视图来存储数据帧快照,而不是使用数据帧变量(完整的示例)。
SaveToTempView
副作用关联转换器将数据帧保存为 Spark 会话的临时视图。一种特殊类型的数据帧TempViewDataFrame
用于从临时视图中访问数据。临时视图被另一个副作用关联变压器DropTempView
丢弃。
面向管道的实现更易于维护,因为它是基于经过测试、有文档记录和生产就绪的构建块。
ML 管道中的循环
我没有机会将这个想法付诸实践,但是循环加上条件语句和管道变量为我们提供了一个非常强大的计算模型。这个模型应该有足够的表达能力来描述机器学习算法中的数据转换。机器学习方法常用作黑箱;抽象出了它们的实现细节。使用 ML 管道描述模型训练过程的数据操作将是很好的;这将使我们能够更容易地建立定制的机器学习方法,并为我们提供一个统一的形式,可以在机器学习工作流程的所有阶段使用。
结论
我们在 ML 工作流程的不同阶段看到了面向管道方法的各种应用。这种方法通过提供一种组织、测试和维护相关数据操作的策略,可以使 ML 工作流开发更加有效。这对于共享公共业务领域的项目尤其有益,在这些领域中重用定制的特定于领域的管道阶段的机会更高。
参考
[2] Holder Karau,为定制车型扩展 Spark ML(2017)
Scikit 中的管道和定制变压器-了解
带有代码片段的介绍性说明…
机器学习学术课程往往几乎只关注模型。有人可能会说,模型是表演魔术的东西。这种说法可能有一定的道理,但这种魔力只有在数据形式正确的情况下才能发挥作用。此外,让事情变得更复杂的是,“正确的形式”取决于模型的类型。
演职员表:https://www . free pik . com/free-vector/pipeline-brick-wall-background _ 3834959 . htm(*我更喜欢 MarioBros。图片…但是你知道:版权)
以正确的形式获取数据被业界称为预处理。这花费了机器学习从业者大量的时间。对于工程师来说,预处理和拟合或者预处理和预测是两个不同的过程,但是在生产环境中,当我们为模型服务时,没有区别。它只是数据输入,预测输出。管道就是用来做这个的。它们将预处理步骤和拟合或预测集成到单个操作中。除了帮助模型生产就绪,他们还为实验阶段增加了大量的可重复性。
学习目标
- 什么是管道
- 什么是变压器
- 什么是定制变压器
资源
- 管道&sci kit 中的自定义变压器-学习:分步指南(带 Python 代码)
- 使用 Python 定制转换器的 ML 数据管道
- 如何在 Python 中转换回归的目标变量
- 使用 Scikit-learn 管道和特征联合
参考
从 Scikit 学习文档中,我们可以获得:
数据集变换…与其他估计器一样,这些估计器由具有拟合方法和变换方法的类表示,拟合方法从训练集中学习模型参数(例如,归一化的均值和标准差),变换方法将此变换模型应用于看不见的数据。fit_transform 可以更方便和有效地同时对训练数据进行建模和转换。
我们将重点介绍两种变压器类型,即:
定制变压器
虽然 Scikit learn 附带了一套标准的变压器,但我们将从一个定制的变压器开始,以了解它们的功能和工作原理。首先要记住的是,自定义转换器既是估计器又是转换器,所以我们将创建一个从 BaseEstimator 和 TransformerMixin 继承的类。用 super()初始化它是一个很好的做法。init()。通过继承,我们免费得到了 get_params、set_params 这样的标准方法。在 init 中,我们还想创建模型参数或我们想学习的参数。
**class** **CustomScaler**(BaseEstimator, TransformerMixin):
**def** __init__(self):
super().__init__()
self.means_ = **None**
self.std_ = **None**
**def** fit(self, X, y=**None**):
X = X.to_numpy()
self.means_ = X.mean(axis=0, keepdims=**True**)
self.std_ = X.std(axis=0, keepdims=**True**)
**return** self
**def** transform(self, X, y=**None**):
X[:] = (X.to_numpy() - self.means_) / self.std_
**return** X
fit 方法是“学习”发生的地方。这里,我们基于生成模型参数的训练数据来执行操作。
在转换方法中,我们将在 fit 中学习到的参数应用于看不见的数据。请记住,预处理将成为整个模型的一部分,因此在训练期间,拟合和变换将应用于同一个数据集。但是之后,当您使用经过训练的模型时,您只能根据训练数据集而不是看不见的数据,应用带有通过 fit 学习的参数的变换方法。
重要的是,无论要应用的数据是什么,学习到的参数以及变压器的操作都是相同的。
标准变压器
Scikit learn 自带各种开箱即用的标准变压器。考虑到它们几乎不可避免的使用,你应该熟悉数字数据的标准化,或均值去除和方差缩放和简单估算器,以及分类的编码分类特征,特别是 one-of-K,也称为 one-hot 编码。
管道
链接估计量
请记住,变压器是一个估计,但你的模型也是(逻辑回归,随机森林等)。).你可以把它想象成台阶垂直堆叠。这里秩序很重要。所以你要把预处理放在模型之前。关键是一步输出是下一步输入。
特征联合:复合特征空间
通常,您希望对某些要素应用不同的变换。数字数据和分类数据所需的转换是不同的。这就好像你有两条平行的路,或者它们是水平堆叠的。
并行路径的输入是相同的。因此,转换方法必须从选择与转换相关的特征开始(例如,数字特征或分类特征)。
例子
我们将为 Kaggle 的泰坦尼克号数据集做预处理流水线。你可以在这里找到卡格勒斯的教程。
演职员表:https://commons . wikimedia . org/wiki/RMS _ Titanic #/media/File:Titanic _ in _ color . png
对于我们的工作,您可以遵循下面提供的要点中的步骤(在一个新的选项卡中打开它,然后跟着做)。它包含了所有的代码。为了更好地理解,我们将把它分开。
现在让我们开始吧。解压文件并加载数据后,进行快速浏览。
# loading and explorationfilename = '/content/working_directory/train.csv'
raw_train = pd.read_csv(filename)
print('data set shape: ', raw_train.shape, '**\n**')
print(raw_train.head())data set shape: (891, 12)
PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 NaN S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 NaN S
3 4 1 1 ... 53.1000 C123 S
4 5 0 3 ... 8.0500 NaN S
[5 rows x 12 columns]
现在,在删除我们将不使用的功能(乘客 Id、姓名、机票、客舱、已登机)并分离标签(幸存)后,六(6)个功能保留下来,即:Pclass、性别、年龄、SibSp、Parch 和 Fare。
dr = ['PassengerId','Name','Ticket','Cabin','Embarked']
train = raw_train.drop(labels = dr, axis = 1)
X = train.drop('Survived', axis=1)
y = train['Survived'].values
print('data set shape: ', X.shape, '**\n**')
print(X.head())
print(X.describe())data set shape: (891, 6)
Pclass Sex Age SibSp Parch Fare
0 3 male 22.0 1 0 7.2500
1 1 female 38.0 1 0 71.2833
2 3 female 26.0 0 0 7.9250
3 1 female 35.0 1 0 53.1000
4 3 male 35.0 0 0 8.0500
Pclass Age SibSp Parch Fare
count 891.000000 714.000000 891.000000 891.000000 891.000000
mean 2.308642 29.699118 0.523008 0.381594 32.204208
std 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.420000 0.000000 0.000000 0.000000
25% 2.000000 20.125000 0.000000 0.000000 7.910400
50% 3.000000 28.000000 0.000000 0.000000 14.454200
75% 3.000000 38.000000 1.000000 0.000000 31.000000
max 3.000000 80.000000 8.000000 6.000000 512.329200
注意,有数字(Pclass ',' Age ',' SibSp ',' Parch ',' Fare ')和分类(Sex ')特征,它们的预处理是不同的。还要注意,并非所有乘客年龄值都可用。
*# count missing values*
X.isna().sum()Pclass 0
Sex 0
Age 177
SibSp 0
Parch 0
Fare 0
dtype: int64
自定义估算器
年龄大概是预测存活几率的一个关键因素。因此,为了让模型充分发挥作用,我们需要填充缺失的值。一种替代方法是使用数据集年龄平均值。但是性别、阶级和年龄之间是有关联的。男性比女性年长,而且上层阶级的乘客也比下层阶级的乘客年长。我们可以用它来得出一个比一般平均值更好的重置价值。我们将使用由性别和阶级给出的类别的平均值。请注意,我们使用了两个分类特征(Pclass 和 Sex)来对点进行分组,以填充数字特征(年龄)的缺失值。
*# Custom Transformer that fills missing ages*
**class** **CustomImputer**(BaseEstimator, TransformerMixin):
**def** __init__(self):
super().__init__()
self.age_means_ = {}
**def** fit(self, X, y=**None**):
self.age_means_ = X.groupby(['Pclass', 'Sex']).Age.mean()
**return** self
**def** transform(self, X, y=**None**):
*# fill Age*
**for** key, value **in** self.age_means_.items():
X.loc[((np.isnan(X["Age"])) & (X.Pclass == key[0]) & (X.Sex == key[1])), 'Age'] = value
**return** X
数字特征流水线
选择适当的特性后,我们将执行一个简单的估算器和一个标准缩放器。前面介绍的 CustomScaler 与预构建的 Scikit-learn StandardScaler 执行相同的操作。
**class** **NumericalTransformer**(BaseEstimator, TransformerMixin):
**def** __init__(self):
super().__init__()
**def** fit(self, X, y=**None**):
**return** self
**def** transform(self, X, y=**None**):
*# Numerical features to pass down the numerical pipeline*
X = X[['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']]
X = X.replace([np.inf, -np.inf], np.nan)
**return** X.values*# Defining the steps in the numerical pipeline*
numerical_pipeline = Pipeline(steps=[
('num_transformer', NumericalTransformer()),
('imputer', SimpleImputer(strategy='median')),
('std_scaler', StandardScaler())])
分类特征管道
在选择适当的特性(性别)后,我们将通过预构建的转换 OneHotEncoder 执行热编码。
**class** **CategoricalTransformer**(BaseEstimator, TransformerMixin):
**def** __init__(self):
super().__init__()
*# Return self nothing else to do here*
**def** fit(self, X, y=**None**):
**return** self
*# Helper function that converts values to Binary depending on input*
**def** create_binary(self, obj):
**if** obj == 0:
**return** 'No'
**else**:
**return** 'Yes'
*# Transformer method for this transformer*
**def** transform(self, X, y=**None**):
*# Categorical features to pass down the categorical pipeline*
**return** X[['Sex']].values*# Defining the steps in the categorical pipeline*
categorical_pipeline = Pipeline(steps=[
('cat_transformer', CategoricalTransformer()),
('one_hot_encoder', OneHotEncoder(sparse=**False**))])
水平堆叠
分类管道和数字管道并行但独立运行。它们有相同的输入,但产生独立的输出,我们将重新连接这些输出。为了重新加入它们,我们使用 FeatureUnion。
*# Combining numerical and categorical pipeline into one full big pipeline horizontally*
*# using FeatureUnion*
union_pipeline = FeatureUnion(transformer_list=[
('categorical_pipeline', categorical_pipeline),
('numerical_pipeline', numerical_pipeline)])
垂直堆叠
因为我们需要自定义估算器的分类和数字特征(我们在其中填充缺失的年龄值),所以它出现在并行管道之前,现在一起作为预处理管道。为此,我们使用 Scikit Learn 的管道。
*# Combining the custom imputer with the categorical and numerical pipeline*
preprocess_pipeline = Pipeline(steps=[('custom_imputer', CustomImputer()),
('full_pipeline', union_pipeline)])
模型
我们将使用 Scikit Learn 的决策树分类器。这里的重点不是模型,而是查看转换和管道运行的借口。DecisionTreeClassifier 是我们在预处理管道之后堆叠的另一个估计器。
为了查看所有正在进行的操作,我们将在 full_pipeline 上调用 fit,即预处理和建模,以及稍后的预测。
*# MODEL*
**from** **sklearn** **import** tree
*# Decision Tree*
decision_tree = tree.DecisionTreeClassifier()*# define full pipeline --> preprocessing + model*
full_pipeline = Pipeline(steps=[
('preprocess_pipeline', preprocess_pipeline),
('model', decision_tree)])
*# fit on the complete pipeline*
training = full_pipeline.fit(X, y)
print(full_pipeline.get_params())
*# metrics*
score_test = \
round(training.score(X, y) * 100, 2)
print(f"**\n**Training Accuracy: **{**score_test**}**")
最后是预测部分:
*# Prediction*
my_data = X.iloc[[77]]
y = full_pipeline.predict(my_data)
print(my_data, y)Pclass Sex Age SibSp Parch Fare
77 3 male -0.211777 0 0 8.05 [0]
关闭
这个非常简短的关于 Scikit learn 的转换和管道的叙述,应该已经为您提供了以生产就绪和可重复的方式集成机器学习模型中的预处理阶段的工具。希望你喜欢。编码快乐!
scikit-learn 中的管道和定制变压器:分步指南(带 Python 代码)
了解 scikit 的基本知识和工作方式——从头开始学习管道,这样您就可以构建自己的管道。
本文将涵盖:
- 为什么另一个关于管道的教程?
- 从头开始创建一个自定义转换器,以包含在管道中。
- 修改和参数化变压器。
- 通过TransformedTargetRegressor定制目标转换。
- 将所有东西链接在一个管道中。
- 链接到从 GitHub 下载完整代码。
对于那些喜欢这种格式的人来说,最后有一个代码的视频演示。我个人喜欢书面教程,但过去也有人要求视频版本,所以就有了。
为什么还要有关于管道的教程?
既然你在这里,很有可能你已经知道管道通过预处理数据使你的生活变得简单。我也听说了,并试图在我的代码中实现一个。
对我能找到的关于这个主题的几个很棒的教程的大声呼喊!我建议你在这篇文章之前或之后浏览一下:
一、https://towards data science . com/custom-transformers-and-ml-data-pipelines-with-python-20 ea 2 a 7 ADB 65
二、https://machine learning mastery . com/how-to-transform-target-variables-for-regression-with-scikit-learn
三、http://Zac Stewart . com/2014/08/05/pipelines-of-feature unions-of-pipelines . html
在跟随教程和使用标准的输入、缩放、功率变换等时,一切都很好。但是后来我想编写应用于数据的特定逻辑,并且不太确定什么在哪里被调用?
我试图寻找一个清晰的解释,说明构造函数 fit(),transform()函数何时被调用,但是找不到一个简单的例子。因此,我决定一步一步地完成代码,并向任何想从头开始理解它的人展示我的理解。
那我们开始吧!
从头开始创建自定义转换器,以包含在管道中
创建数据框架
为了更好地理解示例,我们将创建一个数据集来帮助我们更好地探索代码。
上面的代码创建了遵循等式y = X1 + 2 * sqrt(X2)
的数据。这确保了简单的线性回归模型不能完美地拟合它。
让我们看看有哪些预测结果抛给了我们:
原始数据的线性回归预测
完美的预测是 14 和 17。预测并不差,但是我们是否可以对输入要素进行一些计算以使预测更好?
输入特征操作后的预测
输入操作使其符合完美的线性趋势(现在是y=X1+X2
),从而得到完美的预测。现在,这只是一个例子,但是假设对于一个数据集,你的分析说这样的输入转换是好的,你如何通过管道以一种安全的方式做到这一点。
让我们看一个使用管道拟合的基本 LinearRegression()模型。
带有管道的 LinearRegression()
不出所料,我们得到了与第一次尝试相同的预测。此时的语法非常简单—
- 我们使用内部有
steps
数组的Pipeline
类声明了一个pipe1
变量。该步骤的名称(在本例中为linear_model
)可以是您选择的任何独特的名称。其后是一个实际的变压器或估计器(在这种情况下,我们的LinearRegression()
模型)。 - 像任何其他模型一样,它是根据训练数据拟合的,但是使用了
pipe1
变量。 - 使用
pipe1
对测试集进行预测,就像您在任何其他模型中所做的那样。
为了执行输入计算/转换,我们将设计一个定制的转换器。
定制输入变压器
我们创建一个类,并将其命名为ExperimentalTransformer
。我们设计的所有变形金刚都将继承BaseEstimator
和TransformerMixin
类,因为它们免费提供给我们已有的方法。你可以在我上面提供的文章链接中读到更多关于它们的内容。
这里有 3 种方法可以处理:
__init__
:这是构造器。初始化管道时调用。fit()
:拟合管道时调用。transform()
:在管道上使用 fit 或 transform 时调用。
目前,让我们将 print()消息放在 init & fit()中,并将我们的计算写在 transform()中。正如您在上面看到的,我们在那里返回修改后的值。当调用 fit()或 transform()时,所有的输入特征将被传递到X
。
让我们把它放到一个管道中,看看这些函数被调用的顺序。
管道中的实验变压器
你可以在上面的代码注释中看到,也可以使用更短的 make_pipeline() 语法来创建管道。
现在输出:
实验变压器输出
需要注意的 3 件重要事情:
a.init 在我们初始化pipe2
变量时被调用。
b.当我们对训练数据拟合管道时,我们的实验转换器的 fit()和 transform()都被调用。这是有意义的,因为这就是模型拟合的工作方式。在尝试预测 train_y 时,您需要变换输入要素。
c.当我们调用 predict(test_X)时,会像预期的那样调用 transform()-在进行预测之前,输入测试要素也需要平方根并加倍。
结果——完美的预测!
修改和参数化变压器
B ut..
我们在实验转换器的 transform()函数中假设列名是 X2。我们不要这样做,而是通过构造函数 init()传递列名。
这是我们的ExperimentalTransformer_2
:
向构造函数传递参数
注意保持函数参数和类变量中的参数名完全相同(feature_name
或任何你选择的名字)。当我们试图转换目标特征(y)时,改变它会导致问题。由于某种原因,它导致了对 init 的双重调用。
我还添加了一个带有默认值的 additional_param,只是为了混淆一下。在我们的例子中,它并不真的需要,而是作为一个可选的参数。
现在创建新管道:
用新管道调用
输出符合预期:
新输出,与以前相同
通过 TransformedTargetRegressor 自定义目标转换
W 什么情况下需要做一些前后处理?
考虑一个稍微修改过的数据集:
数据集中的 y 平方
一切照旧,但现在y
已经摆平。为了将它拟合到一个简单的线性模型中,我们需要在拟合我们的模型之前对y
求平方根,然后对模型做出的任何预测求平方。
我们可以使用 scikit-learn 的TransformedTargetRegressor
来指示我们的管道对目标变量执行一些计算和反计算。让我们首先编写这两个函数:
变换和逆变换函数
一个平方根y
,另一个平方回来。
通过管道调用:
TransformedTargetRegressor 调用
TransformedTargetRegressor 类接受将我们的管道连接到这些新函数的regressor
、func
和inverse_func
参数。
请注意我们现在如何安装model
,而不是管道。
不过,输出显示了一些有趣且出乎意料的东西:
TargetRegressor 的第一个输出
结果很好,但是您能看到当 fit()被调用时,我们的 target_transform()和 inverse_target_transform()方法是如何被多次调用的吗?这将成为大型项目和复杂管道的开销。处理这个问题所需的更改只是将 TransformedTargetRegressor 的check_inverse
参数设置为 False。我们将在下一步中这样做,同时寻找另一种处理目标转换的方法——在 TransformedTargetRegressor 中使用transformer
param,而不是 func 和 inverse_func。
我们可以传递一个内置的转换器或我们自定义的转换器,而不是我们设计的两个函数。定制的转换器看起来与我们之前为我们的管道设计的几乎相同,但是在它里面有一个额外的inverse_transform
功能。实现如下:
自定义目标变压器
就这样,现在就在我们的 TransformedTargetRegressor 调用中使用它:
用变压器参数调用
输出现在看起来是固定的:
没有重复调用的输出
这里要做的最后一件事。我们将利用缓存来保存计算,并查看如何从外部获取或设置我们的管道参数(如果您想在此基础上应用 GridSearch,稍后将需要这一点)。
get_params()
请注意,如何通过使用名称后跟双下划线__
来访问管道每个组件的每个参数。
我们将把它们联系在一起,甚至尝试从外部设置一个参数——我们已经传递给构造函数的列名X2
。
把这一切联系在一起
完整代码:https://github . com/HC grit/machine learning-iamJustAStudent/tree/master/pipeline foundation
代码走查
接下来呢?
如果不使用 FeatureUnion 或 ColumnTransformer ,任何实际的流水线实现都很难完成。我在上面提供的第一个参考链接会带你浏览 FeatureUnions。我发现对 ColumnTransformers 和 FeatureUnions 非常有帮助的参考资料有:
一.https://sci kit-learn . org/stable/modules/compose . html # feature union-composite-feature-spaces
二https://scikit-learn . org/stable/modules/generated/sk learn . compose . column transformer . html # sk learn . compose . column transformer
此外,您最终将在您的模型上使用 GridSearch。在这里用管道解释一下:https://scikit-learn . org/stable/auto _ examples/compose/plot _ feature _ union . html?高亮显示=管道
既然您已经很好地掌握了管道创建的基础,使用这些概念应该很容易。
对分享想法、提问或简单讨论想法感兴趣?在 LinkedIn 、 YouTube 、 GitHub 上或通过我的网站与我联系:我只是一个学生。
回头见&学习愉快!
www.linkedin.com](https://www.linkedin.com/in/himanshu-chandra-33512811/)
Pipenv 到 Heroku:轻松部署应用程序
使用 Pipenv 将应用程序从 GitHub 部署到 Heroku 的指南
作者: Edward Krueger 数据科学家兼讲师和 Douglas Franklin 助教兼技术作家。
在本文中,我们将介绍如何使用 Github 资源库中的 Pipfile 部署应用程序,从而简化应用程序部署!
照片由 Jesus Kiteque 在 Unsplash 上拍摄
有关虚拟环境或 Pipenv 入门的更多信息,请查看本文!
数据科学和部署问题
数据科学家通常是跨学科的,并没有被教导如何与其他人合作并将项目推向生产。因此,通常缺乏部署、适当的环境和包管理技能。这给复制、部署和共享项目带来了困难。可再现的数据科学项目是那些允许其他人在您的分析基础上重新创建和构建,并重用和修改您的代码的项目。
由于缺乏对虚拟环境的了解或经验,新开发人员通常在系统级安装所有东西。用 pip 安装的软件包被放置在系统级。对每个项目都这样做的结果是一个臃肿且难以管理的单一 Python 环境。在这种环境下开发应用程序的人如何知道在 requrements.txt 中包含哪些依赖项?
数据科学家需要能够将他们的模型投入生产。在模型开发过程中使用良好的实践大大简化了部署过程。即使是拥有独立部署或开发团队的数据科学家也可以从学习部署过程中受益。了解从开发到部署的完整工作流程的数据科学家可以交付一个更易于部署的产品。这种意识可以减轻开发人员和 DevOps 团队的压力。
有效的环境管理可以节省时间,并允许开发人员和数据科学家创建易于部署的独立软件产品。
Pipenv 和 Heroku
Pipenv 将软件包管理和虚拟环境控制结合到一个工具中,用于安装、删除、跟踪和记录您的依赖关系;以及创建、使用和管理您的虚拟环境。Pipenv 本质上是将 pip 和 virtualenv 包装在一个产品中。
Heroku 提供许多软件服务产品。我们需要 Heroku 云平台服务来托管应用程序。不用担心;创建帐户和托管应用程序是免费的。云平台支持多种编程语言的应用,包括 Python、Node.js、Scala、Ruby、Clojure、Java、PHP 和 Go。
应用部署
一旦我们将一个带有 Pipfile 的应用程序推送到 Github,Heroku 允许我们快速地将一个应用程序从 GitHub 部署到 Heroku。确保您的 Pipfile 在项目的根目录下。我们的应用程序使用 SQLite 数据库进行部署。
我们需要对我们的应用程序项目文件进行一些修改,以便应用程序可以在 Heroku 上运行。
安装 gunicorn
Gunicorn 是一个 Python WSGI HTTP 服务器,它将为 Heroku 上的 Flask 应用程序提供服务。通过运行下面的代码行,您将 gunicorn 添加到您的 Pipfile 中,这是在 Heroku 的容器中运行您的应用程序所需要的。
pipenv install gunicorn
添加 Procfile
在项目根文件夹中创建一个Procfile
,并添加下面一行:
web: gunicorn app:app
第一个app
代表运行您的应用程序的 python 文件的名称或者应用程序所在的模块的名称。第二个app
代表你的应用名称,即 app.py,这个 Procfile 配合 gunicorn 和 Heroku 的 Dynos,远程为你的应用服务。
部署
一旦我们有我们的应用程序测试和工作在本地,我们把所有的代码推到主分支。然后在 Heroku 上,去部署一个新的 app 看看下面的页面。
选择 Github 并搜索您的存储库
接下来在 Heroku 上,选择 GitHub 并输入存储库的名称,然后点击 search。一旦您的用户名/存储库出现,单击连接。选择所需的分支,然后单击部署。
选择主服务器并部署分支
构建日志将开始填充页面上的控制台。注意,Heroku 首先查找 requirements.txt 文件,然后从 Pipenv 的 Pipfile.lock 安装依赖项。
一旦从 Pipfile.lock 构建了您的环境,并且构建成功,您将会看到下面的消息。
成功的应用部署
该应用程序已成功部署!点击查看按钮,查看 Heroku 上部署的应用程序。
自动部署
我们可以启用自动部署,让 Github master 的更改在推送时显示在 Heroku 上。如果您使用这种方法,您将希望确保始终有一个工作的主分支。
启用自动部署
结论
对于希望在生产中部署、构建或使用代码的数据科学家和开发人员来说,实践良好的环境和包管理至关重要。当数据科学家和开发人员在其工作流程的上游使用这些开发技能时,可以帮助团队。
有了一个管理良好的 Pipenv 和 Pipfile,Heroku 的服务器可以用最少的故障排除来重建我们的应用程序。这使得我们可以在几分钟内将 GitHub 中的应用程序项目目录转移到一个已发布的应用程序中。
使用 Pipenv 这样的环境和包管理器可以使包括部署在内的许多过程变得更加舒适和高效!有关虚拟环境或 Pipenv 入门的更多信息,请查看本文。我们希望本指南有所帮助。谢谢大家!
解释机器学习时要避免的陷阱——PDP/ICE 案例
莱奥·麦克拉伦(@leiomclaren) 在 Unsplash 上的照片
摘要
今天你可以找到很多关于可解释人工智能的文章,其中一些可以在这里找到****。最标准的可解释人工智能指南无疑是克里斯托夫·莫尔纳尔的这本书。当我看到最近的论文 解读机器学习模型时要避免的陷阱 ,时,我决定写一些关于它的博客。这是本文中提出的一个方面。
本文的重点是在解释部分依赖图(PDPs 个体条件期望图(ICE)时我们需要避免的陷阱。这些是事后技术,用于观察模型如何通过保持所有外部变量不变来做出决策,除了一个(在 PDP 的情况下还有两个)被视为感兴趣的特征。这个变量可以取所有可能的值,我们观察它对模型决策的边际影响。要有正确的理解请参考 本 。
定义
使用加性模型时,要素依赖性是一个问题,因为多重共线性会导致普通最小二乘法失败,或者使数据科学家难以解释模型的系数。但基于树的模型就不是这样了,无论它是决策树还是袋装集成(例如随机森林)还是提升树(像梯度提升机器)。在使用这种模型时,我们从来不关心严格检查外生变量之间的特征依赖性。为什么我们要?树基于单变量要素分割工作,最终多重共线性不会像 Logit 那样造成混乱。
然而,PDP/ICE 的工作假设感兴趣的特征和互补的特征集应该是独立的。他们为什么要独立?PDP/ICE 技术基于数据点的扰动。感兴趣特征的扰动由以下因素产生:
- 从特征等距网格替换。
- 数值的随机抽样。
- 使用分位数替换。(我们的图将基于百分位数)
在所有这三种情况下,当扰动时,从属特征会导致联合分布,该分布将具有在现实世界中不可行的外推数据点。在这种情况下解释一个特性的边际贡献会导致错误的解释。我们将很快在我们的例子中观察到这种现象。
数据和问题陈述
我们从来自ka ggle的 自行车共享数据集 开始分析。记录自行车共享信息的地点在华盛顿特区,在北半球(这对我们的分析很重要)。根据时间、季节、天气信息,我们需要预测预订数量(自行车租赁)。相应的 笔记本 为本文。****
我们从导入库开始
import numpy as np
import pandas as pd
import calendar
import seaborn as sns
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from yellowbrick.target import FeatureCorrelation
from yellowbrick.regressor import ResidualsPlot
from pdpbox import pdp, info_plots
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 100
我们加载数据集,提取基于时间的特征,并检查它。
df = pd.read_csv('train.csv')
df['datetime'] = pd.to_datetime(df['datetime'])#Convert to Pandas Datetime Type
df['year'] = df['datetime'].dt.year#Extract the Year as a Feature
df['month'] = df['datetime'].dt.month#Extract the Month as a Feature
df['hour'] = df['datetime'].dt.hour#Extract the Hour as a Feature
df.head() #Print the top 5 rows of the dataframe
数据样本(来源:作者)
特征相关性(连续变量)
我们计算连续变量的皮尔逊相关。我们观察到温度(“temp”)和“感觉”温度(“atemp”)高度相关。这为我们提供了评估 PDP 图的案例。
#Pearson Correlation for Continiuos Variables
continious_variables = df[['temp', 'atemp', 'humidity', 'windspeed','count']]
corr = continious_variables.corr()
corr = sns.heatmap(corr,
xticklabels=corr.columns,
yticklabels=corr.columns,
annot=True, fmt="f",cmap="Blues")
皮尔森相关性(来源:作者)
特征相关性(分类变量到连续变量)
我们观察到租赁预订的连续特征—温度因素(“temp”)和两个分类因素—季节(“season”)和月份(“month”)之间的相关性。计算两个相关性的方差分析表,即带有“季节”的“温度”和带有“月份”的“温度”。我们观察到在两种情况下 F 统计量的 P 值都为零。这表明在“月”和“季”的类别中,温度(“temp”)的组均值在统计上是不同的。这表明这些特征与线性相关。这是非常直观的,因为不同月份/季节的平均温度肯定会有所不同。
#Fit an OLS with temperature as the continious target variable
#and season as the explanatory categorical variable.
model = smf.ols(formula='temp ~ C(season)', data=df)
res = model.fit()
print('ANOVA - temp~season')
summary = res.summary() #OLS Summary
print(summary.tables[0]) #Print the Anova Table only#Fit an OLS with temperature as the continious target variable
#and month as the explanatory categorical variable.
model = smf.ols(formula='temp ~ C(month)', data=df)
res = model.fit()
print('ANOVA - temp~month')
summary = res.summary() #OLS SUmmary
print(summary.tables[0]) #Print the Anova Table only
方差分析表(来源:作者)
部分相关图(无相关变量)
我们首先拟合一个随机森林回归变量,确保我们不采用任何连续或分类的因变量。我们确保我们有一个很好的契合。残差图(如下所示)表明残差是均匀随机分布的,其平均值约为零。更不用说训练和测试的良好的 R 平方值。这两个因素都表明很适合。为了解释模型的预测决策,一个拟合良好的模型是绝对必要的。
除了 PDP 图(下面粗线所示),我们还有两个集群版本的 ICE 图。两条细细的蓝色线代表各种冰图(分别对应‘temp’高于平均值和低于平均值的边际效应)。PDP 从平均值 ~77 (出租销售)开始。随着温度(“temp”)的升高,租赁销售额持续增加,直到“temp”值达到 ~30。此后,销量下降,最终平均在 ~217 左右。相应的代码和情节如下。
#Drop the correlated variable 'atemp' and then fit
X,y = df[['year','hour','temp','humidity','windspeed','holiday','workingday']],df['count']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)model = RandomForestRegressor(n_estimators=200) #Fit a Random Forest with 2oo Trees
visualizer = ResidualsPlot(model, qqplot=True) #Instantiate a Residual Plot Classvisualizer.fit(X_train, y_train) # Fit the training data to the visualizer
visualizer.score(X_test, y_test) # Evaluate the model on the test data
visualizer.show()
拟合优度(没有因变量的回归)(来源:作者)
pdp_temp = pdp.pdp_isolate(
model=model, dataset=X_train, model_features=X_train.columns, feature='temp'
)
fig, axes = pdp.pdp_plot(pdp_temp, 'temp',center=False, cluster=True,n_cluster_centers=2,\
plot_lines=True, x_quantile=True, show_percentile=True, plot_pts_dist=True )
部分相关图(有无因变量)。左边的红色方块在大约 77 处,右边的在大约 217 处,标志着 PDP 的结束(来源:作者)
部分相关图(有相关变量)
在这种情况下,我们考虑两个高度相关的连续变量—“temp”和“atemp”都作为回归变量。PDP 看起来更平了,不是吗?PDP 起始值为 ~146 (租赁的平均销售额)。这与上面那个从 ~77 开始的形成对比。该图这次在大约值 ~211 处结束。为什么两个情节不一样?****
#Include the correlated variable 'atemp' and then fit
X,y = df[['year','hour','temp','atemp','humidity','windspeed','holiday','workingday']],df['count']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)model = RandomForestRegressor(n_estimators=200)
visualizer = ResidualsPlot(model, qqplot=True)visualizer.fit(X_train, y_train) # Fit the training data to the visualizer
visualizer.score(X_test, y_test) # Evaluate the model on the test data
visualizer.show()
拟合优度(因变量回归)(来源:作者)
部分相关图(有因变量)。左边的红色方块在 146 左右,右边的在 211 左右,标志着 PDP 的结束(来源:作者)
要避免的陷阱
PDP 与扰动一起工作,即对于每个数据点,我们外推感兴趣的特征的值(这里以百分位数替换)。相关变量导致虚假数据点。例如,在下表中,我们看到七月份的外推值为 0.82** ,以及“atemp”值为 32.5 (在表格中以红色框突出显示)。这样的数据点是不可行的(华盛顿特区 7 月的气温永远不可能是 0.82)。因此,每个变量的影响变得平滑,我们看到虚假的 PDP(在我们的情况下是一个更平坦的曲线)。对此的解决方案- 是在拟合之前移除一个相关变量(“temp”或“atemp”)(在这种情况下使用皮尔逊相关)。**
X_train['month'] = df[df.index.isin(X_train.index)]['month']
X_train['month'] = X_train['month'].apply(lambda x: calendar.month_abbr[x]) #Get the month
#Get all the possible values the feature temp(based on the quantile grids) is allowed to take w.r.t
#to all the data points and values of other features reamining constant.
X_train['extrapolated_temp'] = [pdp_temp.feature_grids.tolist()]*len(X_train)
X_train = X_train.explode('extrapolated_temp')
X_train.head()
七月份温度的外推值为 0.82(来源:作者)
部分相关图—分类情况(无相关变量)
现在,我们去掉‘temp’(以及‘atemp’),取而代之的是‘season’作为回归变量。因为季节是一个分类变量,所以我们取它的一次性编码版本。我们评估拟合优度并获得 PDP。PDP 揭示的是,随着“季节”的变化,春季或冬季的平均租赁销售量分别为 118.8 和 201.476。夏季和秋季销量最高(平均分别为 217 辆和 230.4 辆)。
#Removing the variable 'temp' and instead using the variable 'season'
X,y = df[['year','hour','season','humidity','windspeed','holiday','workingday']],df['count']
X = pd.get_dummies(X,columns=['season']) #One hot encode
X = X.rename(columns = {'season_1':'spring','season_2':'summer',
'season_3':'fall','season_4':'winter'}) #Proper Renaming of Dummies
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
X_train.head()
一个热编码变量“季节”(来源:作者)
model = RandomForestRegressor(n_estimators=200)
visualizer = ResidualsPlot(model, qqplot=True)visualizer.fit(X_train, y_train) # Fit the training data to the visualizer
visualizer.score(X_test, y_test) # Evaluate the model on the test data
visualizer.show()
拟合优度——分类案例(无因变量的回归)(来源:作者)****
pdp_season = pdp.pdp_isolate(
model=model, dataset=X_train, model_features=X_train.columns,
feature=['spring', 'summer', 'fall', 'winter']
)
fig, axes = pdp.pdp_plot(pdp_season,'season', center=False, cluster=True,n_cluster_centers=2,\
plot_lines=True, x_quantile=True, show_percentile=True)
部分相关图(有因变量)。春季、夏季、秋季和冬季的 PDP 值分别为 118.5、217.1、230.5 和 204.7。(来源:作者)
部分相关图—分类情况(有相关变量)
从我们的 ANOVA 表中,我们知道“季节”和“温度”是相互依赖的,所以现在除了“季节”,我们还包括“温度”。我们评估拟合优度,并最终评估 PDP。我们看到非常相似的结果,与没有相关变量的情况相比,这种情况下的 PDP 又平坦了很多。
#Included the dependent variable 'temp'
X,y = df[['year','hour','temp','season','humidity','windspeed','holiday','workingday']],df['count']
X = pd.get_dummies(X,columns=['season'])
X = X.rename(columns = {'season_0':'spring','season_1':'summer',
'season_2':'fall','season_3':'winter'})
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)#Fit the model
model = RandomForestRegressor(n_estimators=200)
visualizer = ResidualsPlot(model, qqplot=True)visualizer.fit(X_train, y_train) # Fit the training data to the visualizer
visualizer.score(X_test, y_test) # Evaluate the model on the test data
visualizer.show()
拟合优度—分类案例(因变量回归)(来源:作者)
pdp_season = pdp.pdp_isolate(
model=model, dataset=X_train, model_features=X_train.columns,
feature=['spring', 'summer', 'fall', 'winter']
)
fig, axes = pdp.pdp_plot(pdp_season,'season', center=False, cluster=True,n_cluster_centers=2,\
plot_lines=True, x_quantile=True, show_percentile=True)
部分相关图(有因变量)。春季、夏季、秋季和冬季的 PDP 值分别为 165.6、199.6、208.6 和 215.2。(来源:作者)
要避免的陷阱
因为“季节”和“温度”是相互依赖的,所以当两者都被用作回归变量时,我们看到这两个特征的效果变得平滑。这就是第二个 PDP 平坦很多的原因。更深入地说,我们观察“季节”的推断值。例如,第一行显示“七月”对应的外推季节是“春天”(在表中以红色突出显示)。这是一个不可行的数据点,因此会产生虚假的结果,因为两个因变量的边际效应相互抵消,并且图是平坦的。解决方案-** 我们必须检查连续分类依赖性,并从我们的回归变量列表中删除其中一个(使用 ANOVA )。**
X_train['month'] = df[df.index.isin(X_train.index)]['month'] #Get the Month
X_train['month'] = X_train['month'].apply(lambda x: calendar.month_abbr[x])#Get the Month
#Get all the possible values the feature season is allowed to take w.r.t to all
#the data points and values of other features reamining constant.
X_train['extrapolated_season'] = [pdp_season.feature_grids.tolist()]*len(X_train)
X_train = X_train.explode('extrapolated_season')
X_train.head()
一个推断的七月“春天”的季节(来源:作者)
结论
特性依赖不仅仅是关于相关性。我们可以使用卡方测试,以及非线性特性依赖测试(例如基于内核方法的测试)来进一步检查分类到分类的关系。上述相同的扣除也适用于 ICE 地块。
在本文中,我们展示了 PDP 和 ICE 图是如何因为依赖关系而被误解的。
参考
- Christoph Molnar、Gunnar knig、Julia Herbinger、Timo Freiesleben、Susanne Dandl、Christian A. Scholbeck、Giuseppe Casalicchio、Moritz Grosse-Wentrup、Bernd Bischl-https://arxiv.org/abs/2007.04131
- https://christophm.github.io/interpretable-ml-book/
- https://compstat-lmu . github . io/IML _ methods _ limits/PDP-correlated . html
使用的库/数据
- https://www.kaggle.com/c/bike-sharing-demand/data
- https://www.scikit-yb.org/en/latest/
- https://www . stats models . org/stable/generated/stats models . formula . API . ols . html # stats models . formula . API . ols
- https://sci kit-learn . org/stable/modules/generated/sk learn . ensemble . randomforestclassifier . html
- https://pdpbox.readthedocs.io/en/latest/
Python 中用于日交易的支点计算
让我们看看如何用 Python 计算日交易的中枢点
脸书股票价格在 15 分钟时间框架内与经典支点。图片由作者提供,使用 Tradingview 制作
日内交易和摆动交易有很大的不同。这更加困难,需要一些特殊的工具来盈利。支点是日内交易者可以使用的一些工具。
在这篇文章中,我解释了它们是如何工作的。
什么是支点?
支点是日内交易中经常使用的价格水平(但它们甚至可以用在摇摆交易中)。它们被认为是每日市场交易中价格的“天然”支撑和阻力,所以它们对日内交易者非常有用,因为当市场接近它们时,市场经常以一种不平凡的方式表现。
枢轴点有几种类型,但经典枢轴点通常是从最后一天的最高价、最低价和收盘价开始定义的。将一些数学计算应用到前一个交易日的价格,交易者可以计算出当前交易日的中枢水平,并使用它们。在本文中,我将重点关注经典支点。
它们是如何使用的?
经典枢轴点通常定义主枢轴级别。主要观点是,如果价格高于中枢水平,市场看涨。如果价格低于主要支点水平,市场是熊市。
主要的枢轴点之后通常是一些支撑位和阻力位,它们是日内市场的自然关键点。
下图显示了 15 分钟时间框架内的脸书股票价格。水平线是支点支撑(S1,S2,S3)和阻力(R1,R2,R3),而绿色水平线是支点水平(P)。
正如你所看到的,市场似乎经常拒绝这些价位,就像它们是真正的支撑和阻力一样。甚至枢轴级别本身也是如此。所以,很容易理解这种水平在日内交易中的重要性。
日内交易者可以在价格接近中枢水平时寻找交易机会,等待回调或突破,并将最近的中枢水平作为目标。
如何计算它们
传统的支点级别是根据以下公式计算的,这些公式取自 Tradingview 知识库(https://www . trading view . com/support/solutions/43000521824-pivot-points-standard/),应用于前一天的最高价、最低价和收盘价:
- PP =(高+低+收盘)/ 3
- R1 = 2 * PP -低
- S1 = 2 * PP -高
- R2 = PP +(高-低)
- S2 = PP -(高-低)
- R3 = PP + 2 *(高-低)
- S3 = PP - 2 *(高-低)
PP 是主要支点级别。S1、S2 和 S3 是支撑位。R1、R2 和 R3 是阻力位。
Python 中的计算
让我们看看如何在 Python 中计算这些级别。对于这个例子,我将关注脸书股票数据。整个笔记本可以在这里找到:https://github . com/gianlucamalato/machine learning/blob/master/Calculation _ of _ daily _ pivot _ levels . ipynb
首先,我们必须使用 yfinance 库获取股票数据。
!pip install yfinance
然后,我们可以导入我们需要的库。
import numpy as np
import pandas as pd
import yfinance
我们现在可以将脸书的数据导入熊猫的数据框架。
ticker = yfinance.Ticker("FB")
df = ticker.history(interval="1d")
这个数据框架的最后一个记录是最后一个交易日。我们将使用它的价格水平来计算我们今天可以使用的中枢水平。
last_day = df.tail(1).copy()
现在,根据枢纽公式,我们可以计算水平。
last_day['Pivot'] = (last_day['High'] + last_day['Low'] + last_day['Close'])/3last_day['R1'] = 2*last_day['Pivot'] - last_day['Low']last_day['S1'] = 2*last_day['Pivot'] - last_day['High']last_day['R2'] = last_day['Pivot'] + (last_day['High'] - last_day['Low'])last_day['S2'] = last_day['Pivot'] - (last_day['High'] - last_day['Low'])last_day['R3'] = last_day['Pivot'] + 2*(last_day['High'] - last_day['Low'])last_day['S3'] = last_day['Pivot'] - 2*(last_day['High'] - last_day['Low'])
结果是:
结论
枢轴点在日内交易中非常常用,并且在 Python 中很容易计算。这只需要一个单一的市场日数据,所以他们不需要太多的历史记录。日内交易者应该根据这些水平(或其他类型的中枢水平,如斐波那契线,Woodie 线,Camarilla 线)和策略类型(如突破或回调)制定交易策略。
使用熊猫的数据透视表和操作
图片来自 Unsplash
数据透视表对于分析来说是不够的,对数据透视表的操作才是关键
我们将学习如何有效地创建数据透视表并执行所需的分析。我们将用一个著名的汽车数据集来做这件事,这个数据集来自加州大学欧文分校。您可以通过以下链接找到数据集。建议您也阅读一些关于数据集的内容。我使用这个数据集作为我参加的数据分析在线课程的一部分。
https://archive . ics . UCI . edu/ml/machine-learning-databases/autos/imports-85 . data
因此,让我们先获取库,加载我们的数据,并浏览一下数据。
import pandas as pd
import numpy as nppath = "Downloads/imports-85.data"
df = pd.read_csv(path, header=None)
如果我们现在看数据,你什么都不能理解。不过,还是让我们来看一看吧。
df.head()
初始数据集
数据看起来像这样。首先,让我们给数据一些好的标题。
headers = ["symboling","normalized-losses","make","fuel-type","aspiration", "num-of-doors","body-style",
"drive-wheels","engine-location","wheel-base", "length","width","height","curb-weight","engine-type",
"num-of-cylinders", "engine-size","fuel-system","bore","stroke","compression-ratio","horsepower",
"peak-rpm","city-mpg","highway-mpg","price"]df.columns = headers
所以现在数据看起来像这样。
带标题的数据
数据清理
您需要执行一些数据清理步骤。但是,我不会详细谈论它们。因为我们正在讨论数据透视表,所以我假设您可能有一些关于数据清理的基础知识。为此,请使用下面的代码。
df.replace("?", np.nan, inplace = True)df["normalized-losses"].replace(np.nan, df["normalized-losses"].astype("float").mean(axis=0), inplace=True)
df["bore"].replace(np.nan, df['bore'].astype('float').mean(axis=0), inplace=True)
df["stroke"].replace(np.nan, df['stroke'].astype('float').mean(), inplace=True)
df["horsepower"].replace(np.nan, df['horsepower'].astype('float').mean(), inplace=True)
df["peak-rpm"].replace(np.nan, df['peak-rpm'].astype('float').mean(), inplace=True)
df[["price"]] = df[["price"]].astype("float")
我就简单介绍一下我刚才做的事情。如果您使用df.dtypes
检查每一列的数据类型,您会发现有些列的数据类型不正确,这是您在使用数据透视表时根本不想要的。例如,“价格”列的数据类型为“对象”,这是没有意义的。此外,数据有空值,问号(?)更确切地说。
为了解决这个问题,我们用 NAN 替换了它。然后,一些数字列的 NAN 值被该列的平均值所取代。
现在,既然我们有了干净的数据,让我们创建我们的第一个数据透视表。首先,我们必须从数据帧中选择我们想要的数据。
分组依据和透视表
df_selction = df[['fuel-type', 'drive-wheels', 'price']]
现在,我们将按照“燃料类型”和“驱动轮”这两列对数据进行分组。我们将使用汽车价格的平均值。
df_selection_gb = df_selction.groupby(['drive-wheels', 'fuel-type'], as_index=False).mean()df_selection_gb
分类资料
这可能有点难以阅读或执行任何分析。因此,我们把它转换成一个数据透视表,让我们看看我们得到了什么。
df_first_pivot = df_selection_gb.pivot(index='drive-wheels', columns='fuel-type')df_first_pivot
数据透视表
现在,您可能会注意到数据透视表中的一些 NAN 值。但这没关系,因为我们没有任何四轮驱动和柴油驱动汽车的数据。
可能的分析
有了上面的数据透视表,你可以回答这样的问题—
- 柴油动力车前轮驱动的均价是多少?
- 哪种车型最贵?
所以你可以得到答案,因为后轮驱动的柴油动力汽车是最贵的。假设我们想回答这样的问题-
- 四轮驱动的燃气汽车总数?
那么你可以用计数函数来代替均值—
df_selection_gb_2 = df_selction.groupby(['drive-wheels', 'fuel-type'], as_index=False).count()df_count_pivot = df_selection_gb_2.pivot(index='drive-wheels', columns='fuel-type')df_count_pivot
现在,让我们继续创建另一个组并尝试分析它。
df_selection_2 = df[['drive-wheels','body-style','price']]grouped_df = df_selection_2.groupby(['drive-wheels','body-style'],as_index=False).mean()grouped_df
正如您可以非常清楚地看到的,这种分组数据比第一种数据更难阅读和理解。因此,让我们在数据透视表中转换它,然后再试一次。
grouped_pt = grouped_df.pivot(index='drive-wheels',columns='body-style')grouped_pt
现在,这个更容易分析了。如果您想要在这些数据透视表上执行操作,您可以像普通的数据帧一样执行,但是在列命名上稍有不同。
透视操作
让我们看看数据透视表的列。
grouped_pt.columns
如你所见,它是多索引的。假设我们想得到轿车和旅行车车身风格的汽车的平均价格的差异。
grouped_pt[('price','sed-wag')] = (grouped_pt[('price', 'sedan')]-grouped_pt[('price', 'wagon')])grouped_pt
同样,如果您想要比较一段时间内的值,例如上个月和本月。你可以继续做同样的事情。您可能想要计算销售额的增长或者增长率。由于我们的数据集中没有日期列,我将向您展示一些您可能会发现的更多可能的见解。
例如,我们希望根据驱动轮列得到一辆轿车的平均价格。
为此,您可能需要删除我们刚刚创建的“sed-wag”列。
del(grouped_pt[('price','sed-wag')])
现在让我们用汽车的数量创建另一个枢纽。
df_selection_3 = df[['drive-wheels','body-style','price']]grouped_df_2 = df_selection_3.groupby(['drive-wheels','body-style'],as_index=False).count()grouped_count_pt = grouped_df_2.pivot(index='drive-wheels',columns='body-style')grouped_count_pt
让我们计算一下,以数据图表的形式看看我们的结果。
rate_4wd_sedan = (grouped_pt[('price', 'sedan')]) / (grouped_count_pt[('price', 'sedan')])rate_4wd_sedan.to_frame()
所以,你可以看到,根据驱动轮一栏,我们得到了轿车的平均价格。你可能会多玩一会儿,获得许多像这样有用的见解。
如果您有任何疑问,请随时联系我们,我们随时欢迎您提出建议。我希望这篇文章是有用的。
数据透视表 Python 中的数据聚合工具
在 Python 中有效使用 pivot_table()函数的多种方法
马库斯·温克勒在 Unsplash 上的照片
探索性数据分析是机器学习项目的一个重要阶段。奇妙的熊猫图书馆为此配备了几个有用的功能。其中之一是 pivot_table ,它在一个简洁的二维表格中总结了一个特性的值。数据分析软件中常见的数据汇总工具,提供了极大的灵活性。DataFrame 有一个 pivot_table 方法( 熊猫)。DataFrame.pivot_table 大多数情况下,我们最终会使用带有默认参数的 pivot_table。本文将通过优化使用默认参数来帮助您实现更多目标。
数据透视表()
pivot_table()方法返回一个 DataFrame ,这是一个 Excel 风格的数据透视表。数据透视表通过一个或多个键聚合数据表,将数据排列在一个矩形中,其中一些组键沿行排列,一些组键沿列排列,形成一个二维表,提供数据的多维汇总。
句法
**pandas.DataFrame.pivot_table()**
因素
https://pandas . pydata . org/pandas-docs/stable/reference/API/pandas。DataFrame.pivot_table.html
数据透视表()的基本用法
让我们通过在数据集上应用这个方法来研究它的基本用法。文中用到的网购数据集可以从 Kaggle 下载。为了直接进入代码,一个附带的笔记本发表在 Kaggle 上。
使用 Kaggle 笔记本探索和运行机器学习代码|使用网上购物的数据
www.kaggle.com](https://www.kaggle.com/tanyadayanand/multiple-ways-to-effectively-use-the-pivot-table)
导入数据集
让我们从导入必要的库和加载数据集开始。这是每个数据分析过程中必不可少的一步。
***# Importing necessary libraries***import pandas as pd
import numpy as np***# Importing and Loading the data into data frame***market_df = pd.read_csv("../input/market.csv")
customer_df = pd.read_csv("../input/customer.csv")
product_df = pd.read_csv("../input/product.csv")
shipping_df = pd.read_csv("../input/shipping.csv")
orders_df = pd.read_csv("../input/order.csv")***# Merging the dataframes to create a master_df***df_1 = pd.merge(market_df, customer_df, how='inner', on='Cust_id')
df_2 = pd.merge(df_1, product_df, how='inner', on='Prod_id')
df_3 = pd.merge(df_2, shipping_df, how='inner', on='Ship_id')
master_df = pd.merge(df_3, orders_df, how='inner', on='Ord_id')
提取前几行
master_df.head()
计算空值的数量
***#Identifying Missing Values in Column***
master_df.isnull().sum()
因此,列Product_Base_Margin
具有空值。
此时,我们对数据集的样子几乎没有任何概念。因此,现在让我们以多种不同的方式来研究 pivot_table()的用法,以进一步探索数据。
1.在数据透视表中使用索引对数据进行分组
索引是我们用来对数据进行分组的列、分组器、数组或列表。最简单的数据透视表需要一个 dataframe 和一个index
。索引要素将显示在结果表的索引列中。默认情况下,如果没有指定 value 和 aggfunc 参数,它将对所有数值列数据进行平均。
我将使用 Customer_Segment 列作为这里的 索引 :
***#a single index
#Using*** [***pandas.DataFrame.pivot_table***](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.pivot_table.html)master_df.pivot_table(index = 'Customer_Segment')***#Same as above - results in same output
#Using*** [***pandas.pivot_table***](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.pivot_table.html)pd.pivot_table(master_df, index = 'Customer_Segment')
该函数返回一个以 Customer_Segment 为索引的数据透视表,并对数字列进行平均。我们可以看到,数据透视表足够智能,可以根据 Customer_Segment 开始聚合数据并进行汇总。
运行具有多个索引的 pivot_table()
pivot_tabl e 可以通过一个列表将多个特征作为索引。这增加了结果表中的粒度级别,我们可以获得更具体的结果:
***#multiple indexes***
master_df.pivot_table(index =['Customer_Segment','Product_Category'])
这里,pivot_table 将产品类别与客户细分组合在一起,并为我们提供了一个组合汇总表。现在我们开始了解数据透视表能为我们做些什么。
2.用值聚合特定要素
值参数是我们告诉函数要聚合哪些特性的地方。这是一个可选字段,如果我们不指定该值,那么该函数将聚合数据集的所有数字特征。在前面的索引示例中,我们看到对所有数字列进行了聚合。由于没有指定value 参数,默认情况下,pivot_table 考虑所有数值列。
以下示例比较了客户群的平均销售额
***#Single value***
master_df.pivot_table(values = 'Sales', index = 'Customer_Segment')
销售列自动对数据进行平均,但是我们也可以进行计数或求和(通过添加参数aggfunc
和np.sum
或count
)。
聚合多个特征
类似于索引参数,值参数也可以通过一个列表获取多个特征。
***#multiple value***
master_df.pivot_table(values = ['Order_Quantity','Sales'], index = 'Customer_Segment')
数据透视表聚集了功能 Order_Quantity 和 Sales 的数据,并将其与 Customer_Segment 分组。
3.使用 aggfunc 操作数据
如前所述,默认情况下,pivot_table 使用均值函数(numpy.mean
)来聚合或汇总数据。但是还有其他重要功能或功能列表需要考虑。 aggfunc 是 pivot_table 应用于我们分组数据的聚合函数。aggfunc
(可选)接受我们希望在群组中使用的功能或功能列表。聚集规范可以是诸如'sum'
、'mean'
、'count'
、'min'
、'max'
等字符串,或者是实现聚集的函数(例如np.sum()
、min()
、sum()
等)。现在是试验aggfunc
参数的时候了。
第一个示例通过取平均值来聚合值:
***#Single aggrigate function(mean) and single value*** master_df.pivot_table(values = 'Sales', index = 'Customer_Segment', aggfunc = 'mean')
第二个示例通过求和来聚合值。它比较各地区的订单总数。
***#Single aggrigate function(sum) and single value*** master_df.pivot_table(values = 'Order_Quantity', index = 'Region', aggfunc = 'sum')
以下示例通过对多列求和来进行聚合。
***#Sum aggregate function is applied to both the values***
master_df.pivot_table(values = ['Order_Quantity','Sales'], index = 'Product_Category', aggfunc='sum')
具有多重聚合功能的透视表
我们不仅可以指定我们想要的聚合函数,还可以指定多个聚合函数。例如,如果我们对销售的总数和计数都感兴趣,我们可以将函数指定为参数 aggfunc 的列表。让我们试一试。
***#multiple Aggregating Function applied to single column***
master_df.pivot_table(values = 'Sales', index = 'Product_Category', aggfunc=['sum', 'count'])
我们还可以提供一个集合函数列表来应用于每个值。下面是同样的例子。
***#Sum and Mean aggregate function is applied to both the values***
master_df.pivot_table(values = ['Order_Quantity','Sales'], index = 'Product_Category', aggfunc=[np.sum, np.mean])
不同的功能使用不同的聚合函数
我们也可以对不同的特性使用不同的聚合函数。只需提供一个字典映射作为 aggfunc 参数的输入,以特性名作为键,以相应的聚合函数作为值。
在下面的例子中,我将使用 sum 作为Order _ Quantity特性和 mean 作为 Sales 特性。另外,请注意,我省略了关键字values
;为aggfunc
指定映射时,这是自动确定的。**
*****#different aggregate applied to different values*** master_df.pivot_table(index = 'Product_Category', aggfunc = {'Order_Quantity':sum, 'Sales':'mean'})**
当我们给出多个聚集函数时,我们将得到一个多索引的数据帧作为输出。
4.特征与列参数之间的关系
列参数在结果表的顶部水平显示数值。大多数时候,与使用columns
和values
相关的pivot_table
会被混淆。请记住,columns
是可选的,它们提供了一种补充方式来分割我们关心的实际值。聚合函数应用于我们列出的values
。
如果我们想查看按客户细分细分的利润,那么columns
参数允许我们定义一个或多个列。这里,我们比较了不同产品类别和不同客户群的总利润。
*****#Single column
#Grouping by both rows and column*** master_df.pivot_table(values = 'Profit',
index = 'Product_Category',
columns = 'Customer_Segment',
aggfunc = 'sum')**
具有多列的 pivot_table()
使用多个特性作为索引没问题,但是使用一些特性作为列将帮助我们快速理解它们之间的关系。同样,通过合并 pivot_table 的列参数,可以更好地查看结果表。
*****#multiple columns***
master_df.pivot_table(values = 'Profit',
index = 'Customer_Segment',
columns = ['Product_Category','Ship_Mode'],
aggfunc = 'count')**
****列和索引参数都是可选的,但是有效地使用它们将帮助我们直观地理解特征之间的关系。
5.添加总行数/列数
有时计算每个分组的总数是很有用的。现在,如果我们想查看一些数据的总和,margins=True
会帮我们做到。margins
是将所有行和列相加的布尔类型(如小计/总计),默认为“ False”。
*****#Margin***
master_df.pivot_table(values = ‘Profit’,
index = ‘Product_Category’,
columns = ‘Customer_Segment’,
aggfunc = ‘sum’, margins=True)**
边距标签可以用关键字margins_name
指定,默认为"All"
。margins_name
类型为 string,接受当 margins 等于 True 时包含总计的行/列的名称。
*****#margins_name***
master_df.pivot_table(values = 'Profit',
index = 'Product_Category',
columns = 'Customer_Segment',
aggfunc = 'sum',
margins=True,
margins_name ='TOTAL')**
这两个参数都是可选的,通常有助于改善显示。
6.处理缺失数据
奶奶的有点让人分心。 pivot_table 通过参数 dropna 和 fill_value 帮助我们处理。 其中的两个选项fill_value
和dropna
与缺失数据有关,并且相当简单。
dropna
是布尔类型,允许我们在条目都是NaN
的分组表中删除空值。它默认为 True。fill_value
是标量类型,可用于用我们提供的值替换表中的 NaN 值。默认为无。
*****#Displaying NaN values in the table
#These can be imputed using fill_value*** master_df.pivot_table(values = 'Product_Base_Margin',
index = 'Customer_Name',
columns = 'Customer_Segment',
aggfunc = 'mean')**
我将用来自 Product_Base_Margin 列的平均值替换 NaN 值:
*****#imputing with mean using fill_value***
master_df.pivot_table(values = 'Product_Base_Margin',
index = 'Customer_Name',
columns = 'Customer_Segment',
aggfunc = 'mean', fill_value=np.mean(master_df['Product_Base_Margin']))**
我们没有所有条目都是NaN
的列,但是值得一提的是,如果我们有pivot_table
会根据dropna
的定义默认删除它们。
结论
因此,我们可以看到 pivot_table()是一个方便的工具,我们可以用这一行代码进行一些有趣的分析。当你构建数据透视表时,我认为一步一步来是最简单的。添加项目并检查每个步骤,以验证您是否获得了预期的结果,并查看哪种演示最符合您的需求。一旦您开始处理数据并慢慢添加项目,您就可以感受到它是如何工作的。
我们已经介绍了pivot_table
的强大参数,所以如果你在你的项目中使用这些方法进行实验,你会得到很多。
参考
熊猫。DataFrame.pivot_table 文档
熊猫的数据透视表
数据透视表不仅仅是 Excel 的事情
如果你是一个经常使用 Excel 的用户,那么你每天都要做一个数据透视表或 10 个数据透视表。这是一种快速便捷的数据切片和识别关键趋势的方法,至今仍是 Excel 的主要卖点之一(也是美国公司初级分析师的克星)。
当不熟悉 Python 的人问我 Pandas 是什么时,我回答说,在高层次上,它就像 SQL 和 Excel 的结合体(功能更多)。因此,考虑到它包含了 Excel 的许多最佳部分,Pandas 对 Excel 著名的数据透视表有自己的实现是很自然的。让我们看看它是如何工作的,以及它如何帮助我们快速分析事物。
获取一些数据
我们将使用泰坦尼克号数据集(从 Kaggle 下载)。我已经将它的训练部分下载到一个 csv 文件中,并将其加载到一个数据帧中:
import numpy as np
import pandas as pdtitanic = pd.read_csv('train.csv')
在开始构建数据透视表之前,让我们确保没有空值:
**In:**print(titanic.isna().sum())**Out:**PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
fare_bin 15
我们有几个空值,让我们把它们去掉。通常,如果我们试图构建一个健壮的模型,我们会更加努力地考虑如何处理空值。但是今天,我们只是在这里演示如何建立和使用熊猫的数据透视表,所以我们将更加漫不经心地放弃数据。我们将完全删除 Cabin 列,以及包含非 Cabin 空值的任何其他行。第一行放下舱柱。第二行查找包含空值的任何其他行并删除它们。
titanic.drop(axis=1, labels=['Cabin'], inplace=True)
titanic.dropna(axis=0, how='any', inplace=True)
结果是我们的数据帧从 891 行增加到 712 行。那还不算太糟。票价和年龄列都有很多不同的值,所以我们应该将它们绑定。我们可以利用 Pandas 的 cut 函数(和分位数方法)来做到这一点。下面的代码段根据它们的五分位数分成 5 个段(五分位数 1 是最低的,五分位数 5 是最高的):
fare_bin_edges = [0,
titanic['Fare'].quantile(0.2),
titanic['Fare'].quantile(0.4),
titanic['Fare'].quantile(0.5),
titanic['Fare'].quantile(0.8),
max(titanic['Fare'])+1]titanic['fare_bin'] = pd.cut(titanic['Fare'],
fare_bin_edges,
labels=[1,2,3,4,5])
并且这一组代码箱老化(我手动分配箱边缘)。注意,仓是范围,但是我只使用较大的仓边缘来命名每个仓。例如,第一个 bin 包括年龄在 0 到 10 岁之间的所有人,但我只称之为 10。
age_bin_edges = [0,10,20,30,40,50,60,70,80,1000]titanic['age_bin'] = pd.cut(titanic['Age'],
age_bin_edges,
labels=[10,20,30,40,50,60,
70,80,999])
酷,是时候做些数据透视表了。
泰坦尼克号的旋转工作台
为了弄清我们的方位,让我们先按年龄组和性别算出乘客的数量。我们可以使用 pivot_table 函数来计算:
**In:**pivot1 = pd.pivot_table(titanic, values=['PassengerId'],
index='age_bin', columns=['Sex'],
aggfunc='count'
)
print(pivot1)**Out:** PassengerId
Sex female male
age_bin
10 31.0 33.0
20 46.0 69.0
30 81.0 149.0
40 54.0 100.0
50 31.0 55.0
60 14.0 28.0
70 2.0 14.0
80 NaN 5.0
对于那些不熟悉数据透视表的人来说,它基本上是一个表格,其中每个单元格都是经过筛选的计数(另一种方式是将其视为二维或多维分组)。例如,值 31 对应于 age_bin=10 和 gender = female 换句话说,有 31 名 0 到 10 岁的女性乘客。因此,它有助于我们快速观察几个感兴趣特征的数据的横截面组成。例如,从之前的数据透视表(pivot1)中,我们可以看到泰坦尼克号训练数据中的大多数乘客年龄在 20 到 40 岁之间,并且在我们所有的年龄区间中,男性乘客都比女性乘客多。在我们制作更多表格之前,让我们快速检查一下 pivot_table 函数的参数:
- 第一个参数是包含我们数据的数据帧。
- 第二个,值,是我们想要应用聚合函数的值。例如,在这种情况下,我们希望计算乘客的数量(通过 PassengerId)。
- 第三个参数 index ,是我们在行轴上分组的特性。所以我们的表的行标签将是这个特性的唯一值。在 pivot1 中,是 age_bin。
- 第四个是列,是我们在列轴上分组的特性。在 pivot1 中,是性别。
- 最后, aggfunc 是我们应用于值的函数。这里我们应用计数,但是我们也可以应用总和,表示,最小,最大或者其他。
安妮·斯普拉特在 Unsplash 上拍摄的照片
谁在泰坦尼克号上幸存了?
现在让我们用一张数据透视表来回答这个问题:谁最有可能在泰坦尼克号上幸存?从电影(我知道我真的做了我的历史研究对吗?),我回忆起在珍贵而稀缺的救生艇上,女人和孩子是预留的空间。根据大多数游轮的布局,高级舱在船上更高的位置——当船迅速装满水时,这是一个有利的位置。因此,让我们制作一个数据透视表,在行轴上按 age_bin 分组,在列轴上按性别和乘客级别分组。由于我们的数据的幸存特征是 0 和 1(0 表示这个人没有幸存,1 表示他们幸存了),取其平均值是计算幸存人数百分比的一种简便方法。那就是:
percentage that survived
= titanic['Survived'].mean()
= titanic['Survived'].sum()/titanic['Survived'].count()
现在让我们对数据透视表进行编码(我们将输出四舍五入到小数点后两位,以便于解释):
pivot2 = pd.pivot_table(titanic, values=['Survived'],
index='age_bin',
columns=['Sex','Pclass'],
aggfunc='mean',
)
pivot2.round(2)
这是数据透视 2。每个单元格是子组中幸存者的百分比(由 age_bin、sex/gender 和 Pclass 指定)。例如,以最右上的单元格为例——它表明年龄在 0 到 10 岁之间且属于 Pclass 3 的男性乘客中有 36%幸存。
数据透视 2
很明显发生了什么:
- 如果你是一名女性乘客,买得起高级机票(1 级或 2 级),你很有可能在灾难中幸存下来。
- 对于 Pclass 3 的女乘客来说,这变成了抛硬币,不管你的年龄出奇的大。
- 除非你是个孩子(10 岁或 10 岁以下),否则男乘客一般都不走运。尽管 Pclass 1 男性乘客仍然比他们不幸的同胞更有可能幸存。
- 同样令人惊讶的是,在三个等级中,男孩的存活率存在巨大差异。这也可能与以下事实有关:Pclass 3 有 22 名男生,而其他两个班总共只有 11 名男生)
如果我们通过删除年龄(并将 Pclass 移到行轴)来简化数据透视表,性别和可悲的阶级的影响会变得更加明显:
pivot3 = pd.pivot_table(titanic, values=['Survived'],
index='Pclass',
columns='Sex',
aggfunc='mean',
)
pivot3.round(2)
数据透视 3
差别是明显的:第一组和第二组中的雌性可能存活,而第二组和第三组中的雄性可能死亡。
感谢我们的数据透视表,我们现在有了一个简单模型的基础(也许是逻辑回归或决策树?).事实是,我们并不总是需要复杂的机器学习模型来弄清楚发生了什么(有时使用太复杂的模型实际上遮蔽了我们看到真相的能力)。很多时候,合理的逻辑和一些简单的交叉列表(以及一些热图或散点图)将帮助我们实现大部分目标。所以下次你分析你的数据时,先抛出几个数据透视表。你的发现可能会让你大吃一惊。干杯,大家注意安全!
转向高效的数据汇总
对数据实施数据透视表的快速指南
所有职业和行业中最强大的工具之一是数据透视表。在大多数传统分析中,Microsoft Excel 是必备技能,数据透视表是数据探索的核心。它们是动态的、相对直接的,并在数据的表层和深层提供重要的摘要。
资料来源:giphy.com
我本人来自基于 excel 的分析背景,当我在 Python 中处理数据时,从数据透视表的角度思考总是处于我思考过程的最前沿。在 Pandas 数据框架中使用数据透视表实际上相对简单,在汇总数据时可以节省大量时间和精力。让我们仔细看看如何在数据中直接实现数据透视表。
我们将使用一个显示多个大洲历史视频游戏销售的数据集。让我们首先导入必要的数据库,看看我们有哪些数据:
导入包&数据
让我们看一下数据帧的快照。我们有视频游戏名称的等级、平台、记录年份、游戏类型、游戏发行商,以及来自北美、欧洲、日本、全球和其他地区的累计销售额。所有“销售”的销售数字都以百万计。查看我们的初始数据框架,我们看到 Wii Sports 是排名最高的游戏,全球销量相当于 8274 万。
这个来自 Kaggle 的数据集已经被清理过了,已经满了,所以我们不用担心任何数据清理,可以直接跳到数据透视表本身。
使用索引的基本数据透视表
创建数据透视表的语法非常简单。
我们指定数据、索引和想要聚合的列。生成的表格如下所示:
提醒:这些数值以百万为单位
这只是前十年销售的快照,但是我们可以看到给定列的数据汇总是多么简单和快速。这个数据框架现在很容易理解,并且可以被视为一段时间内的销售趋势。
我们很容易看到,随着时间的推移,日本是世界上最重要的视频游戏购买者,直到 1995 年左右,所有其他行业开始在销售上趋同。20 世纪 80 年代末,在视频游戏引入的第一波浪潮过去并成为一种社会规范之后,整体销售额显著下降。
让我们在表格中做一个小小的调整,将索引从“Year”改为“Genre”。
通过在代码中做这个简单的调整,我们可以得到一个完全不同的描述性统计数据。现在,我们可以通过流派和地理位置看到各种视频游戏的成功。我们看到平台和射击游戏是全球销量最高的视频游戏类型。在日本,角色扮演游戏卖得最多。
多索引数据透视表
如果我们想要跨多个索引进行聚合,pivot 函数只需要简单地增加' index '参数。我们唯一需要确定的是,我们以列表格式传递索引。现在让我们使用数据透视表来看看出版商的销售情况,然后是流派。
我们可以看到这是多么容易做到的,因为我们首先看到一个出版商公司的索引,然后是流派,接着是欧洲、日本和北美的各自销售情况。请注意,index 参数将['Publisher ',' Genre']作为其参数。我们指定“值”参数只包括欧洲、日本和北美的销售额。自然,最终的数量会低得多,因为有大量的出版商,所以销售会少得多。
具有不同聚合的数据透视表
默认情况下,数据透视表将使用 sum 函数聚合值。然而,我们并不总是希望这样。也许我们想检查平均值、中间值或标准差。这些都可以通过对数据透视表语法中的“aggfunc”参数进行简单修改来实现。
我们以字典的形式输入' aggfunc '参数,将聚合列指定为键,将我们想要的聚合函数指定为值(使用 numpy 函数)。在我们的例子中,我们对欧洲销售额使用平均值,对全球销售额使用总和,对日本销售额使用中值。通常,我们不会混合使用这些集合函数,因为这会导致一个混乱的表格,但是对于这个实验来说,这样就足够了。
数据透视表内的关系
最后,让我们看看在 x 轴和 y 轴上使用可变索引。我们想尝试看看每年每种类型的全球销售额。我们可以像前面的表格一样创建一个多层次的索引,但是因为有这么多的年份和这么多的流派,这将会形成一个非常长且无法消化的表格。相反,我们将列指定为特性索引,然后声明我们正在聚合的值。
该表现在在 y 轴上显示年份(索引参数),在 x 轴上显示流派(列)。聚合函数对全球销售额求和。我们现在可以很容易地看到,例如,2011 年射击游戏在全球销售了 9936 万美元。
注意:可以将“fill_value”参数调整为等于 0、NaN 或任何其他聚合函数,如列的中值或平均值。标准 NaN 填充流程适用。
希望这将帮助您以更有效和高效的方式总结和探索您的数据,甚至帮助您从 Excel 分析转向 Python(参见我在那里所做的)!
PlaidML:基于预算的深度学习
PlaidML 允许人们像使用 Nvidia 显卡一样使用他们的 Intel 和 AMD 硬件
在深度学习和人工神经网络(ANN)的世界中,硬件一直是讨论的主要话题。由于 TensorFlow 等软件包,GPU 已经能够加速训练,并提供更精确的准确度分数;然而,它们通常都有很高的价格。在这个博客中,我希望勾勒出图形卡的世界,并为那些试图在预算有限的情况下学习人工神经网络的人提供替代方案。
在开始安装和使用 PlaidML 之前,我想先讨论一下显卡的重要性,以及为什么它会被广泛应用于深度学习。在每个专用显卡中,都有一块板(类似于主板)、专用散热风扇和一个处理单元(类似于 CPU)。这对深度学习如此有帮助的原因是,它为研究人员提供了同时运行多个进程的能力,同时释放了 CPU 工作负载——这反过来又允许更快的计算和更有效的 CPU 周期。今天,一个经常跟随深度学习的流行名字是 Nvidia,一家专注于 GPU 硬件的公司。英伟达在深度学习领域如此受欢迎的一个原因是,该公司提供允许这种类型培训的软件。例如,GPU 的传统用途往往是图形处理,如游戏、图形设计和视频编辑。当涉及到神经网络时,问题是大多数图形卡都是为了给它们如何处理视频的指令而创建的。Nvidia 已经成为这里的一个大玩家,因为他们已经创建了他们的硬件,以便它可以被给予各种不同的指令,而不仅仅是如何处理视频或游戏。虽然英伟达是提供深度学习软件的领导者,但出于同样的原因,他们也有比竞争对手更昂贵的产品。
RTX & GTX 显卡(图片来源:娜娜杜瓦
我可能只是一个典型的粉丝,但我是 AMD 的所有方式。如果你从来没有听说过 AMD,他们是一家不仅专门研究 CPU,而且也研究 GPU 的公司。在过去的几年里,他们已经能够扩展他们的产品,超过像英特尔这样的大公司,在某些情况下也超过 Nividia。这里唯一的问题是 AMD 没有专注于深度学习,而是更关注游戏和图形设计等方面的计算机性能。
我如此热爱 AMD 的原因是我内心是一个游戏玩家。在我开始学习深度学习和神经网络之前,我真的很喜欢计算机硬件,以便提高我在堡垒之夜和英雄联盟等游戏中的表现。像许多其他游戏玩家一样,我遇到的问题是,由于内置的硬件,游戏电脑的价格通常比传统电脑和游戏控制台高得多。话虽如此,但考虑到现代英特尔 CPU 的价格可能在 200 美元到 1000 美元之间,而 Nividia GPUs 的价格在 500 美元到 1200 美元之间,我有点气馁。记住,这些只是你组装一台电脑所需的 6 到 7 个零件中的 2 个。然而,随着我做了越来越多的研究,我开始更多地了解 AMD 如何利用其竞争对手的价格优势。AMD 提供相同的硬件,如果性能不比其他领先品牌更好的话,但价格是其他品牌的一小部分。经过几个月的研究,我最终购买了锐龙 7 2700x 8 核 CPU(200 美元)和 RX 580 8gb GPU(220 美元)。为了与他们的竞争对手进行比较,英特尔提供了一款定价 350 美元以上的 8 核 CPU,而英伟达提供的 GPU 性能几乎相同,价格为 400 美元以上。因为 AMD,我能够建造我自己的计算机,它是强大的和,比我使用英特尔或英伟达的成本低得多。
然而,一旦我开始进入深度学习和数据科学领域,我非常失望地看到 TensorFlow 和其他流行的软件包只能与 CUDA 核心(Nvidia 的技术)一起工作。这对我来说毫无意义,因为英伟达和 AMD 之间的性能如此之小,以至于在玩计算密集型视频游戏时,你几乎感觉不到差异,所以为什么不能在 AMD GPUs 上进行深度学习呢?这里的答案不是因为他们没有能力或不如他们的竞争对手,而是因为 AMD 没有真正像 Nvidia 那样花费金钱或人力来开发这些类型的软件。
这让我想到了 PlaidML,这是我用过的最好的开源库。PlaidML 允许人们像使用 Nvidia 显卡一样使用他们的 Intel 和 AMD 硬件。像 TensorFlow 一样,PlaidML 作为 Keras 的后端,允许计算在你的显卡而不是 CPU 上进行。在本教程中,我将向您展示如何设置 PlaidML,以及与仅使用 CPU 相比,它如何能够将训练速度提高近 4 倍甚至更多。
PlaidML Logo ( Github )
在输入下面的代码之前,我建议创建一个虚拟环境,这样您就可以隔离这些更改,以防您想要恢复到旧版本。
在您的终端中,使用以下命令安装 PlaidML:
pip install -U plaidml-keras
安装 PlaidML 后,您必须通过在终端中键入以下命令来配置计算机中的硬件:
plaidml-设置
当出现此消息提示时,键入 y(是)并按 enter 键。然后会出现另一个屏幕提示您选择不同的选项。它应该是这样的:
我当前机器上的显卡是英特尔高清显卡 4000。这可能与您在终端中看到的有所不同,但无论如何,请选择 OpenCL 选项,然后按 enter 键。
就是这样!!
安装 PlaidML 后,运行以下代码:
运行上面的单元后,您应该看到以下内容:
使用 PlaidML 作为 Keras 后端
如你所见,我们没有将 TensorFlow 显示为后端,而是成功地将其更改为 PlaidML。现在,每当你运行一个带有神经网络的单元时,它将利用你在设置时指定的图形卡,而不是你的 CPU。
我想说明的是,这个教程是在 Mac OS 上完成的。在我运行 Windows 10 的游戏电脑上,要完成这个任务要稍微复杂一点。如果您使用的是 Windows 机器,请遵循与 Mac 相同的步骤。如果您运行上面的单元格,它仍然显示 TensorFlow backend,那么您必须找到 keras.json 文件,并将后端从“TensorFlow”更改为“plaidml.keras.backend”。
虽然这个软件包将帮助训练速度提高 4 倍或更多,但根据您使用的显卡,您仍可能会遇到内存问题。我还想指出的是,很多笔记本电脑和台式机都不是为处理这种计算而设计的,所以我建议安装第三方软件来监控你电脑内的温度。如果你训练超过 30 分钟,看到你的 CPU 或 GPU 超过 90-100 度,我会停止训练,并找到一个替代方法,因为你不想损坏你的电脑(特别是 MAC 电脑……业界最差的散热)。
如何将 Minimax 应用到 2048 年
用极大极小算法玩 2048】
2048 年——一个简单的游戏,但是给计算机编程来解决它就不简单了
作者图片
最小最大值是一种被指定用于玩对抗性游戏的算法,即有对手参与的游戏。在本文中,我们将看到如何应用极大极小算法来解决 2048 游戏。这是 3 部分系列的第一篇文章。这一部分将包括在概念层面上规划我们的游戏程序,在接下来的两篇文章中,我们将看到实际的 Python 实现。
这里我假设你已经知道了极大极小算法的一般工作原理,并且只关注如何将它应用到 2048 年的游戏中。所以,如果你还不知道极小极大算法,看看:
用人工智能玩游戏
towardsdatascience.com](/understanding-the-minimax-algorithm-726582e4f2c6)
在 2048 年应用极小极大时,我们需要考虑的主要 4 件事,实际上不仅仅是 2048 年,而是任何其他游戏,如下:
1。我们如何把 2048 年想成一个双人游戏?麦克斯是谁?敏是谁?
2。我们如何决定一个游戏状态何时是终结?
3。我们如何评价一个游戏状态的分数/效用?
4。我们如何确定一个游戏状态的子节点?
上面的第一点是因为这就是 minimax 的工作方式,它需要两个玩家:max 和 Min。其他 3 件事来自算法的伪代码,如下所示:
当我们编写算法的一般形式时,我们只关注突出显示的函数/方法的结果(“它应该确定状态是否是终结的”、“它应该返回分数”、“它应该返回该状态的子代”),而没有考虑它们实际上是如何完成的;那是游戏特有的。
现在,当我们想将这个算法应用到 2048 年时,我们将注意力转移到“如何”部分:我们实际上如何为我们的游戏做这些事情?****
我们如何把 2048 年想成一个双人游戏?麦克斯是谁?敏是谁?
回想一下极大极小算法,我们需要 2 个玩家,一个最大化分数,一个最小化分数;我们称它们为最大值和最小值。
当我们在 2048 年比赛时,我们想要一个大比分。麦克斯是谁?我们。我们想最大化我们的分数。
谁想最小化我们的分数?嗯…没有人。有游戏本身,电脑,随机产生大部分是 2 和 4 的棋子。但是,它并不是真正的对手,因为我们实际上需要这些棋子来增加我们的分数。我不认为这个游戏把这些棋子放在我们不利的地方,它只是随机地放置它们。
但是极大极小算法需要一个对手。因此,我们将认为 Min 是放置这些瓷砖的游戏本身,虽然在游戏中瓷砖是随机放置的,但我们将认为我们的 Min 玩家试图以最糟糕的方式放置瓷砖。
我们如何决定一个游戏状态何时是终结?
这是一个简单的问题:当游戏结束,或者我们达到了一定的深度时,游戏状态被认为是一个终结状态。当游戏棋盘上满是瓷砖,我们无法移动时,我们将认为游戏已经结束。
游戏树上的深度阈值是为了限制每次移动所需的计算。如果我们让算法遍历整个博弈树,这将花费太多的时间。我们希望限制这个深度,这样算法将为我们需要做出的每一步给出相对快速的答案。对于 2048 游戏,5-6 的深度很好。
我们如何评价一个游戏状态的分数/效用?
2048 游戏的目标是将瓷砖合并成更大的瓷砖,直到你得到 2048,甚至超过这个数字。在上面的文章图像中,您可以看到我们的算法如何获得 4096 的图块。但是我们应该在极小极大中使用的确切度量是有争议的。人们可以认为一个好的效用函数应该是最大瓦片值,因为这是主要目标。但是如果我们在同样的最大值下有更多的游戏配置呢?我们如何区分它们?我认为我们应该考虑是否还有其他大的部分,这样我们可以稍后再合并它们。
那么,我们是否应该将所有瓷砖价值的总和视为我们的效用?但是这个总数也可以通过在棋盘上填满小方块来增加,直到我们没有更多的移动。我认为我们应该惩罚这个占据了太多棋盘空间的游戏。所以,用这个总数除以非空瓷砖的数量,对我来说是个好主意。我们希望在尽可能小的空间里展示尽可能多的价值。
我们如何确定一个游戏状态的子节点?
在极小极大博弈树中,一个博弈状态 S 的子博弈状态是所有其他的博弈状态,这些博弈状态只需一步就可以到达。我们如何确定 S 的子代取决于从 S 移动到它的一个子代的玩家是什么类型的。
如果玩家是 Max(也就是我们——试图赢得游戏),那么它可以按下其中一个箭头键:上、下、右、左。根据游戏状态的不同,并非所有这些移动都是可能的。所以麦克斯的可能行动也可能是这四个行动的子集。
作者图片
而在这种情况下,S 的子系就是 Max 在做其中一个动作时可以达到的游戏状态。
有哪些招式可以做到 Min?闵的工作是把瓷砖放在棋盘的空白方格上。这些牌大多数是 2 和 4,但它也可以使用我们在棋盘上拥有的牌。然而,我们将只考虑 2 和 4 作为可能的瓦片;这是为了避免不必要的大分支因子,并节省计算资源。如前所述,我们认为 Min 试图做对我们最不利的一步棋,那就是放一张小牌(2 / 4)。
所以,如果参与人是 Min,可能的移动是所有空方块的集合和集合{2,4}的叉积。而 S 的子就是这其中一步棋所能达到的所有博弈状态。
下面是一个简单的例子:
作者图片
在上图中,两个没有阴影的方块是游戏棋盘上仅有的空白方块。Min 能做的移动是在每一个上面放一个 2 或者放一个 4,这样总共有 4 个可能的移动。
暂时就这样了。在下一篇文章中,我们将看到如何通过Grid
类在 Python 中表示游戏棋盘。这个类将包含我们完成任务所需的所有游戏逻辑。
…以及如何以面向对象的方式做到这一点
towardsdatascience.com](/how-to-represent-the-game-state-of-2048-a1518c9775eb)****
我希望这些信息对您有用,感谢您的阅读!
这篇文章也贴在我自己的网站这里。随便看看吧!
强化学习扑克牌
概念和代码🐍
♣️️蒙特卡洛赌场夜♠️ 来源
今天,我们要去打牌!嗯,不完全是这样…我们打算造一个像职业玩家一样玩纸牌游戏的机器人😎🔥
更确切地说是 Easy21 游戏,是 21 点的一个变种。我从大卫·西尔弗在 UCL 的强化学习任务中挑选了这个项目。我认为这是一种有趣的方式,通过对每个人都有意义的真实具体的应用来抓住一些基本的 RL 概念:
尝试在任何情况下击败庄家💪
这篇文章是一篇个人笔记,我用它来提醒我并说明一些核心 RL 概念。所以,我会建议一个完全的 RL 初学者花些时间熟悉 RL 的词汇,因为我不会在基础上花太多时间。
让我们首先描述一下 Easy21 规则:
- 牌的值在 1 到 10 之间(游戏中没有人头)
- 玩家和庄家都从一张随机的牌开始
- 有 2 种可能的行动,玩家/庄家可以选择击(挑选一张新牌)或棒(停止挑选牌)
- 黑卡的值相加,而红卡的值从分数中减去(而不是与 21 点相比一直相加)
- 当庄家出牌时,他遵循一个固定的策略,如果他的分数在 17 和 21 之间,他就用打,否则就用打
- 如果牌的总数小于 1 或大于 21,玩家/庄家输
简而言之,Easy21 比 21 点更“复杂”,因为你可以减去牌的价值。你可能想检查图 1。下面看一个完整的游戏序列示例(在 RL 中称为集)或者看一下任务的完整描述。
图一。RL 中的一个游戏序列或集…希望在不久的将来我们训练有素的代理人🤖会挑更好的动作让我们赢!
现在我们更好地理解了 Easy21 中发生的事情,让我们简单地介绍一下 RL 的一些基本概念,为我们的问题提供一个更正式的背景。RL 是关于在给定的环境中为代理找到最优 策略。简单地说:RL 是在给定 Easy21 规则的情况下,为玩家找到最佳策略。
在 RL 中,代理人通过奖励分配进行学习。对于 Easy21 中的每个终端状态,获胜状态(玩家得分>庄家得分)、松散状态(玩家得分<庄家得分)或一个平局状态(玩家得分=庄家得分)关联一个奖励,分别为 +1 、 -1 和 0 。
由于奖励,代理学习了所谓的 Q 值函数。简而言之,Q 值函数是给定状态-行为对的期望报酬。简单地说,Q 值是在特定情况下对潜在回报的估计。这个 Q 值函数是 RL 跳动的心脏💘
现在我们可以把 Q 值函数想象成一个大的查找表,在这里我们可以找到给定一个状态和一个动作的相关预期报酬。
一旦 Q 值函数被计算出来,我们的代理人将会知道在给定的游戏状态下,哪一个动作,击或棒,具有最高的期望回报。记得在图 1 中,在状态:{玩家得分= 13,庄家得分= 9}中,玩家被粘住并输了。也许这不是最佳的行动,也许是…一个人怎么能忍受这样的不确定性...保持信念,RL 会告诉我们🔮
这篇文章涵盖了 3 种评估最佳策略的常用方法:蒙特卡罗控制、SARSA(λ) 和数值逼近。
闲聊够了,让我们做点真正的研究吧🚀
如果你对代码有更好的理解,你可以直接去注释本👇
实现各种 RL 算法学习玩 Easy21 - Matyyas/Easy21
github.com](https://github.com/Matyyas/Easy21)
一.环境
首先,我们必须定义我们的环境,在这里我们的代理将学习在给定的状态下选择哪一个动作(击打或坚持)来击败庄家。
环境简单编码 Easy21 的规则。参考笔记本查看实施细节。
太好了!但是现在我们有了一个代理环境,我们如何学习在这个环境中遵循最优策略呢?
二。蒙特卡罗控制方法
在本节中,我们将用简单的英语解释蒙特卡罗控制(MCC)方法背后发生的事情。但是,您可以去查看笔记本以深入了解实现的细节。
让我们首先揭开这些术语的神秘面纱:
- Monte-Carlo 是一个花哨的名字,用来表示我们将对剧集进行采样(在我们的例子中是游戏序列)。
- 控制意味着我们将找到最佳策略,即在任何状态下选择最佳行动,以最大化我们的获胜机会。
使用 MCC 方法,我们首先开始取样一集↔️使用“当前”策略玩 Easy21 的游戏,并观察终端状态的回报。
然后,非常简单,我们更新在采样剧集中遇到的每个状态-动作对的 Q 值函数(预期回报),该函数与在剧集结束时观察到的回报有关。
上面的更新步骤是从 MCC 代理的脑袋里面看到的:“如果玩的游戏是赢的(resp。损失),我已经采取的状态-动作序列的值应该增加(相应地。减少了)”。
假设 Q 值函数已经更新,我们可以在它的基础上建立一个“新”策略,并且我们准备使用这个“新”策略来采样新的情节。
现在重复这个采样/更新过程,直到你达到固定的集数,或者直到新的策略与当前的策略不变……这里你有一个很好的最优 Q 值函数👨🍳
图二。显示了我们选择采样 1000、100 000 或 1 000 000 集时获得的不同最佳 Q 值函数。我们逻辑上看到,样本越多,最优 Q 值函数越平滑。
图二。最佳 Q 值对不同数量的采样集起作用。正如所料,采样的剧集越多,最佳 Q 值函数的方差越小。
根据上面计算的最优 Q 值函数(越红,预期奖励越高),代理现在可以识别在任何给定状态下选择哪个动作,以优化他以获胜结束状态结束的机会。那是我的朋友们的控制👊
记得图 1,在状态:{玩家得分= 13,庄家得分= 9},玩家被粘住,输了 …现在,我们训练有素的代理🤖知道他将有更高的机会赢得它,他选择了而不是,正如我们在图 3 中看到的。
图三。🏆最优策略又名 RL 的 Holly Graal,在抽样一百万集后计算得出🏆
有了上面的最优策略,我们的代理人知道在任何可能的状态下应该采取什么样的行动来增加击败庄家的机会💥🥊
综上所述,MCC 是通过采样剧集 ↔️玩 Easy21 的多个游戏来学习的,采样的剧集越多,学习的越好。在 Easy21 这样的简单游戏中可能没问题,但是在更深的环境中,采样一百万或更多集可能是不可行的……为了克服这个主要缺点,我们需要从不完整的集开始学习。
三。萨莎(λ)
尽管这种算法的名字听起来像某种晦涩的蛇佬腔公式⚡🐍,SARSA 简单来说就是 S tate,aaction➡️reward, S tate ' ,aaction'的首字母缩写,这与算法向最优更新 q 值函数的方式有关。 λ 是一个模型的参数,我们将很快进一步开发。和往常一样,用代码更好理解的可以直接去笔记本。
NB:SARSA 方法有两种观点,一种是 前进 和一种 后退 观点,这两种观点都与 相当 。在这篇文章中,我们将只关注 向后 视图,因为它是我们使用的实现。
🔎SARSA 算法的核心有两个主要概念,我们将在深入研究该方法之前简要回顾一下(如果有兴趣,我将留给读者来深化这些主题)。
- 时间差 (TD) 学习 : TD 学习是指我们要查看未来一步的 Q 值,以将当前 Q 值更新为这个未来 Q 值估计值。更简单地说,我们调整当前的 Q 值以更好地匹配未来的 Q 值估计。
因此,Q 值在运行中被更新,SARSA 开始从不完整的剧集中学习,而不是像在 MCC 中那样在采样剧集结束时学习(图 4 示出了该学习过程)。
图 4:蒙特卡罗和 TD 学习的比较更新步骤** ( 来源)**
NB :使用 现有估计 进行 Q 值更新,这就是为什么 SARSA 在文献中也被称为 自举 方法。
- 资格痕迹(👽ET):利用 ET,我们想要对每个状态-动作对的 Q 值更新进行加权,关于:多频繁 t 和多长时间前在情节的给定步骤看到状态-动作对。
λ参数又名轨迹因子指定轨迹消失的“速度”。λ参数在 0 和 1 之间,越接近 0 意味着衰落越快,越接近 1 意味着衰落越慢。这个概念用图 5 中的 Gridworld 示例来说明。
图五。一个简单的 Gridworld 环境示例来捕捉资格跟踪概念。为了适应我们的 Easy21 环境,需要做一点小小的心理调整
SARSA(λ)没有在 TD 学习或蒙特卡罗采样之间进行选择,而是使用跟踪因子λ** ,从 0 到 1 进行缩放,以结合两个概念中的最佳概念。如果λ=1,我们回到经典的 MCC 方法(之前在第 II 节中看到),如果λ=0,我们进行简单的(一步)TD 学习更新。我们的工作是寻找和挑选最佳λ,使代理在不破坏其学习的情况下最快地学习最优策略。**
与蒙特卡罗相比,用** SARSA 学习的代理不需要采样那么多片段来收敛到最优策略,这得益于它从不完整片段中学习的能力。当剧集非常长,并且对其中许多剧集的采样开始变得计算过于密集时,这非常有用。如果你不相信我,请看图 6!👇**
图六。 MSE 曲线在 最佳 Q 值 和计算出的 Q 值之间,取决于采样集数和不同的λ值。正如预期的那样,与经典 MCC 方法(λ=1)相比,TD 学习方法(λ=0)需要更少的事件来收敛到最优 Q 值函数。
我们刚刚部署的所有 RL 武器库还可能出什么问题?🤔其实还有最后一件事我们还没有考虑…
在 Easy21 中,有 10212 = 420 个不同的状态-动作对。然而,在现实生活中,环境往往有成千上万种可能的状态和动作,甚至是连续的状态和动作域。在这样的环境中,不可能再存储或显式计算每个状态-动作组合,因此需要一个Q值函数近似值来将我们从这种维数灾难中解放出来。
四。价值近似值
最后,一个有意义的标题🙌!值近似** (VA)令人惊讶地意味着它所暗示的:我们将近似 Q 值函数,而不是为所有可能的状态-动作对计算它。**
为了近似我们的 Q 值,我们将需要减少可能性的范围。为了做到这一点,我们可以想象一个对游戏有深刻了解的 Easy21 专家玩家,指导我们构建下面的特性(图 7)。这位专家知道什么样的庄家得分区间和玩家得分区间是有意义的,以便像职业玩家一样玩游戏。在 ML 中,特征工程步骤总是与领域专家的知识质量直接相关,并且可能与环境复杂性有关。
图 7。pro Easy21 播放器的不同工程特征。
记住上面所有的方法,我们可以把 Q 值函数看作每个状态-动作对的查找表。
现在,我们正在使用 3∫6∫2 = 36 个可能的工程特征组合来对 Q 进行建模,这比我们之前的 420 个可能的状态-动作对要少得多。
图 8。没有更多的查找表,Q 值是一个线性函数。
Q 值函数不再是一个查找表,而是一个依赖于某些权重的函数。现在,我们需要学习最佳权重,以使我们的近似函数尽可能接近最佳 Q 值函数。
因此,前面章节中的“经典”更新步骤被线性函数的权重θ 的更新所取代。很简单,对吧?****
图九。我们都知道…ML 没有免费的午餐。当比较 MCC 和 VA 的 100 000 集后的 Q 值时,我们可以看到,时间和空间复杂度的降低伴随着精确度的降低。
NB :在我们的例子中,我们实现了线性值近似。然而,可以考虑使用非线性方法,并进入使用神经网络逼近 Q 值函数的 深度强化学习 的惊人领域。使用神经网络导致更高的表示能力,并使特征工程无用。下集见……
好了👌!我们的强化学习之旅现在结束了,你是到达这篇文章终点的勇敢者之一,你的灵魂无疑会去瓦尔哈拉🙌
资源: -【1】帖子的笔记本(所有代码/剧情都在那里):
实现各种 RL 算法学习玩 Easy21 - Matyyas/Easy21
github.com](https://github.com/Matyyas/Easy21)
通过身体上的跳跃和蹲下来玩 chrome 的恐龙游戏
一个有创意的 PoseNet 应用程序,它运行在你的浏览器上,并试图预测你是在跳跃、蹲伏还是静止不动
截图来自 chromedino.com
你们都知道这个游戏的内容。这是世界上最好的离线道歉服务页面。人们制作了简单的机器人,在恐龙跳起来打败游戏的时候,用 CNN 状态编码器强化学习代理。
这是个游戏,我们应该玩得开心。今天,我将带你了解如何编写一些 JavaScript 代码,通过在房间里跳来跳去来玩这个游戏。
这个东西很难玩。
克服技术壁垒
对于经验丰富的开发人员来说,建立一个具有基本 javascript 支持的小网页来获得网络摄像头提要和 dino 游戏容器是微不足道的。你所需要的只是最新的 chrome,一个<video>
标签,一些从 stack overflow和抓取的 t-rex 游戏加载网络摄像头的 JavaScript 片段。
接下来是有趣的部分。运动/动作检测。
Tensorflow Lite 开源了许多针对网络或移动应用的微调模型。我决定使用完整的(下半身和上半身)姿势探测器。通过在您的<head>
中添加这两行代码,您可以导入 tensorflow lite for javascript 以及我在这里使用的模型。
下载并准备好每个依赖项后,我们可以开始处理相机元素的loadeddata
事件。
一旦网络摄像头准备就绪,加载模型并开始姿势预测循环。
我还通过在 video feed 容器上添加一个点击事件,加入了一个小的录制播放/暂停功能。
检测动作
Posenet 输出每个预测骨骼位置的列表,以及一个得分值。
https://www . tensor flow . org/lite/models/pose _ estimation/overview
为了简单起见,我们的算法非常简单。
- 挑出左右髋骨
- 选择最有把握(得分)的髋骨
- 如果分数不是
> 0.6
(比随机猜测好),则转到下一帧 - 否则,在
y
轴上执行简单的阈值处理来检测动作
这是目前为止我们算法的第一部分:
继续进行阈值处理。
亲亲。
- 如果臀部位于摄像头进给的下部(底部
20%
),则将其视为下蹲。 - 如果臀部在进给的中间(在摄像机进给高度的
20%
和70%
之间),则视为空转。 - 否则,将此视为跳转
当然,这是假设玩家站在摄像机前的正确位置。这很容易做到。只要确保你离得很远,让你的臀部在视口的中间,有足够的空间让你可以蹲下和跳跃。
最后一部分,实现toggleAction
非常简单。我们只是要模拟按键事件。键码32
表示空格键,键码40
表示下箭头。
就是这样。感谢阅读!
用深度 Q 学习玩 Connect 4
通过一个著名的游戏环境探索强化学习的力量
信用:allthefreestock.com
深度 Q 学习可能是所有强化学习中最重要的算法之一,因为它对它可以在复杂环境中进行的观察和采取的行动没有限制。这种强化学习方法结合了深度神经网络,允许代理重复“玩”一个环境,并通过一个观察、行动和奖励系统随着时间的推移学习环境。与标准的深度神经网络实现相比,这种结构具有明显的优势,因为它允许代理与其周围环境进行交互,从周围环境接收反馈,然后针对期望的(高回报的)未来动作进行优化。在这篇文章中,我们将看到一个熟悉的环境,它是最近由 Kaggle 在其 Kaggle 竞赛(https://www.kaggle.com/c/connectx)中实例化的。
Kaggle 环境中的电路板状态示例
在我们开始探索深度 Q 学习代理的结构以玩 Connect 4 之前,让我们首先简要概述一个简单的、不太有用的 Q 学习代理的结构。Q-Learning 的基本思想是创建整个观察空间的地图,并在这个地图中记录代理的动作。随后,每当代理遇到相同的观察时,它将基于其先前的动作是否获得了正面或负面的奖励来递增地更新其先前采取的动作。这种数据结构被称为 Q 表,其中存储了观察空间中每个观察的先前动作。Q 表的这种增量更新通常通过以下 Q 学习等式来完成:
虽然这个等式乍一看似乎很复杂,但它只是简单地使用预期回报来更新与当前观察相对应的 Q 表的特定位置。通过一个代码示例可以更好地理解这一过程,尽管对我们的深度 Q 学习项目来说没有必要,因为我们将使用深度神经网络来更新我们的“Q 表”(稍后将详细介绍)。
为了更好地理解 Q-learning 的本质,让我们从我们非常著名的环境 Connect 4 的角度构建一个观察示例。以下是在游戏期间连接 4 板的上述示例状态,换句话说,观察空间的元素(玩家 1 筹码:1,玩家 2 筹码:2,空白空间:0)
之前显示的由神经网络看到的电路板观察
在这里,我们看到了先前播放的片段的特定排列,这些片段将作为观察结果发送给代理。代理将获取该观察结果,并在其 Q 表中找到该观察结果。本质上是问自己这样一个问题,“上次我看到这些碎片的排列时我做了什么,这次我如何做得更好?”。然后,它将继续以适当的方式行动,并在整个过程中为每次移动在 Q 表中的适当位置更新动作。从总体上看,这似乎是 Q-Learning 的一个很好的用例……而不是一个微妙的例外。你抓住它了吗?
标准 Q 学习的局限性
Q-Learning 结构对于某些环境非常有用,但是它在其中起作用的环境的数量非常有限。这要归功于之前说过的一句话:“Q-Learning 的基本思想是创建整个观察空间的地图……”。即使在我们简单的 Connect 4 环境中,想想这意味着什么。在 Connect 4 中,我们有 42 个条目,可以由玩家 1 筹码、玩家 2 筹码或无筹码来填充。我们也必须给予重力应有的重视;因为只有在格子下面的空间中有先前玩过的筹码时,才可以在格子的位置中玩筹码。此外,如果任何 4 个相同颜色的筹码排成一行,游戏结束,因此未来的情况不能包括在观察空间中。这相当于一个有点复杂的计算,就像我们已经说过的;尽管如此,您可以看到可能的观察值的数量开始迅速增加。根据网上的整数序列百科全书,结果是 4531985219092(【oeis.org/A212693】T2)。我不知道你怎么想,但对于我的 1TB 硬盘来说,4000 亿次的入门级 Q 表似乎有点太大了。因此,标准 Q-Learning 不是一个好的起点,让我们转向一个不同的解决方案。
深度 Q 学习
记住了 Q 学习的基础结构,深度 Q 学习就很容易理解了;唯一的区别是 Q 表的替换。在 Q 学习中,对于具有大观察空间的环境,Q 表的限制很快达到。如果我们知道某种算法,可以将观察结果与我们从奖励中计算的某种损失函数结合起来,并对观察到的环境进行某种概括以选择单个输出(或动作),那么我们就不必存储这么多环境观察结果了。任何对机器学习感兴趣的人都知道,、深度神经网络就是针对这个问题的工具。总之,为了简化我们在 Connect 4 游戏中观察空间的惊人大小,我们将使用一个标准的 Q 学习结构,用一个简单的深度神经网络替代原本是 Q 表参考的内容。
连接 4 Kaggle 环境
为了方便起见,我们将使用正在进行的 Kaggle 竞赛中的 Connect X 框架(稍加修改)来构建我们的代理。这将允许我们非常简单地从环境中获取观察结果并向环境发送行动,而不必自己构建 Connect 4 游戏。接下来的 4 行代码处理环境的构建,以及两个代理相互玩 Connect 4 的模拟,在这个例子中是两个随机代理(【https://pypi.org/project/kaggle-environments/】)。该环境为训练提供了“随机”代理和“negamax”代理;其中,随机代理人只是进行随机合法移动,而 negamax 代理人进行初级水平,但优于随机合法移动。考虑到这一点,我们意识到自我游戏的概念对于我们的模型与自身游戏的训练是至关重要的。
环境奖励修改
对 Kaggle 提供的环境所做的唯一微小的改变是手动计算游戏何时结束。这允许奖励决定不同种类的赢,输,和无效的移动。例如,当我们的代理人与随机代理人对抗时,垂直胜利很容易到来,因为随机代理人只会在 3/7 的时间里阻止垂直胜利。在这个特定的训练实例中,可能有用的是在纵向胜利上放置较少的奖励,例如,导致代理仅在第 3 列中玩,并且仍然使用明显不好的策略在每 7 场游戏中赢得 4 场。赢/输的确定由下面的函数演示。这是简单的蛮力迭代,分别通过所有可能的垂直、水平和对角线配置。
创建神经网络模型和优化器
这些步骤在某种程度上是不言自明的,假设读者之前已经接触过神经网络;但是,下面是对模型创建、损失函数和优化的简要概述。首先,该模型只是一个密集的神经网络,它是通过非常方便地使用 tensorflow.keras.layers 模块而构建的。我首先尝试使用卷积神经网络,因为我认为关系信息是有益的,但发现这样做效果不好(我认为是由于电路板的尺寸较小)。网络的输出有 7 个神经元,每个神经元对应于将一个芯片投入棋盘 7 个不同列中的一列的动作。
类似的结构,我们的隐藏层各有 50 个神经元。使用 alexlenail.me 网络工具创建
损失函数简单地取一集(或 Connect 4 的一个完整游戏)的回报之和,将它们应用于网络的每个输入和输出(分别为观察和动作),执行交叉熵,并给每个观察/动作对一个与收到的回报成反比的损失值。例如,如果代理赢得一场游戏,整个游戏中的所有观察/动作对将获得 20 的奖励,这是我们奖励结构中提供的最高奖励。这将反过来导致一个小的损失值,因此是相反的关系。这种反向关系使得我们的优化器朝着正回报优化网络,因为任何标准优化器的目标都是最小化网络的损失。理论上(实践中很少),最小化损失将增加回报,反过来增加网络做出决定的能力,从而在 Connect 4 游戏中获胜。
train_step()函数中的优化步骤对于深度学习经验丰富的人来说可能有点陌生。这是因为它使用了 GradientTape()模块,这是 tensor flow 2.0 版新增的模块。总的来说,该模块“观察”(或记录)调整后的网络梯度,并允许使用自定义损失函数轻松训练网络,正如我们在这里实现的那样,这是对旧版本的 model.fit()方法的重大改进。
选择具有ε衰变的行动——探索与开发
在所有的强化学习问题中,在探索和利用之间存在着不可避免的权衡。探索是指一个代理人做出一些与角色无关的决定(不是由网络决定的),试图找到新的策略,并可能从中获得回报;因此,这是一个学习更好策略的机会。利用就是利用网络已经学会的策略。例如,如果代理已经学习了一点如何玩 Connect 4,并且它总是利用它已经学习的技术,它将永远不会被呈现新的观察/动作对来学习。另一方面,如果代理人总是在探索新的策略,它本质上总是在玩随机移动,永远不会达到一个高水平的游戏来学习。处理这个问题的方式通常是通过一种被称为ε衰变的现象。epsilon 衰减中的 epsilon 可以被认为是代理选择随机动作而不是网络选择的动作的概率。epsilon decay 中的衰变就是这样:因为在训练周期的开始,我们期望代理是哑的,因为没有更好的术语,我们将让它做出很大程度上随机的决定,从而让代理探索更多而不是利用,因为它还没有学到任何可以利用的东西。随着时间的推移,ε会变得越来越小,也就是说:智能体会越来越多地开始利用它学到的知识;会越来越少探索随机行动。该代理将使用ε衰减函数ε= .99985^x,其中 x 是训练的剧集数。这将根据培训期的集数进行相应调整,在本例中,我们使用 40,000 集。
使用 Desmos.com 网络工具创建
创建代理
这部分很简单。代理只是一个函数,它接受观察,并发出动作。下面显示的是一个非常简单的代理,它通过神经网络发送观察结果并输出一个动作。虽然有一个小的补充,如果你愿意,一个错误修复:在' else '语句中,你会看到它只是选择网络预测的下一个最大概率动作,如果该动作被确定为无效。如果网络决定在已经满员的栏目中播放,则动作无效。你会注意到,经过大量的训练和适当的奖励结构,代理最终学会了只玩有效的行动;使这成为一种“以防万一”的功能。
终于,开始训练的时间到了
这里的工作流相对简单,因为所有的结构函数都已经构建好了。我们首先实例化我们的内存对象,该对象将用于将信息从环境传递到网络。然后,我们遍历每一集,在本例中为 40,000 集,按照前面描述的函数,将观察结果和奖励正确地传递给网络。如前所述,请注意我们定制的特定奖励。训练过程总共包括三次训练:第一次训练对抗随机代理,第二次训练对抗 negamax 代理,第三次训练对抗它自己(或者更具体地说,训练成扮演 2 号玩家角色的相同模型)。
结果呢
下面是我们经纪人(蓝色)对 negamax 经纪人(灰色)的一场比赛。总的来说,我对经纪人的表现印象深刻;很明显,代理已经想出了如何阻止获胜,如何赢得自己,以及如何只在尚未满员的栏中玩(这些无论如何都会被纠正,但它在训练的这一点上没有尝试无效的移动)。这个例子中我印象最深的招式是它的倒数第二招,32 招。这一步令人印象深刻,因为它不仅设置了一个胜利,而且实际上通过同时设置水平胜利和对角胜利来保证胜利。
结论
从该项目中获得的一些主要信息旨在解决:
- 我意识到深度 Q 学习可能不是解决这种环境的最佳方式。我认为,如果我真的与这个代理人在 Kaggle 比赛中竞争,深度学习辅助的蒙特卡罗树搜索算法是最好的方式。我选择这条路线的原因是,由于每个算法能够处理的环境的复杂性,我对基于树搜索的算法的深度 Q 学习更感兴趣。深度 Q 学习可以处理任何观察空间,只要观察本身对于网络来说不是太数据密集。我觉得这种算法更适合实验,因为它更有可能在现实环境中实现,而树搜索类型的算法是专门为受限环境设计的,比如棋盘游戏。
- 虽然代理人的行动确实令人印象深刻;我不认为它能与人类水平的智力竞争。这个算法的训练过程只花了一天多一点的时间,我不想继续分配 GPU 资源来让它继续训练,但我有兴趣看看这个算法的过度训练是否会允许更大的改进,或者它是否接近其技能水平的最大阈值。
用人工智能玩毁灭:深度 Q 学习的多目标优化
Pytorch 中强化学习的实现。
简介
O 在线学习方法是一个动态的算法家族,推动了过去十年强化学习的许多最新成就。在线学习方法属于基于样本的学习类强化学习方法,允许简单地通过重复观察来确定状态值,消除了对显式转换动态的需要。与它们的离线对应物、不同,诸如时间差异学习(TD)之类的在线学习方法,允许在代理-环境交互期间状态和动作值的增量更新,允许观察到持续的、增量的性能改进。
除了 TD,我们还讨论了 Q-learning 的理论和实际实现,Q-learning 是 TD 的一种发展,旨在允许对环境中的状态-动作值进行越来越精确的估计。Q-learning 因成为模拟游戏环境的强化学习方法的支柱而闻名,如在 OpenAI 的健身房中观察到的那些。因为我们已经在过去的文章中涉及了 Q-learning 的理论方面,所以这里不再重复。
一个代理扮演我们之前 Tensorflow 实现中的基本场景
在我们的上一篇文章中,我们探索了如何通过使用开源的 OpenAI gym 包装库 Vizdoomgym ,将 Q-learning 应用于训练代理人在经典 FPS 游戏 Doom 中扮演一个基本场景。我们将在那篇文章的基础上引入一个更复杂的 Vizdoomgym 场景,并用 Pytorch 构建我们的解决方案。这是研究各种末日 RL 算法系列文章的第一篇,作为我们的基线。
实现
我们将要探索的环境是 Vizdoomgym 的防线场景。环境中,代理在走廊的一端,恶魔在另一端繁殖。环境的一些特征包括:
- 一个 3 的动作空间:开火,左转,右转。不允许扫射。
- 向玩家发射火球的棕色怪物,命中率为 100%。
- 粉红色的怪物试图以曲折的方式靠近来咬玩家。
- 重生怪物的生命值明显更高。
- 杀死一个怪物+1 点
-
- 1 代表死亡。
防线场景的初始状态。
毫无疑问,在这个环境中的成功需要平衡多个目标:理想的玩家必须学会优先考虑褐色怪物,它们能够在产卵时伤害玩家,而粉红色怪物由于它们的旅行时间可以安全地忽略一段时间。这个设置与我们之前的末日文章形成了对比,在那篇文章中我们展示了单个目标。
我们的 Google 协作实现是使用 Pytorch 用 Python 编写的,可以在 GradientCrescent Github 上找到。我们的方法基于 Tabor 的优秀强化学习课程中详述的方法。由于这种方法的实现非常复杂,让我们总结一下所需动作的顺序:
- 我们定义了最大化性能所需的预处理函数,并将它们作为自动化健身房环境的包装器引入。这些主要是通过使用元素最大值和帧堆叠来捕捉环境的运动。
- 我们定义我们的深度 Q 学习神经网络。这是一个 CNN,它拍摄游戏中的屏幕图像,并输出 Ms-Pacman gamespace 中每个动作的概率,或 Q 值。为了获得概率张量,我们在最后一层不包括任何激活函数。
- 由于 Q-learning 要求我们了解当前和下一个状态,我们需要从数据生成开始。我们将表示初始状态 s 的游戏空间的预处理输入图像输入到网络中,并获得动作的初始概率分布,或 Q 值。在训练之前,这些值将是随机的和次优的。
- 利用我们的概率张量,我们然后使用 argmax()函数选择具有当前最高概率的动作,并使用它来构建 epsilon 贪婪策略。
- 使用我们的策略,我们将选择动作 a ,并评估我们在健身房环境中的决策接收关于新状态s’、奖励 r 以及该集是否已结束的信息。
- 我们以列表形式
将该信息组合存储在一个缓冲区中,并重复步骤 2-4 预设次数,以建立一个足够大的缓冲区数据集。 - G 生成我们的目标 y 值, R' 和 A' ,这是损失计算所需要的。虽然前者只是从 R 中减去,但是我们通过将S’输入到我们的网络中来获得 A’。
- 有了所有的组件,我们就可以计算训练网络的损耗。
- 一旦训练结束,我们将评估我们的代理在新一集游戏中的表现,并记录他们的表现
让我们从导入所有必需的包开始,包括 OpenAI 和 Vizdoomgym 环境。我们还将安装火炬视觉所需的 AV 包,我们将使用它进行可视化。请注意,安装完成后必须重新启动运行时。
!sudo apt-get update!sudo apt-get install build-essential zlib1g-dev libsdl2-dev libjpeg-dev nasm tar libbz2-dev libgtk2.0-dev cmake git libfluidsynth-dev libgme-dev libopenal-dev timidity libwildmidi-dev unzip# Boost libraries!sudo apt-get install libboost-all-dev# Lua binding dependencies!apt-get install liblua5.1-dev!sudo apt-get install cmake libboost-all-dev libgtk2.0-dev libsdl2-dev python-numpy git!git clone [https://github.com/shakenes/vizdoomgym.git](https://github.com/shakenes/vizdoomgym.git)!python3 -m pip install -e vizdoomgym/!pip install av
接下来,我们初始化我们的环境场景,检查观察空间和动作空间,并可视化我们的环境..
import gymimport vizdoomgymenv = gym.make(‘VizdoomDefendLine-v0’)n_outputs = env.action_space.nprint(n_outputs)observation = env.reset()import matplotlib.pyplot as pltfor i in range(22): if i > 20: print(observation.shape) plt.imshow(observation) plt.show() observation, _, _, _ = env.step(1)
接下来,我们将定义预处理包装器。这些类继承自 OpenAI gym 基类,覆盖了它们的方法和变量,以便隐式地提供所有必要的预处理。我们将开始定义一个包装器来重复许多帧的每个动作,并执行元素方式的最大值以增加任何动作的强度。您会注意到一些三级参数,如 fire_first 和no _ ops——这些是特定于环境的,在 Vizdoomgym 中对我们没有影响。
class RepeatActionAndMaxFrame(gym.Wrapper):
#input: environment, repeat
#init frame buffer as an array of zeros in shape 2 x the obs space
def __init__(self, env=None, repeat=4, clip_reward=False, no_ops=0,
fire_first=False):
super(RepeatActionAndMaxFrame, self).__init__(env)
self.repeat = repeat
self.shape = env.observation_space.low.shape
self.frame_buffer = np.zeros_like((2, self.shape))
self.clip_reward = clip_reward
self.no_ops = no_ops
self.fire_first = fire_firstdef step(self, action):
t_reward = 0.0
done = False
for i in range(self.repeat):
obs, reward, done, info = self.env.step(action)
if self.clip_reward:
reward = np.clip(np.array([reward]), -1, 1)[0]
t_reward += reward
idx = i % 2
self.frame_buffer[idx] = obs
if done:
break max_frame = np.maximum(self.frame_buffer[0], self.frame_buffer[1])
return max_frame, t_reward, done, infodef reset(self):
obs = self.env.reset()
no_ops = np.random.randint(self.no_ops)+1 if self.no_ops > 0 else 0
for _ in range(no_ops):
_, _, done, _ = self.env.step(0)
if done:
self.env.reset()
#Fire first seems quite useless, probably meant for something like space invader
if self.fire_first:
assert self.env.unwrapped.get_action_meanings()[1] == ‘FIRE’
obs, _, _, _ = self.env.step(1) self.frame_buffer = np.zeros_like((2,self.shape))
self.frame_buffer[0] = obs return obs
接下来,我们为我们的观察定义预处理函数。我们将使我们的环境对称,将它转换到盒子空间,将通道整数交换到张量的前面,并将其从原始(320,480)分辨率调整到(84,84)区域。我们也将我们的环境灰度化,并通过除以一个常数来归一化整个图像。
class PreprocessFrame(gym.ObservationWrapper):
#set shape by swapping channels axis
#set observation space to new shape using gym.spaces.Box (0 to 1.0)
def __init__(self, shape, env=None):
super(PreprocessFrame, self).__init__(env)
self.shape = (shape[2], shape[0], shape[1])
self.observation_space = gym.spaces.Box(low=0.0, high=1.0,
shape=self.shape, dtype=np.float32)def observation(self, obs):
new_frame = cv2.cvtColor(obs, cv2.COLOR_RGB2GRAY)
resized_screen = cv2.resize(new_frame, self.shape[1:],
interpolation=cv2.INTER_AREA)
new_obs = np.array(resized_screen, dtype=np.uint8).reshape(self.shape)
new_obs = new_obs / 255.0 return new_obs
接下来,我们创建一个包装器来处理帧堆叠。这里的目标是通过将几个帧堆叠在一起作为单个批次,帮助从堆叠帧中捕捉运动和方向。这样,我们可以捕捉环境中元素的位置、平移、速度和加速度。通过堆叠,我们的输入采用(4,84,84,1)的形状。
class StackFrames(gym.ObservationWrapper):
#init the new obs space (gym.spaces.Box) low & high bounds as repeat of n_steps. These should have been defined for vizdooom
#Create a return a stack of observations
def __init__(self, env, repeat):
super(StackFrames, self).__init__(env)
self.observation_space = gym.spaces.Box(
env.observation_space.low.repeat(repeat, axis=0),
env.observation_space.high.repeat(repeat, axis=0),
dtype=np.float32)
self.stack = collections.deque(maxlen=repeat)def reset(self):
self.stack.clear()
observation = self.env.reset()
for _ in range(self.stack.maxlen):
self.stack.append(observation) return np.array(self.stack).reshape(self.observation_space.low.shape)def observation(self, observation):
self.stack.append(observation) return np.array(self.stack).reshape(self.observation_space.low.shape)
最后,在返回最终环境供使用之前,我们将所有的包装器绑定到一个单独的 make_env() 方法中。
def make_env(env_name, shape=(84,84,1), repeat=4, clip_rewards=False,
no_ops=0, fire_first=False):
env = gym.make(env_name)
env = PreprocessFrame(shape, env)
env = RepeatActionAndMaxFrame(env, repeat, clip_rewards, no_ops, fire_first)
env = StackFrames(env, repeat) return env
接下来,让我们定义我们的模型,一个深度 Q 网络。这本质上是一个三层卷积网络,它采用预处理的输入观察值,将生成的展平输出馈送到一个全连接层,生成游戏空间中的状态-动作值作为输出。请注意,这里没有激活层,因为激活层的存在会导致二进制输出分布。我们的损失是我们计算的状态-动作值与我们预测的状态-动作值的平方差。我们将使用 RMSProp 优化器来最小化我们在训练期间的损失。
import os
import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as npclass DeepQNetwork(nn.Module):
def __init__(self, lr, n_actions, name, input_dims, chkpt_dir):
super(DeepQNetwork, self).__init__()
self.checkpoint_dir = chkpt_dir
self.checkpoint_file = os.path.join(self.checkpoint_dir, name) self.conv1 = nn.Conv2d(input_dims[0], 32, 8, stride=4)
self.conv2 = nn.Conv2d(32, 64, 4, stride=2)
self.conv3 = nn.Conv2d(64, 64, 3, stride=1) fc_input_dims = self.calculate_conv_output_dims(input_dims) self.fc1 = nn.Linear(fc_input_dims, 512)
self.fc2 = nn.Linear(512, n_actions) self.optimizer = optim.RMSprop(self.parameters(), lr=lr) self.loss = nn.MSELoss()
self.device = T.device(‘cuda:0’ if T.cuda.is_available() else ‘cpu’)
self.to(self.device)def calculate_conv_output_dims(self, input_dims):
state = T.zeros(1, *input_dims)
dims = self.conv1(state)
dims = self.conv2(dims)
dims = self.conv3(dims)
return int(np.prod(dims.size()))def forward(self, state):
conv1 = F.relu(self.conv1(state))
conv2 = F.relu(self.conv2(conv1))
conv3 = F.relu(self.conv3(conv2))
# conv3 shape is BS x n_filters x H x W
conv_state = conv3.view(conv3.size()[0], -1)
# conv_state shape is BS x (n_filters * H * W)
flat1 = F.relu(self.fc1(conv_state))
actions = self.fc2(flat1) return actionsdef save_checkpoint(self):
print(‘… saving checkpoint …’)
T.save(self.state_dict(), self.checkpoint_file)def load_checkpoint(self):
print(‘… loading checkpoint …’)
self.load_state_dict(T.load(self.checkpoint_file))
回想一下,Q-learning 的更新功能需要:
- 当前状态 s
- 当前动作一
- 当前动作后的奖励 r
- 下一个状态s’
- 下一个动作a’
为了以有意义的数量提供这些参数,我们需要按照一组参数评估我们当前的策略,并将所有变量存储在一个缓冲区中,我们将在训练期间从该缓冲区中提取迷你批次中的数据。因此,我们需要一个重放内存缓冲区来存储和提取观察值。
import numpy as npclass ReplayBuffer(object):
def __init__(self, max_size, input_shape, n_actions):
self.mem_size = max_size
self.mem_cntr = 0
self.state_memory = np.zeros((self.mem_size, *input_shape),
dtype=np.float32)
self.new_state_memory = np.zeros((self.mem_size, *input_shape),
dtype=np.float32) self.action_memory = np.zeros(self.mem_size, dtype=np.int64)
self.reward_memory = np.zeros(self.mem_size, dtype=np.float32)
self.terminal_memory = np.zeros(self.mem_size, dtype=np.bool)#Identify index and store the the current SARSA into batch memory
def store_transition(self, state, action, reward, state_, done):
index = self.mem_cntr % self.mem_size
self.state_memory[index] = state
self.new_state_memory[index] = state_
self.action_memory[index] = action
self.reward_memory[index] = reward
self.terminal_memory[index] = done
self.mem_cntr += 1def sample_buffer(self, batch_size):
max_mem = min(self.mem_cntr, self.mem_size)
batch = np.random.choice(max_mem, batch_size, replace=False)
states = self.state_memory[batch]
actions = self.action_memory[batch]
rewards = self.reward_memory[batch]
states_ = self.new_state_memory[batch]
terminal = self.terminal_memory[batch] return states, actions, rewards, states_, terminal
接下来,我们将定义我们的代理。我们的代理正在使用一个勘探率递减的ε贪婪策略,以便随着时间的推移最大化开发。为了学会预测使我们的累积奖励最大化的状态-行动-值,我们的代理人将使用通过记忆抽样获得的贴现的未来奖励。
您会注意到,作为代理的一部分,我们初始化了 DQN 的两个副本,并使用方法将原始网络的权重参数复制到目标网络中。这种双网络方法允许我们在使用现有策略的训练过程中生成数据,同时仍然为下一个策略迭代优化我们的参数,减少损失振荡。
import numpy as np
import torch as T
#from deep_q_network import DeepQNetwork
#from replay_memory import ReplayBufferclass DQNAgent(object):
def __init__(self, gamma, epsilon, lr, n_actions, input_dims,
mem_size, batch_size, eps_min=0.01, eps_dec=5e-7,
replace=1000, algo=None, env_name=None, chkpt_dir=’tmp/dqn’):
self.gamma = gamma
self.epsilon = epsilon
self.lr = lr
self.n_actions = n_actions
self.input_dims = input_dims
self.batch_size = batch_size
self.eps_min = eps_min
self.eps_dec = eps_dec
self.replace_target_cnt = replace
self.algo = algo
self.env_name = env_name
self.chkpt_dir = chkpt_dir
self.action_space = [i for i in range(n_actions)]
self.learn_step_counter = 0self.memory = ReplayBuffer(mem_size, input_dims, n_actions)self.q_eval = DeepQNetwork(self.lr, self.n_actions,
input_dims=self.input_dims,
name=self.env_name+’_’+self.algo+’_q_eval’,
chkpt_dir=self.chkpt_dir)self.q_next = DeepQNetwork(self.lr, self.n_actions,
input_dims=self.input_dims,
name=self.env_name+’_’+self.algo+’_q_next’,
chkpt_dir=self.chkpt_dir)#Epsilon greedy action selection
def choose_action(self, observation):
if np.random.random() > self.epsilon:
state = T.tensor([observation],dtype=T.float).to(self.q_eval.device)
actions = self.q_eval.forward(state)
action = T.argmax(actions).item()
else:
action = np.random.choice(self.action_space)return actiondef store_transition(self, state, action, reward, state_, done):
self.memory.store_transition(state, action, reward, state_, done)def sample_memory(self):
state, action, reward, new_state, done = \
self.memory.sample_buffer(self.batch_size)states = T.tensor(state).to(self.q_eval.device)
rewards = T.tensor(reward).to(self.q_eval.device)
dones = T.tensor(done).to(self.q_eval.device)
actions = T.tensor(action).to(self.q_eval.device)
states_ = T.tensor(new_state).to(self.q_eval.device)return states, actions, rewards, states_, donesdef replace_target_network(self):
if self.learn_step_counter % self.replace_target_cnt == 0:
self.q_next.load_state_dict(self.q_eval.state_dict())def decrement_epsilon(self):
self.epsilon = self.epsilon — self.eps_dec \
if self.epsilon > self.eps_min else self.eps_mindef save_models(self):
self.q_eval.save_checkpoint()
self.q_next.save_checkpoint()def load_models(self):
self.q_eval.load_checkpoint()
self.q_next.load_checkpoint()
#Make sure you understand this line by line
def learn(self):
if self.memory.mem_cntr < self.batch_size:
returnself.q_eval.optimizer.zero_grad()self.replace_target_network()states, actions, rewards, states_, dones = self.sample_memory()
indices = np.arange(self.batch_size)q_pred = self.q_eval.forward(states)[indices, actions]
q_next = self.q_next.forward(states_).max(dim=1)[0]q_next[dones] = 0.0
q_target = rewards + self.gamma*q_nextloss = self.q_eval.loss(q_target, q_pred).to(self.q_eval.device)
loss.backward()
self.q_eval.optimizer.step()
self.learn_step_counter += 1 self.decrement_epsilon()
定义了所有支持代码后,让我们运行主训练循环。我们已经在最初的总结中定义了大部分,但是让我们为后代回忆一下。
- 对于训练集的每一步,在使用ε-贪婪策略选择下一个动作之前,我们将输入图像堆栈输入到我们的网络中,以生成可用动作的概率分布
- 然后,我们将它输入到网络中,获取下一个状态和相应奖励的信息,并将其存储到我们的缓冲区中。我们更新我们的堆栈,并通过一些预定义的步骤重复这一过程。
- 在一集的结尾,我们将下一个状态输入到我们的网络中,以便获得下一个动作。我们还通过对当前奖励进行贴现来计算下一个奖励。
- 我们通过 Q 学习更新函数生成我们的目标 y 值,并训练我们的网络。
- 通过最小化训练损失,我们更新网络权重参数,以便为下一个策略输出改进的状态-动作值。
- 我们通过跟踪模型的平均得分(在 100 个训练步骤中测量)来评估模型。
env = make_env(‘VizdoomDefendLine-v0’)
best_score = -np.inf
load_checkpoint = False
n_games = 500
agent = DQNAgent(gamma=0.99, epsilon=1.0, lr=0.0001,input_dims=(env.observation_space.shape),n_actions=env.action_space.n, mem_size=5000, eps_min=0.1,batch_size=32, replace=1000, eps_dec=1e-5,chkpt_dir=’/content/’, algo=’DQNAgent’,env_name=’vizdoogym’)if load_checkpoint:
agent.load_models()fname = agent.algo + ‘_’ + agent.env_name + ‘_lr’ + str(agent.lr) +’_’+ str(n_games) + ‘games’
figure_file = ‘plots/’ + fname + ‘.png’n_steps = 0
scores, eps_history, steps_array = [], [], []for i in range(n_games):
done = False
observation = env.reset()score = 0
while not done:
action = agent.choose_action(observation)
observation_, reward, done, info = env.step(action)
score += rewardif not load_checkpoint:
agent.store_transition(observation, action,reward, observation_, int(done))
agent.learn()
observation = observation_
n_steps += 1scores.append(score)
steps_array.append(n_steps)avg_score = np.mean(scores[-100:])if avg_score > best_score:
best_score = avg_score
print(‘Checkpoint saved at episode ‘, i)
agent.save_models()print(‘Episode: ‘, i,’Score: ‘, score,’ Average score: %.2f’ % avg_score, ‘Best average: %.2f’ % best_score,’Epsilon: %.2f’ % agent.epsilon, ‘Steps:’, n_steps)eps_history.append(agent.epsilon)
if load_checkpoint and n_steps >= 18000:
break
我们绘制了 500、1000 和 2000 集的代理商平均得分和 epsilon 比率。
500 集后我们经纪人的奖励分配。
1000 集后我们经纪人的奖励分配。
2000 集后我们经纪人的报酬分配。
查看结果,您会注意到一些模式。
- 在最初的 400 集里,我们观察到了平均分数为 12 的表演的最初增长。这段时间,代理人在重重地探索。
- 在 400-750 个训练集之间,我们观察到 epsilon 衰减到 20%以下,表明探索率显著降低。因此,当代理试图最大化利用时,它的性能可能会显著提高或下降。
- 然而,在过去的 750 集里,代理已经进行了足够的探索来找到改进的策略,导致模型性能的增长和稳定。
有趣的是,我们可以在游戏中观察到这些点。下面是我们的特工分别在 500 集、1000 集和 2000 集时的游戏片段。
500 集特工训练的游戏性。
特工的游戏性训练了 1000 集。
2000 集特工训练的游戏性。
所有的特工都表现出持续射击——考虑到弹药消耗没有惩罚,这是可以理解的。请注意,在 500 集时训练过的特工如何表现出更大的转弯弧线,而训练更好的特工似乎坚持在地图的特定区域。这种行为可能是对棕色怪物产卵的预期,这是一种依靠粉色怪物走近穿越火线的策略。请注意,这个环境仍然相对简单,以便于相对容易的训练——引入弹药使用惩罚,或者增加行动空间包括扫射,将导致明显不同的行为。
这就结束了 Q-learning 的实现。在我们的下一篇文章中,我们将继续用更高级的 Q-learning 方法来检查我们的代理在这些环境中的性能。
我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库。
来源
萨顿等人。al,“强化学习”
塔博尔,“运动中的强化学习”
用数据扮演上帝
泰勒·维克在 Unsplash 上的照片
5 个综合数据问题&评估新机器学习方法的代码
所以你有一个你想测试的机器学习方法。很好,但是你怎么证明它的价值呢?答案是:合成数据。
在评估新方法时,合成数据比真实数据有几个优势。对于合成数据,任务的性质、输入特征、维度、采样大小、噪声程度和其他数据因素完全由创建者控制。合成数据问题千差万别,模型在这些问题上的表现可以告诉我们很多关于模型的优点和缺点-也许它在线性可分数据上表现良好,或者不能很好地处理噪声-这可以帮助理解模型在真实世界的数据类型上会表现良好。
合成数据为如何将模型应用于真实数据提供了蓝图。
在本文中,我将概述 5 个二元分类合成数据问题,提供用于创建数据的 Python 代码(支持调整参数),并解释如何解释这些问题的结果。此外,我将概述每个问题的扩展。
我们开始吧!
环形问题
问题是
在 2D 环分类问题中,有两个圆,都以(0.5,0.5)为中心。圆环由两个圆定义,外圆半径为 0.5,内圆半径为 0.3。
落在红色和蓝色区域的重叠中的所有数据点被标记为 1;否则,它们被标记为 0。
写成一个函数,环的问题是:
在下图中,红点代表 1,蓝点代表 0。
环形问题的价值在于它的非线性和几何上明显的模式。
扩展ˌ扩张
- 添加噪声。这种分类任务的噪声版本可以用沿着解边界的高斯噪声来构建。在下面的等式中, z 是从具有标准偏差 s 的高斯分布的正侧抽取的随机数的向量。
- 3+维的环问题。环带问题的另一种形式可以适用于三维或更多维,只需在方程中增加另一维即可。
密码
提供的四个参数是:
- n ,数据点的数量
- 内径,内圆的半径
- 外半径,外圆的半径
- z ,添加了随机性的正态分布的尺度
…可以对其进行微调以调整数据的属性。
import pandas as pd
import numpy as np
import math as m
import random as rn = 800 #number of data points
innerRadius = 0.3 #radius of inner circle
outerRadius = 0.5 #radius of outer circle
z = 0 #gaussian multiplierdata = {'x1':[],'x2':[],'label':[]}
for i in range(n):
x1 = r.randint(1,100_000)/100_000
x2 = r.randint(1,100_000)/100_000
coef = abs(np.random.normal(scale=z))
if (x1-0.5)**2 + (x2-0.5)**2 > (innerRadius-coef)**2:
if (x1-0.5)**2 + (x2-0.5)**2 < (outerRadius-coef)**2:
label = 1
else:
label = 0
else:
label = 0
data['x1'].append(x1)
data['x2'].append(x2)
data['label'].append(label)
data = pd.DataFrame(data
数据输出的前 10 行是:
要绘制环:
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5))
sns.scatterplot(data['x1'],data['x2'],data['label'])
400 个数据点,无噪声:
400 个数据点, z = 0.1:
400 个数据点,其中 z = 0.25:
数据应用
许多真实世界的数据集(如医疗诊断)都有特征空间,在这些空间中,标注的分离更加清晰。在具有合理数量的噪声(z = 0.1 至 0.2)的环形问题上成功的模型可以识别真实世界数据中的几何上明显的非线性模式,并且具有合理的噪声容限。通过增加或减少环的面积,该模型可以适用于平衡或非平衡问题。通过减小环的面积,问题变得不平衡,类似于医学问题,如癌症诊断,其中阳性诊断的数量远小于阴性诊断的数量。通过将环的面积增加到网格面积的一半,问题变得更加平衡,这可能有助于识别男性或女性、18 岁以下或 18 岁以上等。比较几何上明显的问题和棋盘问题。
棋盘二元分类问题
问题是
像棋盘一样,交替的二维空间被标记为 0 和 1。所有值 1(红色)具有相同奇偶性的 x1 和 x2 值(或者都是偶数或者都是奇数),而所有值 0(蓝色)具有相反奇偶性的 x1 和 x2 值。
下面提供了一个视觉效果:
这在数学上可以表示为
棋盘问题的一个解决方案如下:
每条对角线将这些点分隔成各自的颜色。
棋盘问题在两个方面是有价值的——首先,因为虽然数据是线性可分的,但它需要多条线来分隔的事实使它更加真实和复杂;第二,数据是分散的,从计算机的角度来看,数据不是几何图形而是数字图形,没有几何图形,只有数字图形。
扩展ˌ扩张
- 棋盘问题可以扩展成包含 n 种颜色
- 这个问题也可以变成一个三维或 m- D 棋盘问题
密码
在棋盘问题中,可调参数是:
- dim1 ,二维空间的第一维
- dim2 ,二维空间的第二维度
import pandas as pd
dim1 = 20 #x-dimension
dim2 = 20 #y-dimension
data = {'x1':[],'x2':[],'label':[]}
for xcoor in range(dim1):
for ycoor in range(dim2):
if xcoor % 2 == 0 and ycoor % 2 == 0:
label = 1
elif xcoor % 2 == 1 and ycoor % 2 == 1:
label = 1
else:
label = 0
data['x1'].append(xcoor)
data['x2'].append(ycoor)
data['label'].append(label)
data = pd.DataFrame(data)
数据帧看起来像:
…可以想象为
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5))
sns.scatterplot(data['x1'],data['x2'],data['label'])
数据应用
在棋盘问题上表现良好的模型在大量分散的数据上表现良好;其更复杂的关系不一定是几何的而是数字的。它在分层数据上也表现良好;也就是说,只能用多条线分开。
x 维奇偶二元分类问题
问题是
在 X 维(默认为 X = 10)奇偶校验问题中,如果一个 X 位字符串具有奇数个 1(因此,奇数个 0),则该字符串被分类为 1;如果该字符串具有偶数个 1(因此,偶数个 0),则该字符串被分类为 0。这个奇偶校验问题是非线性的。
X 维奇偶校验问题的价值在于,所有列之间存在更深层次的数学关系,而不是某一列是最重要的。这可能是没有几何方面的棋盘问题;宇称问题不能用几何来分析。
扩展ˌ扩张
- 充当噪声的附加噪声列。
- 增加值的范围(而不是只有 0 和 1,可能是 0、1、2 和 3)—例如,如果 3 的数量为奇数,则最终标签为 1。
密码
import pandas as pd
import numpy as np
import random as rdimensions = 10 #bitstring dimensions
data_points = 500 #number of data pointsdata = {'label':[]}
for dimension in range(dimensions):
data['d'+str(dimension+1)] = []
for i in range(data_points):
bitstring = [r.randint(0,1) for i in range(dimensions)]
count0 = 0
for i in range(len(bitstring)):
if bitstring[i] == 0:
count0 += 1
data['d'+str(i+1)].append(bitstring[i])
if count0 % 2 == 0:
data['label'].append(0)
else:
data['label'].append(1)
data = pd.DataFrame(data)
这将输出数据帧:
由于有 2 个以上的维度,我们将尝试用主成分分析来可视化数据。
from sklearn import decomposition
pca = decomposition.PCA(n_components=2)
pca.fit(data.drop('label',axis=1))
X = pca.transform(data.drop('label',axis=1))
pca.explained_variance_.sum()
这将输出:
0.5922694981939761
一个相当低的解释方差,但它应该足以显示数据的一般性质。
绘制数据:
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5))
sns.scatterplot(X[:,0],X[:,1],data['label'])
很明显,没有一个清晰的几何图案可以清晰地确定标签。
数据应用
在 X 维奇偶校验问题上成功的模型在最终目标是所有特征的半相等组合的数据集中也是成功的,例如通过某种特征重要性方法消除了不太有用的列的数据。
异或二进制分类问题
问题
有两个特征的 XOR 值创建了最终目标,还有 x 个额外的随机特征作为噪声。默认情况下, x 是 97(这样总共有 100 列,包括标签列)。
XOR 函数的作用如下:
- 0, 0 : 0
- 1, 1 : 0
- 0, 1: 1
- 1, 0: 1
如果两个输入相同,则输出为 0;否则,输出为 1。
XOR 问题的价值来自于这样一个事实:在几层随机数据下隐藏着一个非常简单的数字关系。
密码
可以调整的参数有:
- noise_features,数据中随机噪声特征的数量
- 数据点,数据点的数量
import pandas as pd
import randomnoise_features = 97
data_points = 500def XOR(bit1,bit2):
if bit1 and bit2:
return 0
elif not bit1 and not bit2:
return 0
else:
return 1data = {'label':[],'bit1':[],'bit2':[]}
for index in range(noise_features):
data['nf'+str(index)] = []
for i in range(data_points):
bit1 = random.randint(0,1)
bit2 = random.randint(0,1)
label = XOR(bit1,bit2)
data['label'].append(label)
data['bit1'].append(bit1)
data['bit2'].append(bit2)
for noise_index in range(noise_features):
data['nf'+str(noise_index)].append(random.randint(0,1))
data = pd.DataFrame(data)
输出数据帧(“nf”代表“噪声特征”):
数据应用
在 XOR 问题中表现良好的模型(尤其是具有高维数的模型)能够强有力地识别哪些特征是重要的,哪些是不相关的。这些类型的模型可能在用于数字识别的 MNIST 数据集上表现良好,因为某些像素在确定最终数字时是无用的,而某些像素的潜在关系在最终标注中具有重大影响。
相关问题
问题
畜栏数据集有 6 个二元特征,标签由( f 1 和 f 2)或( f 3 和 f 4)确定,其中 fn 表示 nt h 输入。任何额外数量的特征 n (默认为 1)与类别标签具有 m %(默认为 75%)相关性,并且可以添加任何数量的 p (默认为 5)额外随机特征。
相关性问题的价值在于衡量模型如何利用相关特征,以及如何从随机特征中识别高度相关的特征。
密码
可以调整的参数有:
- 数据点,数据点的数量
- 随机 _ 特征,随机特征数量
- 相关特征,与目标相关的特征数量
- 相关性,相关特征与目标的相关百分比
import pandas as pd
import randomdata_points = 500 #number of data points
random_features = 5 #number of random features
corr_features = 1 #number of correlated features
correlation = 75 #% correlation with targetdef logic_oracle(f1,f2,f3,f4):
if f1 and f2:
return 1
elif f3 and f4:
return 1
else:
return 0data = {'label':[],'f1':[],'f2':[],'f3':[],'f4':[]}
for corr_feature in range(corr_features):
data['corr'+str(corr_feature+1)] = []
for rand_feature in range(random_features):
data['rand'+str(rand_feature+1)] = []for i in range(data_points):
f1 = random.randint(0,1)
f2 = random.randint(0,1)
f3 = random.randint(0,1)
f4 = random.randint(0,1)
label = logic_oracle(f1,f2,f3,f4)
data['f1'].append(f1)
data['f2'].append(f2)
data['f3'].append(f3)
data['f4'].append(f4)
data['label'].append(label)
for index in range(random_features):
data['rand'+str(index+1)].append(random.randint(0,1))for corr_feature in range(corr_features):
for label in data['label']:
if random.randint(1,100) < correlation:
data['corr'+str(corr_feature+1)].append(int(label))
else:
data['corr'+str(corr_feature+1)].append(int(not label))
data = pd.DataFrame(data)
输出数据帧:
数据应用
在几个真实世界的数据集中,特征将相关,因为它们不完全预测目标,但足够紧密地联系在一起,因此它在标签中有很大的发言权。在相关性问题中表现良好的模型可以区分现实数据中存在的三种类型的要素-直接要素,其组合揭示了查找输出的“黄金法则”、在大多数情况下给出正确答案的相关要素,以及其存在会影响准确性的不相关要素。
感谢阅读!
使用强化学习玩最佳单挑扑克
采用近似策略优化和强化蒙特卡罗算法玩最优单挑扑克
照片来自 Unsplash 的 CCO
强化学习一直是近年来许多人工智能突破的核心。算法在没有数据收集的繁重约束的情况下学习的机会为关键的进步提供了巨大的机会。谷歌的 DeepMind 一直处于强化学习的中心,其项目突破引起了全国的关注,如 AlphaZero,一个自学成才的竞争代理人在 4 天内成为了世界上最好的围棋选手。
传统的强化学习算法如 Q-learning、SARSA 等。在封闭的单代理环境中工作良好,他们能够不断探索,直到找到最佳策略。然而,这些算法的一个关键假设是一个稳定的环境,这意味着转移概率和其他因素在每集之间保持不变。当代理人互相训练时,例如在扑克的情况下,这种假设是不可能的,因为两个代理人的策略都在不断地演变,导致一个动态的环境。此外,上述算法本质上是确定性的,这意味着在给定状态下,与另一个动作相比,一个动作总是被认为是最优的。
然而,确定性政策不适用于日常生活或扑克游戏。例如,在扑克游戏中,当有机会时,玩家可以虚张声势,这意味着他们通过投入过大的赌注来吓唬其他玩家弃牌,从而代表比他们实际拥有的牌更好的牌。但是,如果一个玩家每次都虚张声势,对手会认可这样的策略,并很容易让这个玩家破产。这导致了另一类被称为策略梯度算法的算法,该算法输出随机最优策略,然后可以从中进行采样。
然而,传统策略梯度方法的一个大问题是,由于动态环境以及相对较低的数据效率,缺乏收敛性。幸运的是,近年来出现了许多算法,这些算法提供了一个竞争性的自我游戏环境,可以产生最优或接近最优的策略,如 OpenAI 在 2017 年发布的近似策略优化(PPO)。PPO 的独特性源于目标函数,该函数从以前的模型到新的模型剪切概率比,鼓励小的政策变化而不是剧烈的变化。
PPO 论文中的概率比
PPO 论文中的目标函数,其中 A(t)是优势函数
这些方法已经成功应用于许多多人 Atari 游戏,所以我的假设是它们可以很容易地适用于单挑扑克。在锦标赛扑克中,大部分奖金都集中在赢家圈,这意味着要获利,赢钱比简单的“兑现”或每次赚点钱重要得多。在两人对决扑克中,很大一部分的成功在于全押或不全押的决定,所以在这个模拟中,代理人有两个选择,弃牌或全押,这也极大地减少了可能的状态空间,这是用 PPO 等算法解决这个问题的关键。
扑克规则规定“小盲注”和“大盲注”开始下注,这意味着小盲注玩家必须投入一定数量的筹码,而大盲注玩家必须投入两倍的筹码。然后发牌,玩家下注。给代理人的唯一参数如下:他们在与随机的两人对决中赢得当前牌局的几率有多大,他们是否是第一个下注的,以及他们已经下注了多少。然后将它们输入一个简单的双层神经网络。
在我的 Github here 上可以找到用于比较赢家以及模拟确定给定玩家牌的获胜概率的软件包。为了判断 PPO 的有效性,我决定将它的性能与传统策略梯度法中的增强算法进行比较。我选择在不同的大盲注水平下测试代理,我将其设置为总筹码的 1/50、总筹码的 1/20、1/10、1/4 和 1/2。每手牌结束后,他们的筹码数被重置,这一集再次播放。我对每一个都进行了一百万次模拟,然后进行了比较。令我非常感兴趣的是,在任何盲注水平下,在 0.05 的水平上,增强算法和 PPO 算法之间以赢得的筹码衡量的差异都变得不显著。然而,这两种不同算法的策略却大相径庭。例如,当盲注为筹码的 1/20 时,PPO 策略对不合适牌的热图,在完全相同的情况下,与加强策略相比,代理是第一个下注的。
轴是牌值,a 值为 14
强化实际上是一种确定性策略,而 PPO 则是一种温和得多的转变,这意味着 PPO 会虚张声势,而强化只会与可能的赢家玩。有趣的是,这些对应着两种不同类型的扑克玩家。那些被称为“紧”的玩家只在他们认为自己胜算较大时才玩,而“松”玩家会玩很多手牌,虚张声势,甚至在他们认为自己被击败时盖掉一些大牌。随着手中牌的增加,强化代理人仍然更接近于确定性策略,但增加了他们全押的组合数量。
轴是牌值,a 值为 14
这可以看作是代理学习扑克中的一个关键策略,称为底池赔率。这是一个概念,随着你可以赢得的筹码数量相对于你下一次下注的规模增加,你应该愿意玩更多的牌。原因是只要彩池相对较大,期望值将允许较低的获胜概率。例如,如果底池是$800,而您全押的赌注是$200,那么您只需要有 20%的机会赢得这手牌,就可以在长期内达到收支平衡,因为您有 20%的机会赢得$1000 筹码,这样就等于您的赌注是 200。但是,如果底池是$200,而你的全押是$200,你需要 50%的胜率。代理人认识到了这一点,他们玩的筹码更宽松了,因为与盲注较低时相比,他们获得了底池赔率。当盲注占每个代理总筹码的 50%时,我们可以看到这达到了一个临界点,实际上所有的牌都可能被玩。
轴是牌值,a 值为 14
虽然代理人在表现上没有显著差异,但正如你从图表中看到的,他们有着非常不同的游戏风格,这是一个有趣的发现。我预计,随着复杂性的增加,PPO 会做得更好,因为它似乎有一个更平滑的函数,可以适应最优的随机政策,而钢筋接近确定性政策。扑克,尤其是这个有限的场景,只是政策梯度定理的许多可能应用之一,这些应用正在被不断探索。此外,Carnegie Mellon 和脸书创造了一个真正不可思议的代理,能够在 6 人无限注德州扑克中击败顶级扑克玩家。对于强化学习来说,这确实是一个非常激动人心的时刻。
参考文献
- 西尔弗,休伯特,施里特韦瑟,哈萨比斯。 AlphaZero:给国际象棋、日本兵棋和围棋带来新的启示。https://deep mind . com/blog/article/alpha zero-sheding-new-light-grand-games-chess-shogi-and-go
- 舒尔曼,沃尔斯基,德里瓦尔,拉德福德,克里莫夫。近似策略优化算法。https://arxiv.org/abs/1710.03748T2
- 布朗,桑德霍尔姆。用于多人扑克的超人 AI。https://science.sciencemag.org/content/365/6456/885
玩产品飞镖
寻找产品杠杆常常感觉像蒙着眼睛扔飞镖,但这不应该阻止你找到它。
你的产品杠杆是你的产品如何为你的用户和企业创造价值的主要控制手段。如果你找到了你的杠杆,那么你知道使用它你会创造价值。这表明你有控制权,你的行动会带来可预测的回报。你是否能控制你的产品杠杆会有很大的不同。
一旦你找到了你的杠杆,你可以通过学习它如何工作,发现它的极限来优化它,并产生尽可能多的价值。一些产品经理(pm)努力寻找杠杆(即产品-市场匹配工作),其他人试图优化现有的杠杆(即增长工作),但无论哪种方式,过程都是相似的。
我在 Eventbrite 的一个重要角色是通过指导项目经理如何像科学家一样思考来帮助他们找到杠杆。十几个产品团队的经验和结果表明,你可以通过实践找到并优化杠杆。更快也更有战略意义的方法是玩一个产品飞镖的游戏,但是要像一个科学家。
一个定义明确的产品飞镖游戏
把构建产品想象成一个(简化的)飞镖游戏。假设你有三个飞镖和一个普通的镖靶。每一次投掷都根据它的落点给你加分(为了简单起见,让我们忽略双倍或双倍)。如果你最终获得足够的分数,比如说 40 分,那么你会从狩猎监督官那里得到一份奖励——非常简单。
作者图片
在这里,飞镖代表你的产品发布,你的分数是对业务产生的影响。40 分的门槛是你的目标,达到它可能意味着找到一个新的杠杆或优化现有的杠杆。奖励可以是自我肯定、他人尊重或加薪/奖金/升职的任意组合。
渴望得到所有这些丰厚的回报,你手里拿着三个飞镖站到了起跑线上,但在投掷之前,你仔细考虑了你的策略。你是不是直奔靶心,想马上拿到 50 分就赢了?你有没有试着更安全一点,把三个飞镖都瞄准 20 分的楔形?你选择什么策略取决于你的技能水平。只使用一个飞镖击中靶心就能彻底获胜听起来很理想,但如果你是飞镖新手,这很快就会成为一种非常冒险的策略。幸运的是,你可以一次投一个镖,根据第一次的结果,你可以调整你的策略。
考虑到这一点,你下了第一注,瞄准靶心,希望能走运。你的技术很好,但不是完美的,你的飞镖错过了,而是返回 12 点。再次瞄准靶心似乎有风险,所以你要调整策略。你打赌瞄准 20 楔形仍然会让你赢。成功!你目前的分数是 32 分,你只需要再赢 8 分就可以了。你看着棋盘,看到棋盘左侧有一大片区域,有六个楔形,都在 8 到 16 分之间。你打赌瞄准这个区域会给你很大的胜算。投掷一次飞镖,轻松获得 14 分后,奖励就是你的了。
这个版本的产品飞镖游戏最常见于那些真正了解他们的杠杆以及如何使用它们的发达公司。虽然玩这个游戏更多的是关于逻辑和执行,但也有一些真正的策略。拥有每个楔形点的完整信息就像在开始构建产品之前对预期的商业价值有高度的信心。就像在游戏中一样,有了这种信息,在计划策略和下注(瞄准)时,您可以在努力/时间/能力(飞镖技能)和商业价值(点数)之间做出权衡。一旦你开始推出产品(扔飞镖),你会得到事情是否按计划进行的即时反馈。如果他们没有成功(比如靶心太难击中),那么你可以改变策略,仍然会赢。
LinkedIn 是一个很好的公司玩产品飞镖游戏的例子。根据与我过去共事过的 LinkedIn 项目经理的交谈,产品和 BizOps 组织一起为每个团队建立一个定义良好的目标。但是,大部分公司不是这样的。让一家公司玩一场定义明确的产品飞镖游戏需要大量的时间、努力和领导力。在 LinkedIn 的对面,我们有创业公司,他们玩的是非常不同版本的产品飞镖——看起来有点像下面的棋盘——但这个游戏不仅仅是为创业公司准备的。许多成熟的公司也不了解他们所有的杠杆,或者正在积极寻找新的杠杆。当我在 2017 年开始在 Eventbrite 比赛时,我们经常盲投。
产品飞镖的蒙眼游戏
作者图片
在 Eventbrite 与 PMs 合作时,产品飞镖游戏并没有很好的定义。不要把棋盘分成 20 个大小相等的楔形块,想象一下棋盘上布满了一堆大小不同的斑点,每个斑点都有看似随机的点。如果你只做了这一点改变就玩这个游戏(左下),除了你的技巧和精确度会更重要之外,它将和定义良好的游戏非常相似。然而,还有一个主要的规则变化:斑点和点是不可见的(右下方)。看不到它们,你就不知道该瞄准哪里,也没有反馈。否则,规则是一样的,你仍然得到三个飞镖,是 40 分才能得奖。
作者图片
你再次站在线上,不知道该瞄准哪里,你只是投掷你的飞镖,看看会发生什么。当你发现什么都没发生时,你很快变得非常失望。飞镖在黑板上,但是你不知道你得了多少分。你问狩猎监督官,她告诉你,要赢得奖金,你必须证明你得到了 40 分。当你问她怎么会告诉你你需要自己去发现。
这不是一个很好的位置,以我的经验,我见过一些不同的策略在经前综合症试图得到他们的奖励时展开。
策略一:改变规则。你回到狩猎监督官那里,讨价还价使用不同的度量标准来赢得奖励。
谁需要积分?相反,假设你在黑板上画了一个圆,如果你把三个飞镖都投进了这个圆,那么我就得到奖励。为了公平起见,你甚至可以让狩猎监督官来画圈。听起来怎么样?
作者图片
现在,如果你的目标只是收集你的奖品,那么这种方法工作得相当好,但你失去了所有的策略。在定义明确的游戏中,策略和路线修正是获胜的关键,但在这里却是零。即使你有完美的目标,到最后,当涉及到商业价值时,你也没有什么可以展示的。不适合客户、企业,也不适合想玩有趣的战略游戏的人!
策略二:像科学家一样思考。在这里,你继续按照狩猎监督官的规则玩游戏,但你看起来自己也在得分。您构建了一个简单的小框,显示您在投掷完所有三个飞镖后是否获得了 40 分或更多(通过显示“X”或复选标记)。
再次站在空白飞镖靶前,你已经准备好带着你的三个飞镖、你的新分数追踪盒和你的科学家帽出发了。基于一些研究,你假设镖靶的右侧是得分最高的区域,并决定瞄准那里。你投,三个飞镖都落在右下角——差不多(没有人的飞镖技术是完美的)。你检查你的盒子,它上面有一个红色的大“X”——这不足以获得奖励。
作者图片
现在不要对自己太苛刻,失败往往是成功的先决条件。此外,在接下来的步骤中,您有多种选择。您可以:
- 扔掉你的科学家帽子,扔掉盒子,回到策略 1,改变规则。
- 戴上帽子,根据你最近的结果重新考虑你的理论,或者(a)保持你的理论不变,假设把飞镖扔得离右边更近会产生更好的结果,或者(b)更新你的理论,假设把飞镖扔在别处会产生更好的结果。
考虑到我们都在展示业务影响,让我们深入研究后面的选项。
回顾你最近一次不太理想的结果,你更新了你的假设。你现在的假设是棋盘右侧的胜算很大,就像研究所说的那样,但是你的目标有点太低了。然后你把你的飞镖投向棋盘的右上角。出乎意料的是,丁成功了!
作者图片
但是发生了什么?回头看看你的两个假设和你的两个结果,你现在可以对镖靶表面下的东西有一个更好的了解。叠加最后两组飞镖投掷可以让我们绘制出你认为在棋盘下的东西——一个假设图。
作者图片
当然,事情仍然相当生硬,但它仍然比空白板好得多,我们可以不断迭代以填充更多内容。例如,我们可以通过瞄准绿色可能的好区域的一半来测试我们上面的假设,以查看哪个飞镖对我们最后的成功贡献更大。
在真实的产品世界中,这非常类似于了解你的用户以及什么适合他们。你收集证据来创造一个最初的理论和假设。你下注,发布一些东西,然后看看你的用户会有什么反应。根据结果,你稍微调整一下,直到你得到更好的反应。就像现实世界一样,这个产品飞镖游戏并不总是给你一个清晰的答案,告诉你用户是否喜欢它,但只要你有一些信号,你就可以展示进展。
即使只有简单的信号作为反馈,你也可以构建一个相当好的假设图。只要有足够的时间,这个策略最终会找到你的杠杆。一旦你找到了它,你将到达一个目标点,即发现它能走多远——你能从中获得多少商业价值。做到这一点的最好方法是更好地定义我们的游戏。
更好地定义产品飞镖游戏
虽然简单的是或否反馈系统(和一些三角测量)足以找到并优化任何杠杆,但它并不总是非常有效。在某些时候,值得放下飞镖一会儿,努力升级你的跟踪框。现在,它不再在三次投掷后显示简单的“X”或勾号,而是显示三次投掷后的总分数。
回顾过去两轮的得分,我们看到我们的第一轮实际上并不遥远,我们在第二轮中几乎没有达到目标。鉴于两个分数接近,我们可以假设单个重叠的飞镖是携带分数的一个,并打赌通过瞄准那里,我们可以得到远远超过 40 分。
作者图片
跟踪框的下一次升级是让它为每个单独的 dart 提供反馈。这可以让你真正微调你对棋盘背后的东西的理解。我称这种跟踪为内部视图,其中我们必须测量总分的跟踪是外部视图。在这种情况下,外界的看法是重要的衡量标准,它决定了你是否获奖,以及你的假设是否正确。内部观点告知你的赌注有多有效,并有助于将它们和你的假设联系起来。
我在这里看到的常见错误是项目经理过于关注内部视图,试图最大化单个飞镖的点,而不是更大的图片。在真实的产品世界中,外部的观点甚至更加重要,因为第一次飞镖的结果通常会改变第二次飞镖的结果;他们并不像这个简单的游戏所暗示的那样独立。此外,人们喜欢谈论和写的所有数据或指标陷阱几乎都属于内部观点陷阱类别。换句话说,你不需要把它们都背下来,只要留意外面的景色就可以了。
回到游戏中来。
在你的新定位中,你决定在黄色的不确定区域下注来测试你的假设。你扔一个新的飞镖,看到一个巨大的 19 分!你的赌注验证了你的假设,即你过去的分数是由击中这个小区域所驱动的。
作者图片
是时候更新你的假设地图了!除了模糊的好/坏描述符,您还可以更具体地用每个 dart 值的平均分来标记每个部分。如果你试图根据这些信息优化你的杠杆,瞄准黄色区域听起来是个好主意——假设你有这个技能。在某些时候,探索棋盘的其余部分并找到靶心可能是值得的。
作者图片
虽然我的小例子只关注了一个戴着科学家帽、擅长投掷飞镖的项目经理,但他们的学习也为其他项目经理提供了证据。随着足够多的投掷飞镖的科学家都记录下他们的假设和结果,最终一个空白的棋盘可能看起来像这样——一个非常明确的产品飞镖游戏。
所有图片由作者|使用谷歌幻灯片制作
感谢阅读。🏅
主要产品经理要点:
- 为了找到你的杠杆,你需要投掷飞镖并获得反馈。甚至方向信号通常也是足够的。
- 确保你找到杠杆的最好方法是像科学家一样思考。制定理论和假设,然后在投掷飞镖(推出产品)之前下注(写下它们)。在下另一个赌注之前,利用你的反馈来测试你的假设,并根据你所学到的来调整你的理论。
主要产品领导要点:
- 通过记录分数来展示影响力,不要让别人为了赢得奖励而改变规则。
- 通过聚集和分享每个人的假设和结果来提升每个人。
像电脑程序员一样玩车票游戏
戴夫·普托兹在 Unsplash上拍摄的照片
运用图论和网络分析建立有效的棋盘游戏策略。
“乘车券”是为数不多的战略棋盘游戏之一,它要求玩家在游戏过程中需要大量的计划和策略构建。通过网络分析和图论概念的简单应用,人们可以更有效地玩这个游戏。在这篇文章中,我将分享一些我对“票到乘车”棋盘游戏的计算分析结果。此外,我将讨论如何建立这个游戏的最佳策略。
在我们继续之前,让我澄清一下,这篇文章并不是向你介绍这个游戏或它的规则,期望这篇文章的读者熟悉这个游戏。不管怎样,对于那些不熟悉这个游戏和它的规则的人,请访问这个页面票乘坐维基
建筑结构
我们将使用流行的 python 包[networkx](https://networkx.github.io/)
来构建图形结构。为了适应当前的环境,城市被表示为节点,城市之间的线段可以表示为边。然后,我们使用下面的代码构建网络。
一旦构建了网络,我们可以使用下面的代码快速查看基本信息。
从上面可以注意到,该网络具有 47 个城市(节点)和 90 条轨道(边)。
基本统计
在网络理论中,一个节点的 度 就是它所连接的边数,翻译过来就是一个城市所连接的轨道数。根据下图(图 1)所示的度数分布图,可以看出所有城市的度数都在 1-7 之间。大约 15 个城市的学位为 4,14 个城市的学位为 3。最大 7 度的城市只有一个,6 度的城市有两个。
图 1:学位分布
同样,网络理论术语 边 翻译为轨迹段, 边权重 翻译为当前上下文中的轨迹长度。从下面的边分布图(图 2)可以看出,近 33 条边的轨迹权重为 2,26 条边的权重为 4,25 条边的权重为 3。有 2 条权重为 6 的边。只有一个权重为 8 的边恰好是运行在彼得格勒-斯德哥尔摩之间的轨道段。只需认领这条路线,就能扣上 21 分。
图 2:边缘分布
不出所料,这个游戏的难度随着玩家数量的增加而增加。从下面的比率可以明显看出这一点。
图 3:游戏难度说明
例如,在一个 3 人游戏中,只有 10%的城市度数小于 3,这意味着阻止其他玩家很困难,所以这对于每个玩家来说都是一个简单的游戏。然而,在 4 人和 5 人游戏中,阻挡概率更大。在一个 5 人游戏中,大量的城市近 72%的度数小于 5,这意味着其他玩家有更多的空间来阻止策略,因此游戏变得更加困难。
图 4(下图)显示了目的地卡积分分布。可以观察到非常少的长路线,大约 3 张目的地卡每张 21 分,3 张卡每张 20 分。大量的卡片都是短路线的,大约有 13 张卡片,每张 8 分。
图 4:目的地卡积分分布
现在让我们定义网络的一些常见属性:
- 半径 是将最中心的节点(城市)连接到所有其他节点所需的最小边数。
- 直径 是连接相距较远的两个节点的最小边数。
- 偏心率 是给定节点到所有其他节点的最大距离。较低的离心率值意味着节点相对更靠近网络的中心,而较高的离心率值意味着节点相对远离网络的中心。
- 网络的中心 是离心率等于网络半径的节点的集合。网络中心有 3 个城市(如下所列)。从所有这些城市,到达网络的任何部分都相对容易。
- 网络的外围 是离心率等于网络直径的节点的集合。网络的外围有 7 个城市(如下所列)。从这些城市中的每一个,到达网络的更远的部分是相对困难的。
这些网络参数中的一些计算如下。从下面可以注意到,网络的半径为 5,直径为 9。
图 5:网络参数
图 6:高度古怪的城市
图 7:低怪癖的城市
基于这些网络属性,我们可以得出以下结论:
- 这是一个很好的策略,开始你的游戏时,在像
Berlin,Venezia,Munchen
这样古怪程度低的城市周围宣称路线,因为随着游戏的展开,你可以相对容易地到达你希望继续前进的任何方向。 - 选择包括
Lisboa,Cadiz,Edinburgh,Erzurum,Sochi,Rostov,Moskva
等任何周边城市在内的目的地卡都是不错的策略。这些也是怪癖比较高的城市。在你的控制中拥有一个或多个这些可以增加你建造最长路线的机会。你最好的选择是拥有两个这样的城市,但位于网络的两端。 - 以下列出的是所有来自周边城市的长度等于直径的路线。在你的计划中考虑这些将是一个聪明的策略,特别是如果你正在建造最长的路线。
图 8:周边城市的路线
一些城市的重要性
当然,有些城市比其他城市更重要。这可以用各种中心性度量、聚类系数、连通性和效率来衡量。
这些属性定义如下:
- 度中心性 对于一个节点来说是连接到它的节点的分数。
- 一个节点的介数中心性 是通过它的所有对最短路径的分数之和。
- 节点的接近中心性 是从当前节点到所有其他节点的最短路径距离之和的倒数。
- 节点的聚类系数 是节点邻域内的连接数与邻域全连通时的连接数之比。这个比率表明了节点的邻域连接得有多好。如果邻域是完全连通的,则聚类系数为 1,接近 0 的值意味着邻域中几乎没有任何连接。
- 一个节点的 连通性是为了阻断到该节点的所有路径而需要移除的最少节点数。值越高越好。
所有上述性质可以从网络推导如下:
上面的代码创建了一个数据帧 cities_df ,如下所示(下面只显示了 5 行)
图 9:中心性和聚类系数的图示(仅显示了 5 行)
以下是我们从结果中得出的一些观察结果:
- 基于程度中心性,城市
Paris, Kyiv, Frankfurt
具有较高的值,而城市Ediburgh, Cadiz, Lisboa, Kobenhavn
具有较低的值。此指标的值越低,意味着您在这些城市周围需要更加谨慎,因为您很有可能被这些城市拒之门外。 - 基于中间中心性,城市
Frankfurt, Wien, Budapest
的值较高,而城市Brest, Palermo, Cadiz
的值最低。该度量值越高,意味着经过这些城市的最短路径越多,因此经过这些城市的路线竞争也就越激烈。更多的竞争再次导致堵塞,所以你需要留意这些城市,如果你的计划中有它们的话。 - 基于接近中心性,城市
Wien, Munchen, Budapest
具有更高的价值,因此从这些城市很容易到达网络的远端。而城市Cadiz, Lisboa, Edinburgh
价值较低,因此从这些城市到达远端相对困难。 - 根据聚集系数值,很明显像
Lisboa, Cadiz, Sochi, Barcelona, Brest
这样的城市的邻近区域非常强大,所以即使你的对手试图在一条路线上阻挡你,你也一定能够找到出路并继续前进。不过对于Edinburgh, Moskva, Stockholm, Kharkov, Kobenhavn. London
这样的城市你需要更加谨慎,因为他们的邻里关系并不牢固,你可能会被挡在门外。
除了中心性测量和聚类,我们还可以通过某些攻击策略来测量每个城市的重要性。任何东西的重要性只有在它消失的时候才更为人所知。因此,我们可以迭代地从网络中一次删除一个城市,并测量某些属性的变化,如连通性、效率等。在这种情况下,移除一个城市意味着你的对手已经占领了该城市周围的所有路线,而你被封锁在该城市之外。这个过程可以使用下面的代码来完成:
上述代码将生成如下所示的数据帧 node_importance_df (仅显示 5 行):
图 10:节点重要性的图示(只显示了前 5 行)
下面是我们从节点重要性结果中得出的一些观察结果:
- 可以观察到,当您删除除了
London
和Madrid.
之外的任何城市时,网络几乎保持连接。当您删除London
时,Edinburgh
变得不可达,当您删除Madrid
时,Lisboa
和Cadiz
从其他地方都变得不可达。所以,如果你对这些城市感兴趣,你最好小心London
或Madrid.
- 有趣的是,移除
Essen
会使建造一条路线的总平均成本增加+8.24%
。如果你占领了通往这个城市的所有路线,你将会显著影响到你所有的对手,因为他们的建造成本会增加,因此,他们的游戏会稍微延迟。但是,如果情况反过来,对你就不好了。 - 另一个有趣的观察是,
Marsielle
的缺失通过-10.35%
降低了平均连接性,这同样对你的对手不利,因为他们更容易受到阻塞。
某些轨道段的重要性
在游戏的背景下,任何人在一个城市被封锁是相对困难的,但在一个路段被封锁是相当容易的,因此知道某些边缘路段的重要性在这个游戏中更有用。每个路段的重要性可以通过边的介数中心性以及平均连通性、平均聚类系数、效率等因素的变化来衡量。
使用下面的代码,我们可以计算每条边的介数中心性:
图 11:基于中间中心性的前 5 条边
图 12:基于中间中心性的底部 5 条边
下面是我们从图 11 和图 12 中得到的一些观察结果:
- 可以观察到
Budapest-Wien, Zagrab-Vienna, Wien-Munchen
具有很高的中间中心性。因此,毫无疑问,这些细分市场存在巨大的竞争,因此,如果它们在你的计划中,你需要相对更快地赶上它们。 - 然而,对于像
Rome-Venezia, Barcelona-Marsielle
这样的细分市场,你不需要强调太多,因为它们的介数分值越低,意味着竞争越小。
现在,让我们根据一些攻击策略来评估每条边的重要性,这些攻击策略与我们在上一节中针对节点重要性所做的类似。这可以使用下面的代码来完成:
上述代码将生成如下所示的数据框结果(仅显示了 5 行):
图 13:边的重要性的图示(只显示了 5 行)
下面是我们从边重要性结果中得出的一些观察结果:
- 您可以观察到,只有当您移除边
London-Edinburgh
时,网络才断开连接,当这种情况发生时,只有Edinburgh
变得不可达。但是,网络不受任何其他边的影响。 - 当
Kobenhavn-Essen
消失时,平均最短路径成本增加7.34%.
- 可以观察到,平均连通性受到几乎每次边缘移除的显著影响,当平均连通性下降
8.47%.
时,移除边缘Rostov-Kharkov
导致的影响最大 - 当你移除像
Lisboa-Cadiz, Cadiz-Madrid, Lisboa-Madrid
这样的边缘时,邻域也会受到很大影响,这从网络的平均聚类系数值的巨大下降中可以明显看出。 - 此外,移除边缘
London-Edinburgh.
会导致效率下降2.81%
目的地卡盈利能力分析
如果你知道选对牌的艺术呢?当然,这是很有可能的,但前提是你知道基本的数字。使用下面的代码,我们可以计算所有目的地卡的最短路径特征。
上述代码将生成如下数据帧(仅显示 10 行):
图 14:目的地卡最短路径特征
下面是我们从最短路径结果中得出的一些观察结果:
- 可以观察到,如果你选择最短的路径,所有的长路线(卡点数> =20)都会给你带来大约 50-54 个最终点数。除了在这些路线中的一条路线上走最短的路线之外,你仍然会失去将近一半的机车,因为它最多只花费你大约 20-21 个机车。
- 此外,有趣的是,由于额外的卡点数,路线
Edinburgh-Athina (21)
可能看起来比Lisboa-Danzig (20)
更有吸引力,但最短的路径Lisboa-Danzig
将为您带来50
点数,而Edinburgh-Athina
只能为您带来48
点数。事实上,这一点在shorest path profitability
中表现得更为明显,该比率从本质上解释了该路线的盈利能力。它仅仅是总终点和成本的比率,也就是你用来建造这条路线的机车数量。同样从表中可以看出,最有利可图的最短路径路线是Palermo-Constantinople
,只需花费8
辆机车即可获得25
个积分。除了一条长路线,这将是一个很好的短路线卡。 - 另一个值得关注的因素是
connectivity
,在这种情况下,它适用于所有相关的路由。它指示要删除的最少边数,以封锁给定源节点和目的节点之间的所有路径。越高越好。可以观察到,Berlin-Bucuresti
是迄今为止最稳健的路线,因为它需要5
边缘来阻挡任何人。在较长的路线中,Edinburgh-Athina, Kobenhavn-Erzurum, Cadiz-Stockholm
由于其较低的边缘连通性值,更容易发生堵塞。所以,如果你要走这些路线,你必须更加小心。
替代得分策略
在任何路线上,最短路径完成需要最少的机车数量,但不一定能给你带来最多的分数。在游戏过程中,玩家经常会走一些弯路,特别是为了获得一些额外的分数。
让我们更详细地分析路线Palermo-Moskva
。从目的地卡分析中,我们可以看到,路线Palermo-Moskva
的最短路径以20 locomotives.
为代价为您赢得了总共54
分,但是,如果您有更多的时间,您更有可能计划绕道以获得额外的分数。那你是怎么做到的呢?好吧,这里你需要仔细考虑与每条替代路径相关的各种特性,以便获得好的收益。例如,如果游戏剩下的时间不多了,你可能想选择一条path length
较少的路径,因为完成路径长度的每一段都要花费你一个回合。此外,您应该能够支付所选路径的成本,因此path cost
有时会成为选择最佳路径的重要因素。有时候,path profitability
更有意义,尤其是当你想获得更多投资回报的时候。
推断给定源和目的地之间的所有替代路由需要找到整个网络中的所有简单路径,这有时会导致不确定的时间,尤其是对于大型网络。因此,我们将只找到比最短路径多 2 段的所有替代路径,这简化了我们的过程。下面是实现这一功能的代码片段。
使用上述方法,我们生成了路线Palermo-Moskva
的所有备选路径。您可以仔细分析以下路径,并根据您的标准选择最佳路径。类似地,对于任何给定的路线,可以推导出备选路径。
图 15:巴勒莫-莫斯科备选得分选项
阻挡策略
在上一节中,我们已经看到了替代得分策略。但是,你需要清楚别人阻挡你完成你想要的路线需要付出什么代价。对任何路线使用Mininum edge cut
,我们可以找出最关键边的列表,如果所有边都被阻塞,将意味着您无法完成该路线。
例如,对于上面讨论的同一条路线Palermo-Moskva,
,任何人都需要 3 条边才能把你撞倒。换句话说,这本质上意味着你需要做出明智的选择,相对更快地建立在这些优势上,减少其他人阻碍你的机会。
根据最小边割分析,我们可以得出以下结论:
Berlin-Bucurest
和Paris-Wien
是最坚固的,因为任何人都需要 5 个回合来阻挡它们。这意味着你可以相对轻松地使用这些目标卡,没有被阻挡的压力。Edinburgh-Athina
是最易受攻击的路线,因为任何人只需 1 条边就可以阻止您填写目的地卡。
图 16:最小边缘切割图
结论
虽然你们每个人对游戏都有不同的看法,但知道这里讨论的细节总是有好处的,例如何时走最短路径,何时从最短路径绕道以获得额外的分数,等等,如何阻止某人或如何避免被阻止,哪些部分需要注意,哪些部分可以减少压力,等等。等等。希望你喜欢阅读这篇文章。
分析中使用的所有代码都可以从这个 Github 库下载。
用深度强化学习玩宝藏游戏—第 2/3 部分
艾玩拼图
这是代理在与其他玩家在线对战时“看到”的内容。
在这个系列文章中,我解释了我从事的强化学习(RL)项目。最后,你会知道这是怎么回事:
我讨论了问题的独特和值得注意的方面,我使用了什么技术以及为什么。希望对深度强化学习感兴趣的读者会发现所有这些都很有见地。
在第一部中,对宝藏掉落的游戏进行了说明,并对游戏的缩小版使用了表格 q-learning 方法。在获得了表格方法所需的验证和早期成功之后,这里实现了 Deep Q 网络。
摘要
在获得了表格方法所需的验证和早期成功之后,使用 Keras 库实现了 Deep Q 网络来解决与第 1 部分相同的问题。然而,由于项目的性质,学习过程变得不稳定。实现了几种方法来缓解该问题。即,优先经验重放、目标 Q 网络、具有学习率查找器的循环学习率。
第 2/3 部分的 GitHub 存储库:TD-deepreinforcementlearning-Part 2
表格 q-学习的局限性
在第 1 部分中,q 表适用于尺寸为 3x2(最大可能为 4x2)的电路板。在 4x2 上,q-table 花了很长时间才有收敛值。这是因为在 4x2 中,状态空间具有 4⁹ = 2 个⁸状态。这相当于大约 26 万个州。这对于表格实现来说太多了。原始游戏桌是 4x5,2⁶⁰有不同的可能状态。这使得表格 q 学习对我们的问题不可行。
在许多有实际意义的情况下,包括这个例子,状态比一个表中的条目要多得多。在这些情况下,函数必须是近似的,使用某种更紧凑的参数化函数表示。
对此的解决方案是近似 q 值函数。猜猜什么是好的函数逼近器?是的,你答对了!👏👏深度神经网络!本文是关于使用深度神经网络来逼近 q 值函数。它们通常出现在强化学习环境中,被称为深度 Q 学习网络(DQN)。
从 Q 表到 DQN 的过渡
这很简单。代码中唯一的不同是在我们更新 q 表的那一行:
agentQTable 类中的 q 表更新,代理的表格实现。
相反,我们将把 td_target 和状态一起馈送给神经网络。理论上讲,下一次网络看到状态时,它会输出正确的 q 值。这种情况适用于较小的棋盘尺寸,但对于较大的棋盘尺寸和游戏的原始尺寸来说就不那么容易了。以下更新将取代上面的表格 q-learning 更新:
神经网络
体系结构
决定采用 60 x 300 x 300 x 300 x 300 x 300 x 300 x 8 的形状,尽管输入层和输出层因电路板尺寸而异。例如,对于 4x3 游戏,它们分别是 30 和 4。所有层都是完全连接的。
任务的理想网络架构必须通过验证集误差引导的实验来找到。
在线游戏期间,游戏控制器类从屏幕上收集状态。回合也是从屏幕上推断出来的。然后,将状态作为相应回合的输入馈入模型。输出是 q 值,每个对应于棋盘上的一个动作。具有最高 q 值的动作由代理挑选。
模型和游戏之间的信息流动图。
优化器和损耗
动量= 0.9 的随机梯度下降(SGD)用于所有模型。使用系数 0.5 对渐变进行剪裁。不剪裁梯度有时会导致爆炸结果,神经网络的权重最终是 NaN。
损失函数是行动的预测 q 值和贝尔曼方程计算值之间的 MSE。
学习过程的稳定性
在这一点上,DQN 可以工作,但是只适用于非常小的电路板。它有时会收敛,有时不会。深度强化学习以稳定著称,因为这是当前的热门研究领域。关于不稳定最流行的术语是“致命三重奏”。如果在一个学习算法中存在特定的三个品质,就说学习算法有很大的机会出现分歧。
当使用具有强化学习的深度非线性函数空间时,关于发散是否普遍,以及致命的三元组是否是罪魁祸首,几乎没有指导。
致命三和弦
在 DQN 方法中,我们做了三件值得注意的事情。这些是:
- 函数逼近:我们通过深度神经网络逼近 q 值函数,而不是用数学方法制作实际函数,或者存储每个输入的输出值:
- Bootstrapping:我们部分基于其他估计来更新 q 值估计。我们从另一个猜测中学习一个猜测——我们引导。
- 政策外学习:当学习发生时,我们并不总是遵循政策。这是因为我们采取的是概率为ε(ε)的随机行动。
这三者的结合会产生有害的学习动力,导致函数参数的发散。由于这种分歧的可能性,这三者的结合被称为“致命三重奏”。
在为 3x2 游戏训练时,我注意到模型有时收敛,有时不收敛。由于某种原因,学习过程似乎不稳定。我归咎于致命三和弦,虽然还不清楚这是否是问题所在。尽管如此,学习过程具有所有这三个特点。本文的其余部分是关于为了使学习过程稳定而实现的内容。
体验回放
在数据科学中,数据的底层分布决定了模型学习的内容。经验回放是一个非常非常流行的技术,只要 RL 和 DQN 走到一起就可以实现。用当前事务更新模型很酷,但是浪费数据。模型只看到一个数据。
与此相反,经验回放使用随机的先前交易样本来更新 Q 值。通过保存以前发生的交易的记忆,在每一步中随机抽样。这引入了两个新的超参数:内存大小(max_memory)和每次抽取的样本数量(batch_size)。
Mnih 等人(2015)为各种 Atari 游戏上的 DQN 任务将他们的内存缓冲区大小设置为 10⁶。⁴:以此作为起点是有意义的。在项目的后期,我意识到它会影响性能,所以将它调整到了一个较低的数值。至于批量大小,10 似乎最适合这个游戏。
优先体验重放
体验重放将在线学习代理从按照他们体验的确切顺序处理过渡中解放出来。优先重放进一步解放了代理,使其不用考虑与他们经历的频率相同的转换。⁵
直觉上,有些交易比其他交易更重要。有可能以更明智的方式对交易进行抽样。现在的问题是,哪种交易比其他交易更重要?
代理商必须对那些大额交易更加敏感。这有双重优势。首先,代理可以感觉到高回报并采取适当的行动。第二,代理可以看到敌人是否可以在某个移动后获得高奖励,并可以通过限制自己的行动来防止让敌人获得奖励。
此时,我们只需要一个度量来相应地分配概率。
预测高标量值比预测低标量值有更多的 MSE 损失。这意味着,交易的回报越高,MSE 的损失也就越大。
因此,我们可以放心地将损失视为优先级的衡量标准。损失越大,交易越重要。每次从经验缓冲区中提取一个事务时,都会在其上运行一个预测。由预测导致的丢失被更新,并且事务被放回具有新的优先级度量的缓冲器中。
在训练迭代次数相同的情况下,经过优先体验回放训练的模型似乎比没有经过优先体验回放训练的模型更胜一筹。
目标 Q 网络
这解决了我们学习过程的自举质量。这不是一个万全之策,因为自举仍然存在。然而,它已被证明有助于各种各样的强化学习任务,包括 DQN。
该逻辑只是从与被更新的网络不同的网络中引导。换句话说,有两个网络,互相复制。估计值来自一个网络,值在另一个网络(称为目标网络)上更新。在每个 tn_steps(目标网络步骤的简称)处,权重从目标网络复制到原始网络,网络估计从其导出。
目标 Q 网络在训练过程的早期阶段具有稳定作用。使用 tn_steps = 1 的超参数。意思是每次游戏后复制目标网络。每场比赛持续 100 步左右。所以 tn_steps = 1 实际上意味着每大约 100 次更新就复制一次目标网络。
学习率
学习率是要调整的最重要的超参数。—吴恩达
为了找到最佳的学习速率并系统地调整其他超参数,定义了超参数搜索类。在寻找一个恒定的速率后, 0.009 被确定为目前最好的。
学习率查找器和循环学习率
Leslie Smith 等人描述了一种被称为“超级收敛”的现象,即神经网络的训练速度可能比标准方法快。⁶在这个项目中,在给定相同次数的情况下,以循环学习率训练的代理人似乎比其他人在这个问题上做得更好。
LR 取景器
首先,我们做一个叫做 LR 范围测试的东西。它决定了该方法是否适用于该问题。训练以非常小的学习率开始,并随着情节的增加而增加。理想情况下,在某一点上,模型停止改进,精确度达到稳定状态,停止增加。此时的学习率是循环学习率计划中使用的最大学习率。三分之一或四分之一是最低学习率。
但是,这个问题没有准确性。相反,我使用的是一集训练中发生的平均损失。SGD 的批量大小设置为 100–300 左右。因此,每次损失都是在 30,000 次转换中产生的。这是需要的,使情节顺利如下。否则会太吵,无法解释。
根据 LR 查找器方法,最大学习率大约在 10^-3.5 到 10^-3.之间
从上面的图中,我们可以得出结论,最大学习率可以是 10^-3 和最小学习率。四分之一。
循环学习率计划
澄清一下,“循环”这个词只是指学习率从 min_lr 到 max_lr,然后下降,看起来就像下面的图。转折点不一定是一半,也可以早到四分之一。这个想法基于一个叫做模拟退火的概念,在深度学习文献中很流行。
培养
如果您希望自己训练模型,请准备好将东西转移到 AWS。训练一个在 4x5(原始)游戏上玩得好的代理需要几个小时。只是澄清一下,使用 GPU 不会加快速度,因为模型中没有卷积层。你也不需要 AWS 上的超级强大的实例,我在 c5.xlarge EC2 实例上做了所有的训练。
模型验证和行动⚔️
虽然不是最自动化的方法,但验证模型的一种方法是简单地查看并比较模型预测的等效行动的 q 值。另一种方法是比较镜像态的预测 q 值。为什么这些会是好的启发法的解释在第 1 部分中解释。将结果与第一部分中的表格模型的输出进行比较,我们看到 DQN 的输出不如表格 q-learning 的输出一致。这是因为我们在做近似计算,而且表的大小要大得多。
然而,代理无论如何都会选择具有最高 q 值的动作。即使这些数字之间有很大的差异,在这两种情况下,代理将执行相同(等效)的动作。由于状态沿 y 轴镜像,动作 1 和 6 彼此对应。
我们的学习算法 TD(0)被证明对于算法的表格实现确定性地收敛于单个答案。表格法往往能找到精确解,即往往能精确找到最优值函数和最优策略。
这与这里描述的近似方法不同,近似方法只能找到近似解,但反过来可以有效地应用于更大的问题。如同所有的人工智能一样,适用性的广度和数学的易处理性之间存在矛盾。学习最优策略的代理人做得很好,但在实践中这很少发生。对于这项任务,只有在计算成本极高的情况下才能生成最优策略。我们只能近似到不同的程度。
模型的手动验证显然很耗时,而且根本不实用。因此,下一部分将解释启发式指标以及损失和事件的绘制。
下一步是什么?
测量和绘图是深度学习研究的关键。第三部分介绍了绘图方法。此外,代理是最终确定的,并对玩家在线挑战。接受来自屏幕的输入,点击适当的点,代理能够击败大多数玩家。
关于我
我是一名数据科学家,住在旧金山。热衷于从数据中寻找答案。在 Linkedin 上找到我:梅尔·萨卡里亚
参考
3:哈塞尔特等人 深度强化学习与致命三重奏 。arXiv:1812.02648v1 [cs。2018 年 12 月 6 日
[4]: Hasselt 等 通过深度强化学习的人级控制 。《自然》518,第 529-533 页。2015
[5]: Schaul 等人 优先化经验回放 。arXiv:1511.05952v4 [cs。2016 年 2 月 25 日
[6]: Smith et al. 超收敛:利用大学习率非常快速的训练神经网络 。arXiv:1708.07120 v3【cs。2018 年 5 月 17 日
用深度强化学习玩宝藏游戏—第 3/3 部分
艾玩拼图
在这个系列文章中,我解释了我从事的强化学习(RL)项目。最后,你会知道这是怎么回事:
我讨论了问题的独特和值得注意的方面,我使用了什么技术以及为什么。希望对深度强化学习感兴趣的读者会发现所有这些都很有见地。
在第一部分和第二部分中,解释了宝藏掉落的游戏,并且用一些专门用于深度强化学习的方法实现了深度 Q 网络。这一部分是关于通过绘制度量来监控模型的学习进度,通过对抗来比较代理,以及代理在线游戏的框架。
摘要
跟踪学习进度和绘图是深度学习研究的关键。为了跟踪学习进度,解释了启发式度量,并绘制了情节迭代的 MSE 损失。经过训练的模型相互挑战,以了解哪个学习过程产生更好的代理。为游戏的不同回合(1、2、3 和 4)训练的代理被放在一起在线执行。最后,对 GitHub 库中的一些类进行了高层次的解释。接受来自屏幕的输入,点击适当的点,代理能够击败大多数玩家。
3/3 部分的 GitHub 知识库:TD-deepreinforcementlearning-Part 3
测绘
虽然我是最后提的,但是策划远不是最不重要的。在整个项目中,我一直在绘图,这非常有帮助。不策划,至少不可能成功到这种程度。
在任何机器学习项目中,绘制关于情节的损失是无可争议的关键步骤。在强化学习中,跟踪代理的学习进度也是必不可少的。有时有一些明显的指标你可以跟踪,比如累计奖励或准确性,有时你必须编造它们。无论如何,学习过程有时可能看起来像一个黑匣子,而度量标准将是你黑暗中的灯塔。
指标应传达损失图未能提供的模型进度信息。当你在处理这个问题时,你可能会想到一些有意义的事情。我鼓励你把它们标在损失旁边,并注意它们在训练中的趋势。
在训练循环之后,模型会自动与其绘图一起保存:
这是其中之一:
左栏中的只是损耗,沿 x 轴和 y 轴放大。左下角的点是为学习率计划保留的,如果学习率是恒定的,则留为空白。右边是自定义的启发式分数。你可以在每个图的 y 标签上看到它们是什么分数。它们是对模型学习过程的启发式测量,这就引出了我们对它们的讨论。
启发式得分
在第 1 部分中详细解释了为什么这些分数与模型进度相关。简单地说,一个具有 100%精确 q 值预测的完美模型的得分为 0。分数越低,很可能模型越好。
得分 1
随机选取的(来自经验重放缓冲区)镜像游戏状态的 q 值之间的 MSE 损失。在理想模型中,输出是非常精确的 q 值。因此,镜像的相应动作会非常接近。因为分数是 MSE,理想的模型应该非常接近于 0。
得分 2
相邻等效动作之间的 MSE 损失。如果两个位置下方的直接控制杆中有一枚硬币,则认为动作是等效的。左或右动作将导致完全相同的下一个状态。
左和右的动作对游戏产生相同的效果。
动作 0 和 1 都导致完全相同的下一个状态。
得分公式 2。
得分 3
q 值将显示镜像态之间的差异,因为我们的模型永远不会完美。然而,只有最高的 q 值决定了动作,不管单个值是什么。正因为如此,即使得分和损失都很高,经纪人最终也能打得很好。
得分 3 是 0-1 损失的平均值。它的值在 0 和 1 之间。其测量方法类似于 score1。然而,只考虑最大 q 值。如果镜像状态下的两个动作导致相同的结果,对平均值的贡献为 0。如果不是,那么贡献是 1。例如,如果 score3 为 0.2,这意味着在 10 个镜像状态中的 2 个中,代理仅在 8 个中是一致的。请参见下面每个案例的示例。
尽管这两种状态下的 q 值大相径庭,但最高值属于同一动作。
这里,两个镜像状态具有不一致的最优动作。
情节中的启发式得分
这是另一个循环学习率的训练图。注意分数 3 是如何持续下降的,即使损失在某个点后没有改善(视觉上)。在对超参数进行网格搜索后,对我来说,分析这组图是很有见地的。
分数确实有帮助,但是,在一个装满模型的文件夹中,很难知道哪个模型是最好的。这就把我们带到了对 Duel 和 DuelFolder 类的讨论。
决斗和决斗文件夹类
决斗班有两个训练有素的特工,他们打了几局,结果如下图所示。
结果来自 1000 集。看起来 51-11 大部分时间都会赢。
DuelFolder 以批量方式做同样的事情,将整个模型文件夹作为输入。两两对抗的代理,输出胜利计数他们每一个。
似乎以 51–30 结尾的模型是最好的。
这只是衡量学习过程的另一种形式。没有完美的模型可以和训练过的模型进行比较。然而,我们可以通过击退他们来相互比较。决斗训练模型允许我们比较在不同条件下训练的模型。
在大多数情况下,具有好看的损失和启发式度量图的模型表现最好。基于该比较选择最终模型。
在一个没有自动方法来衡量一个智能体相对于人类表现如何的环境中,衡量模型之间的相对表现是很有趣的。
AgentFull 类
在最开始,为了训练的简单,我们忽略了原始游戏有四轮的事实。每一轮在游戏底部都有不同的分数。这改变了交易的回报。因此,为一轮训练的代理对另一轮无效。
第 1 轮、第 2 轮、第 3 轮和第 4 轮的分数。
这使得有必要为每一轮训练单独的代理。然后 AgentFull 将这四个单独训练过的模型。每当要求给定状态的最佳动作时,它只是将推理过程转发给适当的代理。
游戏控制器类
这个类是 AgentFull 类在线运行的框架。它截取屏幕截图,使用 OpenCV 库检测对象。当轮到代理时,从游戏中读取状态并用鼠标点击适当的点。
行动模型!⚔️
以下是如何运行“运行游戏控制器”笔记本。你打开笔记本和游戏,在同一个屏幕上可见。一旦你运行细胞。start_playing()方法,游戏将被检测到,程序将开始每 0.5 秒抓取一个屏幕截图,与其他玩家进行游戏。请注意,这样做可能会让你被禁止游戏。
仅此而已!
希望你喜欢!😊
关于我
我是一名数据科学家,住在旧金山。热衷于从数据中寻找答案。在 Linkedin 上找到我:梅尔·萨卡里亚
用深度强化学习玩宝藏游戏—第 1/3 部分
艾玩拼图
这是代理在与其他玩家在线对战时“看到”的内容。
在这个系列文章中,我解释了我从事的强化学习(RL)项目。最后,你会知道这是怎么回事:
将强化学习应用于一个新奇的、尚未探索的问题的想法一直吸引着我。我有很多空闲时间在疫情冠状病毒中忙碌。另外,我年轻的时候也玩过这个游戏。这个项目只是在这段时间里的一个完美的侧面。
我讨论了问题的独特和值得注意的方面,我使用了什么技术以及为什么。希望对深度强化学习感兴趣的读者会发现所有这些都很有见地。
在第 1 部分(本部分),
通过缩小游戏桌的尺寸,开始着手解决原始问题的一个更小更简单的版本。这大大减少了状态空间,使问题更容易解决。除了这个小问题之外,还有一个相对简单的技术,适用于更小的状态空间:表格 Q-learning。这有助于建立对问题的直觉。这种直觉在后来用深度 Q 学习网络(DQN)解决问题时非常有用。
在第 2 部分中,
在获得了表格方法所需的验证和早期成功之后,使用 Keras 库实现了 DQN 来解决与第 1 部分相同的问题。然而,由于项目的性质,学习过程变得不稳定。从研究论文中借用了几种方法,并实施这些方法来缓解这个问题。即,优先经验重放、目标 Q 网络、具有学习率查找器的循环学习率。(第二部)
最后,在第 3 部分中,
跟踪学习进度和绘图是深度学习研究的关键。为了跟踪学习进度,解释了启发式度量,并绘制了情节迭代的 MSE 损失。经过训练的模型相互挑战,以了解哪个学习过程产生更好的代理。代理接受不同回合游戏(1、2、3 和 4)的培训,并被组织起来进行在线表演。解释了从计算机屏幕获取输入和执行动作的框架。接受来自屏幕的输入,点击适当的点,代理能够击败大多数玩家。 ( 第三部分)
1/3 部分的 GitHub 知识库:TD-deepreinforcementlearning-Part 1
在这一部分,
我们的问题域,宝藏掉落的游戏,是按照任何强化学习应用的五大要素来解释和细分的: 环境、智能体、状态、动作和奖励** 。接下来,学习算法被简单地提及并且给出了为什么它适合这个问题的原因。接下来,定义了收敛的启发式度量,我们的模型由这两个度量以及(有趣的部分)与之对抗来评估。
让我们从定义环境开始。
环境
每个玩家轮流把一枚硬币一次扔进棋盘的顶部。这个游戏的目标是使硬币从棋盘底部掉落到投币口。因此得名,宝降!积分值越大越好。关于游戏的更多描述:链接到官方网站
如果你对最终代理在网上与真实玩家的对战表现很好奇,可以随意看看下面的视频。为了更好地理解游戏,强烈建议观看,因为仅仅通过文字描述来解释游戏动态是很有挑战性的。
观察:代理人在“真实世界”中的表现
正如你可能在视频中看到的,这个游戏分四轮进行。每一轮,棋盘底部的分数都不一样。为了简化模型训练,忽略了轮次,它们不是模型的输入。对于给定的模型,只考虑第一轮(第一轮)。
你可能想知道,视频中的代理人在 4 轮游戏中表现如何?答案是,实际上有 4 个模型,每一个都被单独训练了不同的回合,不同的分数,用一个包装类放在一起。根据游戏回合的不同,只有一个被激活。
游戏机制是用 python 模拟的,以创建一个训练环境。TreasureDrop
类是游戏的抽象。下面是printState()
方法的控制台输出。另外,代理类的demo()
方法让你通过从控制台接受输入来对抗它。在这个项目的过程中,看到经纪人如何与我对抗是非常有用的。
demo()方法还打印预测的 q 值。由于熟悉这个游戏,我能够很快判断 q 值是否有意义。在下图中,“O”表示槽中有硬币的杠杆,而“V”表示没有硬币的杠杆。现在不要担心回合,这部分只考虑第一回合。想象他们不存在。
上面的板子是 4x5 的。纸板尺寸是指{顶行宽度} x {纸板高度}。在任何项目中从小处着手都是好的。针对较小的董事会规模培训代理更容易。此外,它还验证了这个问题可以通过强化学习技术来学习的假设。对于这一部分,我们将解决较小的电路板尺寸,例如:
为了应用一种成功率更高的技术,并验证我的假设,即这个游戏是可以学习的,首先使用表格的方法来解决这个问题。使用较小的尺寸是为了使问题可行。在原始(4x5)游戏表中有 2⁶⁰可能的状态。用 2⁶⁰条目构建一个表在计算上是不可行的。但是上面的尺寸还可以,
状态
这将我们的讨论带到了状态的定义。游戏有几个数据点可以包含在状态中,并且可以被模型感知。也就是说,回合、分数、轮到谁以及杠杆的位置。为了保持模型的简单性和训练过程的鲁棒性,只有集体杆状态被认为包括在状态中。杆状态是单个杆的状态,编码如下:
正如你在视频中可能注意到的,这正是模型对杠杆的感知。游戏的状态只是单个操纵杆状态的集合。每个杠杆状态从左到右、从上到下相加,顺序与书面英语相同。给定游戏桌的相应状态如下所示:
状态是模型的输入。它是通过连接杠杆状态组合在一起的。
行动、奖励和下一个状态
一个动作是指从游戏桌面的任何一个投币口投出一枚硬币。插槽的索引从 0 开始,向右移动时递增 1。根据所采取的动作,一个奖励和下一个状态将确定地跟随。奖励是采取行动后获得的总点数的总和。这是由硬币到达棋盘底部的分数决定的。
代理人
政策决定下一步采取什么行动。每个动作的 q 值将由基础模型决定。代理遵循贪婪策略,这意味着它将选择具有最高 q 值的动作。
强化学习是从互动中学习如何表现以达到目标。强化学习代理及其环境在整个训练过程中通过一系列离散的时间步骤进行交互。同样,我们的目标是训练一个能够做出好的决策序列的智能体。
这个决策过程被建模为马尔可夫决策过程(MDP)。在 MDPs 中,我们估计每个州 s 中每个行动 a 的值 Q(s,a)。q 值,𝑄(𝑠,𝑎)是预期贴现回报如果我们在𝑠州执行行动𝑎,那么从那时起遵循最优策略。这些状态和动作相关的量对于准确地将长期后果的信用分配给单个动作选择是至关重要的。
考虑上图。给定状态 s 和动作 0、1、2 和 3,我们的策略在 Q(s,0)、Q(s,1)、Q(s,2)和 Q(s,3)中挑选具有最高Q 值的动作。因此,强化学习的任务变成了寻找正确输出 Q 值的函数 Q。
为了实现这个功能,在这一部分中,我们将为每个(状态、动作)对建立一个包含条目(q 值)的表。这就是所谓的 Q 表。在下面的部分中,这个 Q 值函数将被近似。然而,表格和 DQN 实现中的学习算法将保持不变。
学习算法
如果一个人必须确定一个核心的和新颖的强化学习的想法,它无疑将是时间差 TD 学习。
—萨顿&巴尔托公司
选择称为 TD 学习的时间差学习作为学习算法。它的工作是在每个时间步更新 Q 值函数。这意味着每当代理采取行动时,它将更新 Q 表中的一些条目。
TD 方法优于其他强化学习方法的一个优点是,TD 方法可以直接从原始经验中学习,而不需要环境的动态模型。此外,TD 更新可以在每一步调用,这与蒙特卡罗(MC)方法形成对比,在蒙特卡罗方法中,算法要等到剧集结束时才进行更新。这使得 TD 以在线方式实现变得非常自然。这个项目中使用的方法称为一步 TD,即 TD(0)。一步式仅仅意味着更新方程只考虑一步奖励。
最初,所有 Q 值都被初始化为任意值。代理通过遵循ε-贪婪策略来玩游戏,在每次迭代中,代理丢硬币并获得奖励,在每次迭代中,Q 值函数根据贝尔曼方程更新:
贝尔曼方程是 Q 学习的核心。这也是在 TD(0)算法的每一步中更新表格的方式。
对于固定大小的策略,TD(0)被证明确定性地收敛到算法的表格实现的单个答案。
对贝尔曼方程的修正
由于问题的性质,需要对贝尔曼方程做一个小的改动。在一些流行和众所周知的强化学习问题中,如 gridworld 、月球着陆器和 cartpole ,下一个状态的动作也是由 agent 决定的。换句话说,下一个状态属于同一个代理。我们的问题是一个回合制游戏,下一个状态不属于代理。事实上,下一个状态其实是属于敌人的。
在登月过程中,最好去一个 q 值高的州。然而在我们的问题中,情况恰恰相反。代理人必须确保在收集最多奖励的同时,下一个状态必须总是尽可能低的值。重复一遍,(下一个状态,行动)的最大值对我们来说和奖励本身一样重要。这是一个零和游戏,获胜的策略是在获得奖励的同时限制敌人的奖励。
有经验的最终代理对在线对手。
代理使用用户名“Playwme”进行游戏。虽然并不总是如此,但它通常会避免对手采取这样的行动,因为它知道下一个状态可能会给敌人带来比短期奖励更多的分数。由于这种现象,特工必须确保敌人不会做出太有价值的行动,至少不会比我们得到的奖励更好。
这个关于游戏动力学的知识需要对 TD(0)算法使用的更新方程(贝尔曼方程)做一个小的改变,以适应这个问题。
由于这种特定于领域的现象,将属于折扣因子的加号以及最优未来值的估计值切换为负数是有意义的。在下面的修正贝尔曼方程中,它用红色表示:
用书面英语翻译这个变化,意思是:{状态值,动作} = {我得到的奖励}–{下一个状态的最大值,属于敌人的动作}
超参数
这里没有太多的超参数调整,因为这只是一个玩具例子。但是,提到探索率的影响是值得的。
探索率对主体在整个学习过程中遇到的状态分布有影响。在数据科学中,数据的底层分布决定了模型学习的内容。
如果探索率较低,(大约 0.05–0.1)代理人会比探索率较高的情况更频繁地暴露于竞争游戏中可能遇到的状态。相反,如果探索率是 1,代理随机地玩,并且当动作被随机地挑选时暴露于状态。就像人类一样,代理人将会更多地了解它所接触到的东西。
让我们假设有一个边缘状态,一个不经过深入探索就不可能达到的状态。如果勘探率较低,则不能很好地达到该状态,从而导致表中的 q 值不太准确。让我们假设有一个核心状态,这是一个在竞技游戏中经常遇到的重要状态,为了更多地暴露于该状态,探索率需要较低。代理越多地暴露于核心状态,它就越知道在竞争游戏中面对核心状态时如何表现。
无论探索率是 0.05 还是 1,Q-table 迟早会用正确的 Q 值填充,这取决于表的大小。边缘和核心状态被探索只是时间问题,取决于探索速度。TD(0)的表格实现保证收敛到正确的解。
模型验证
虽然不是最科学的,但验证模型最有趣的方式是和它玩。作为一种测试模型的自动化方法,没有像登月那样直接的方法,在登月中,累积奖励是有意义的。然而,我们有几种方法可以获得模型捕捉正确 q 值能力的“启发式”评分。其中之一是查看下面两个镜像电路板的 q 值。两个镜像板简单地意味着游戏桌被 y 轴颠倒。
注意到彼此对应的动作有非常接近的 Q 值吗?为什么这很重要?如果与另一张桌子上的等效动作相对应的动作的 Q 值非常接近,这意味着我们的模型可以看到这两个动作带来了相同的奖励并过渡到等效状态。值得注意的是,该模型能够捕捉到这一点,而无需我们提供游戏机制或让模型知道这两种状态是彼此沿 y 轴的镜像。
注意,分数可能不存在,更不用说接近了,如果状态是在竞争游戏期间不可能发生的。毕竟,我们在这里处理的是一个统计模型,没有魔法。如果代理在训练中没有遇到这种状态,它很可能不会对此有任何意见。
另一个启发是基于这样的事实,一些相邻的动作对转移到相同的下一个状态,并获得相同的回报。更具体地说,如果最上面一排的杠杆上有一枚硬币,那么向左和向右的动作对该杠杆产生相同的效果,因此对整个棋盘也产生相同的效果。
如上图所示,0 & 1 和 4 & 5 的 Q 值非常接近。这是因为简单地说,两个动作都会导致相同的回报和下一个状态,都是通过移动杠杆上的硬币。如果代理人发现了这一点,很可能就发现了整个游戏。
虽然动作 2 和动作 3 彼此相邻,但情况并非如此。因此,这两个动作共享一个非常接近的 q 学习是有意义的。同样,如果模型可以说这些动作的 q 值非常接近,这是一个强有力的指标,表明模型非常了解这个游戏。
⚔️行动模型
去查查笔记本吧。GitHub 资源库中有两个笔记本。一个用于从已经计算的 q 表中运行代理。另一方面,你可以从头开始训练一个代理,只需要一路运行细胞。
看你能不能打败它!
下一步是什么?
现在我们知道这个问题可以通过强化学习方法来解决。在第 2 部分中,我们将 DQN 的表格实现转换为适用于更大的问题。
表格法往往能找到精确解,即往往能精确找到最优值函数和最优策略。这与下一部分中描述的近似方法形成对比,后者只能找到近似解,但反过来可以有效地应用于更大的问题。
关于我
我是一名数据科学家,住在旧金山。热衷于从数据中寻找答案。在 Linkedin 上找到我:梅尔·萨卡里亚
参考
[2]:萨顿&巴尔托。 【强化学习】 (2018)
从流数据到新冠肺炎推特分析:使用 Spark 和 AWS Kinesis
摆弄社交媒体数据、大数据平台和代码的指南。
图片引自www.siasat.com
随着新冠肺炎的蔓延和确诊病例的飙升,世界正在关闭。曾经熙熙攘攘的地方已经变成了鬼城。相比之下,社交媒体比平时更吵。好消息、坏消息和假消息在推特、脸书和 Instagram 上流传。恐惧和安慰,错误的信息和澄清,可以很容易地在所有激烈的讨论中找到。社交媒体可能从未像现在这样发挥过如此重要的作用,让人们体验外部世界和自己。
对于数据科学界来说,是时候做出反应并跟踪新冠肺炎的全球影响了。在这篇文章中,我将展示一个研究社交媒体的简单方法——分析 Twitter。
概述
本文将讲述如何利用流行的大数据平台 Spark 和 AWS 构建数据管道,实现 Twitter 上的情绪和时间序列 分析。管道将把通过标签和关键词过滤的推文转换成流数据格式,这种格式可以在 Spark 和 Pandas 中操作。以接近实时处理的方式处理流数据。
数据管道的架构
如上图所示,该流程将经历以下步骤:
- 步骤 1 :使用 Tweepy 的 python 程序将在 EC2 实例上运行,获取用标签和关键字过滤的 tweets。数据将被发送到 Kinesis 数据流中。
- 第二步:kine sis 数据流以可管理、可扩展的方式消化数据。监控指标并检查数据是否进入 Kinesis。
- 步骤 3 :依靠Data bricks Community Edition提供的笔记本,应用程序将实现 Spark 结构化流和 Kinesis 数据流之间的连接。该应用程序使用 Spark SQL 和 T extBlob 构建了一个情感分析器。
- 步骤 4 :转换成 Pandas Dataframes ,使用 Matplotlib 用时间戳索引数据,并用时间序列可视化。
第 1 部分:捕获推文
要分析数据,首先需要有数据。 Tweepy 是一个强大的 python 库,用于捕捉实时推文。想象一下,捕获 tweets 的程序可能需要几个小时,这会消耗 CPU 和内存。在这种情况下,在远程机器 EC2 上运行它是个好主意。
下面附上了 tweet 捕获代码的一个片段。这里用的是 Tweepy 流。完整的代码上传到这里。
stream_name = '' # fill the name of Kinesis data stream you createdif __name__ == '__main__':
# create kinesis client connection
kinesis_client = boto3.client('kinesis',
region_name='', # enter the region
aws_access_key_id='', # fill your AWS access key id
aws_secret_access_key='') # fill you aws secret access key
# create instance of the tweepy tweet stream listener
listener = TweetStreamListener()
# set twitter keys/tokens
auth = OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
# create instance of the tweepy stream
stream = Stream(auth, listener)
记得用您的 AWS 和 twitter 凭证填写缺失的字段。
第 2 部分:使用 Kinesis 数据流处理流数据
如果我们开始运行代码,数据就会像水一样源源不断地被提取出来。它需要通过软管或管道,或者需要储存。AWS Kinesis 数据流(KDS)就是这样一个软管,提供弹性缩放、实时度量和数据分析等功能。
创建 KDS 非常简单。进入 AWS Kinesis 服务页面,选择创建数据流,填写数据流名称。至于碎片的数量,因为这只是一个演示,1 个碎片应该没问题。
创建 Kinesis 数据流的页面
一旦创建了 KDS,我们就可以运行第 1 部分中的 tweet 捕获代码。在这个演示中,捕获的推文应该包含标签“#新冠肺炎”和关键字“加拿大”。
运行 tweet 捕获代码
等待几分钟后,从 Kinesis 数据流的 monitoring 选项卡中,我们可以看到 tweet 流的指标。
反映在 Kinesis 数据流上的指标
第 3 部分:使用 Spark 结构化流接收数据
Spark 是一个流行的集群计算框架,用来处理大数据问题。自 2014 年诞生以来,Spark 一直在不断发展,经历了很多变化。某些变化是关键的,可能会令人困惑,所以我会解释一下。
引自 javatpoint.com
【Spark】弹性分布式数据集(RDD) 是跨集群节点划分的、可以并行操作的元素集合。rdd 是 Spark 中主要的逻辑数据单元。基于 RDD, Spark Streaming 构建为可扩展的容错流式处理系统,本机支持批处理和流式工作负载。
从 Spark 2.x 开始引入了变化。 数据帧 作为 RDD 之上的抽象发布,随后是数据集。在 Spark 1.x 中,RDD 是主要的 API,但是从 Spark 2.x 开始,鼓励使用 DataFrame API。为了支持数据帧和数据集, Spark 结构化流 应运而生,提供了比 Spark 流 API 更高级的接口。
位于链接的幻灯片
Spark SQL 允许用户操作数据帧。从名字我们就能猜到 Spark SQL 提供了 SQL 语言支持,挺像 Hive 的。
现在我们已经完成了理论,是时候动手了。Spark 结构化流的编程范例如下:
- 导入必要的类并创建一个本地 SparkSession
- 创建火花流数据帧
- 在数据帧上进行操作
更多内容请参考如何使用 Spark 结构化流进行编程。
Databrick 社区版是一站式大数据平台。用户可以像在 Jupyter 笔记本中一样编写 Python 代码,但是能够与 Spark 集群流畅地交互。此外,Databricks 社区版还提供了有用的功能,如 混合语言 和 可视化 。
首先,我们需要创建一个 spark 会话:
spark = SparkSession.builder\
.master("local")\
.appName("Structured Streaming")\
.getOrCreate()
然后从 Kinesis 数据流中创建火花流数据帧:
pythonSchema = StructType() \
.add("id", StringType(), True) \
.add("tweet", StringType(), True) \
.add("ts", StringType(), True)awsAccessKeyId = "" # update the access key
awsSecretKey = "" # update the secret key
kinesisStreamName = "" # update the kinesis stream name
kinesisRegion = ""kinesisDF = spark \
.readStream \
.format("kinesis") \
.option("streamName", kinesisStreamName)\
.option("region", kinesisRegion) \
.option("initialPosition", "LATEST") \
.option("format", "json") \
.option("awsAccessKey", awsAccessKeyId)\
.option("awsSecretKey", awsSecretKey) \
.option("inferSchema", "true") \
.load()
通过在 EC2 和 Kinesis 数据流上运行 tweet 捕获程序,我们可以看到来自 Kinesis 数据流的数据是如何进入 Spark 流数据帧的:
df = kinesisDF \
.writeStream \
.format("memory") \
.outputMode("append") \
.queryName("tweets") \
.start()
流式数据正在进入 Spark
df 是一个流查询来处理主动流查询。 format("memory") 表示输出将作为内存表存储在内存中。输出模式指定写入输出接收器的内容,这里我们选择追加模式,这意味着只向结果表添加新行。
一旦开始流式传输,就可以通过控制面板监控传入的数据。通过检查 df 的状态,我们知道数据是可用的,并且我们可以开始探索数据。
对查询应用 SQL 命令:
tweets = spark.sql("select cast(data as string) from tweets")
查看查询的标题数据:
浏览数据帧中的前 5 行数据
第 4 部分:使用 Spark 数据框架进行情感分析
为了分析数据,它需要更加结构化。所有返回 tweets 的 Twitter APIs 都提供了使用 JavaScript Object Notation (JSON)编码的数据。让我们简单看一下一个例子:
{
"created_at": "Thu May 10 15:24:15 +0000 2018",
"id_str": "850006245121695744",
"text": "Here is the Tweet message.",
"user": {
...
},
"place": {},
"entities": {},
"extended_entities": {
...
}
}
当 tweet 捕获代码运行时,我们已经指定了所需的字段:“id”表示为 tweet id,“text”表示为 tweet 内容,“ts”表示为时间戳。
# tweet capture code running in EC2def on_data(self, data):
# decode json
tweet = json.loads(data)
# print(tweet)
if "text" in tweet.keys():
payload = {'id': str(tweet['id']),
'tweet': str(tweet['text'].encode('utf8', 'replace')),
'ts': str(tweet['created_at']),
},
print(payload)
try:
put_response = kinesis_client.put_record(
StreamName=stream_name,
Data=json.dumps(payload),
PartitionKey=str(tweet['user']['screen_name']))
...
所以我们收集的数据已经组织好了。使用 UDF ,我们可以将这些字段添加到 Spark 数据帧中。
def parse_tweet(text):
data = json.loads(text)
id = data[0]['id']
ts = data[0]['ts']
tweet = data[0]['tweet']
return (id, ts, tweet)
# Define your function
getID = UserDefinedFunction(lambda x: parse_tweet(x)[0], StringType())
getTs = UserDefinedFunction(lambda x: parse_tweet(x)[1], StringType())
getTweet = UserDefinedFunction(lambda x: parse_tweet(x)[2], StringType())# Apply the UDF using withColumn
tweets = (tweets.withColumn('id', getID(col("data")))
.withColumn('ts', getTs(col("data")))
.withColumn('tweet', getTweet(col("data")))
)
添加字段后浏览数据框架
现在我们对它进行情感分析。情感分析是自然语言处理( NLP )的一部分,对文本中的情感进行解释和分类。有许多学术和工业作品涉及这一领域,大多数 NLP 图书馆都有能力做到这一点。仅出于演示目的,我们使用 TextBlob 来执行这项工作。
情感分析的基本任务是将给定文本的极性分类,分为负面、中性和正面。极性数是一个位于[-1,1]范围内的浮点数。1 表示肯定的陈述,而-1 表示否定的陈述。因此,我们可以将数据分类如下:
import textblobdef get_sentiment(text):
from textblob import TextBlob
tweet = TextBlob(text)
if tweet.sentiment.polarity < 0:
sentiment = "negative"
elif tweet.sentiment.polarity == 0:
sentiment = "neutral"
else:
sentiment = "positive"
return sentiment
现在我们有了情感信息的领域,我们可以利用它对推文进行高层次的洞察。在这里,我们希望看到这些捕获的推文的情绪分类,猜测哪些类型的情绪被揭示。利用 Databricks Community Edition 平台上的 混合语言 和 可视化 的特性,实现了关于情感与推文数量关系的情感分析。
%sql
select sentiment, count(*) as cnt from tweets_parsed group by sentiment
用条形图表示的情感分析
第 5 部分:使用 Numpy,Pandas,Matplotlib 进行时间序列分析
以上分析基于PySpark data frames。但是 Numpy/Pandas/Matplotlib 的组合更专业的对数据做操作。因此,我们需要将 PySpark 数据帧转换成帕纳斯数据帧。幸运的是,这并不难。
tweets_pdf = tweets.toPandas()
现在,我们可以使用 Pandas 的丰富功能来探索数据:
要进行时间序列分析,最佳实践是用时间单位索引数据。这里我们需要将字段 ts 转换成 datatime 类型,然后用它来索引数据帧。
pd.to_datetime(tweets_pdf['ts'])
idx = pd.DatetimeIndex(pd.to_datetime(tweets_pdf['ts']))
检查 ts 字段是否被正确解析为时间戳索引
下面的代码是对数据进行操作,然后使用 Matplotlib 将其可视化成时间序列折线图。该图表描述了包含标签“#新冠肺炎”和关键词“加拿大”的推文数量如何随时间变化。
# Plotting the series
%matplotlib inlinefig, ax = plt.subplots()
ax.grid(True)ax.set_title("Tweet Numbers")
interval = mdates.MinuteLocator(interval=10)
date_formatter = mdates.DateFormatter('%H:%M')datemin = datetime(2020, 3, 28, 16, 00)
datemax = datetime(2020, 3, 28, 17, 10)ax.xaxis.set_major_locator(interval)
ax.xaxis.set_major_formatter(date_formatter)
ax.set_xlim(datemin, datemax)
max_freq = per_minute.max()
min_freq = per_minute.min()
ax.set_ylim(min_freq-100, max_freq+100)
ax.plot(per_minute.index, per_minute)display(fig)
x 轴:推文发布时间,y 轴:推文数量
结论
在这项工作中,我们经历了从建立数据管道到进行有意义的分析的过程。但这只是挖掘社交媒体的一小步。在未来,我将继续探索并找出其他方法来洞察大数据。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
请停止在熊猫身上做这 5 件事
这些错误非常普遍,也非常容易纠正。
作为一个在进入数据科学之前从事了十多年开发工作的人,我看到数据科学家在使用熊猫时犯了很多错误。好消息是这些真的很容易避免,修复它们也能让你的代码更可读。
丹妮拉·霍尔泽在 Unsplash 上的照片
错误 1:缓慢地获取或设置值
这不是任何人的错,熊猫有太多的方式来获得和设定价值。在某些情况下,您必须只使用索引来查找值,或者只使用值来查找索引。然而,在许多情况下,您将有许多不同的方法来选择数据:索引、值、标签等。
在这种情况下,我更喜欢用最快的。以下是一些常见的选择,从最慢到最快,这表明你可能会错过 195%的收益!
使用 20,000 行的数据框架进行测试。如果你想自己运行,这是笔记本。
**# .at - 22.3 seconds**
for i in range(df_size):
df.at[i] = profile
Wall time: **22.3 s****# .iloc** - **15% faster than .at**
for i in range(df_size):
df.iloc[i] = profile
Wall time: **19.1 s****# .loc** - **30% faster than .at**
for i in range(df_size):
df.loc[i] = profile
Wall time: **16.5 s****# .iat**, doesn't work for replacing multiple columns of data.
# Fast but isn't comparable since I'm only replacing one column.
for i in range(df_size):
df.iloc[i].iat[0] = profile['address']
Wall time: **3.46 s****# .values / .to_numpy()** - **195% faster than .at** for i in range(df_size):
df.values[i] = profile
# Recommend using to_numpy() instead if you have Pandas 1.0+
# df.to_numpy()[i] = profile
Wall time: **254 ms**
(如Alex Bruening和miraculixx注释中指出,对于循环来说并不是执行这种动作的理想方式, 请看。敷()敷。我在这里用它们,纯粹是为了证明,回路内部,线路的速度差。)
错误 2:只使用了 25%的 CPU
无论你是在服务器上还是在笔记本电脑上,绝大多数人都不会使用他们所有的计算能力。如今大多数处理器(CPU)都有 4 个内核,默认情况下,熊猫只会使用一个。
从摩丁文档来看,4 核机器上的 4 倍加速。
Modin 是一个 Python 模块,通过更好地利用硬件来增强 Pandas。摩丁数据帧不需要任何额外的代码,在大多数情况下,它会将你对数据帧所做的一切加速 3 倍或更多。
摩丁更像是一个插件,而不是一个库,因为它使用熊猫作为后备,不能单独使用。
摩丁的目标是悄悄地扩充熊猫,让你不用学习新的库就能继续工作。大多数人需要的唯一一行代码是用import modin.pandas as pd
替换普通的import pandas as pd
,但是如果你想了解更多,请查看这里的文档。
为了避免重复已经做过的测试,我在 Modin 文档中添加了这张图片,展示了它可以在标准笔记本电脑上加速read_csv()
功能的程度。
请注意,Modin 正在开发中,虽然我在生产中使用它,但您应该会遇到一些错误。查看 GitHub 中的 问题和 支持的 API了解更多信息。
错误 3:让熊猫猜测数据类型
当您将数据导入到数据帧中,并且没有明确地告诉 Pandas 列和数据类型时,Pandas 会将整个数据集读入内存,以确定数据类型。
例如,如果您有一个充满文本的列,熊猫将读取每个值,看到它们都是字符串,并将该列的数据类型设置为“字符串”。然后对所有其他列重复这个过程。
您可以使用df.info()
来查看一个数据帧使用了多少内存,这与 Pandas 计算每一列的数据类型所消耗的内存量大致相同。
除非您在处理微小的数据集或者您的列经常变化,否则您应该总是指定数据类型。为此,只需添加dtypes
参数和一个字典,将列名及其数据类型作为字符串。例如:
pd.read_csv(‘fake_profiles.csv’, dtype={
‘job’: ‘str’,
‘company’: ‘str’,
‘ssn’: ‘str’
})
注意:这也适用于不是来自 CSV 的数据帧。
错误 4:剩余的数据帧
数据帧的一个最好的特点是它们很容易创建和更改。不幸的是,这样做的副作用是大多数人最终会得到这样的代码:
# Change dataframe 1 and save it into a new dataframedf1 = pd.read_csv(‘file.csv’)df2 = df1.dropna()df3 = df2.groupby(‘thing’)
所发生的是你将df2
和df1
留在 Python 内存中,即使你已经转移到了df3
。不要在内存中留下多余的数据帧,如果你用的是笔记本电脑,它几乎会影响你做的所有事情的性能。如果你在一个服务器上,它会损害该服务器上其他人的性能(或者在某个时候,你会得到一个“内存不足”的错误)。
相反,这里有一些简单的方法来保持你的记忆干净:
- 使用
df.info()
查看数据帧使用了多少内存 - 在 Jupyter 中安装插件支持,然后为 Jupyter 安装变量检查器插件。如果你习惯在 R-Studio 中有一个变量检查器,你应该知道 R-Studio 现在支持 Python !
- 如果你已经在 Jupyter 会话中,你可以通过使用
del df2
删除变量而无需重启 - 在一行中将多个数据帧修改链接在一起(只要它不会使你的代码不可读):
df = df.apply(thing1).dropna()
- 正如Roberto Bruno Martins所指出的,另一种保证干净内存的方法是 执行函数 内的操作。您仍然可以这样无意中滥用内存,解释范围超出了本文的范围,但是如果您不熟悉,我鼓励您阅读这篇文章。
错误 5:手动配置 Matplotlib
这可能是最常见的错误,但它排在第五位,因为它影响最小。我甚至在经验丰富的专业人士的教程和博客帖子中看到这种错误。
Matplotlib 是由 Pandas 自动导入的,它甚至在每个数据帧上为您设置了一些图表配置。
没有必要为每个图表导入和配置它,因为它已经为您烤成熊猫了。
这里有一个错误做法的例子,尽管这是一个基本图表,但仍然是浪费代码:
import matplotlib.pyplot as plt
ax.hist(x=df[‘x’])
ax.set_xlabel(‘label for column X’)
plt.show()
正确的方法是:
df[‘x’].plot()
更容易,对吗?你可以对这些数据帧绘图对象做任何你可以对任何其他 Matplotlib 绘图对象做的事情。例如:
df[‘x’].plot.hist(title=’Chart title’)
我确信我正在犯我不知道的其他错误,但是希望与您分享这些已知的错误将有助于更好地使用您的硬件,让您编写更少的代码,完成更多的工作!
如果你还在寻找更多的优化,你一定会想读:
我不知道没有他们我是如何生活的
towardsdatascience.com](/the-3-secret-weapons-that-changed-my-python-editor-forever-c99f7b2e0084)
请停止给数据科学的候选人布置带回家的作业
要求候选人完成带回家的作业既不公平又低效。幸运的是,替代方法是存在的。
为您的团队找到完美的数据科学家或分析师是一项艰巨的任务。你可能会收到数百份候选人的申请,他们都列出了相同的关键词。筛选他们并不容易,为此,(太)许多公司给他们的一些顶级候选人分配带回家的数据科学任务,以模拟工作环境并评估候选人的价值。
候选人通常会收到一份与公司通常处理的数据集(或实际数据集)相似的数据集,并应发回一份报告或笔记本,说明他们是如何分析的以及他们发现了什么。
如果你正在这样做,请停止。🙏
候选人的时间是宝贵的
应聘者在申请贵公司时已经付出了很多努力。他们浏览了招聘信息,研究了你的公司,修改了他们的简历,写了一封个性化的求职信,并反复查看收件箱寻找答案。另外,他们也可能去其他公司面试。
他们可能会通过电话筛选过程,潜在的技术测试或筛选,然后在周末完成你的任务。如果他们足够幸运,他们还会和你的首席技术官、首席执行官或部门主管交谈。
我相信你的本意是好的,希望筛选出真正有积极性的候选人,但是从候选人的角度来看,在一份申请上投入 10 个小时以上的工作可能会被视为滥用,他们可能会开始质疑,在一家公司工作,甚至在开始与他们合作之前就要求如此多的努力,这是怎么回事——免费的。你可能会把时间限制在几个小时,但这是行不通的,因为它不能被强制执行,投入更多的时间会被视为候选人的竞争优势。
信不信由你,数据科学家通常对他们的周末有比做你的带回家的作业更好的计划。照片由 Magnet.me 在 Unsplash 上拍摄
准备错过最佳候选人
让我们现实一点。顶级数据科学候选人可以轻松获得多家公司的面试和工作机会。如果他们可以在几周内进入 5 或 6 个面试程序,为什么他们要花一部分周末的时间来完成你的任务。错过一个潜在的机会对他们来说很少是个问题。
因此,除非你的公司提供非常有吸引力的东西或有很强的正面形象,否则一些高调的候选人可能会拒绝带回家的任务,而他们可以参与不那么耗时的选择过程。
你的时间也很宝贵
值得注意的是,这个过程不一定会减少你的时间消耗。
你可以选择快速浏览他们的作品,但这是对资源的浪费:候选人的时间和他们为你提供的评估材料。如果你只花几分钟阅读他们的作品,你还不如问他们一些更简单的问题,比如谈论他们的一个项目。
另一种方法是仔细阅读每份报告。尽管这种方式更尊重他们的工作,但你最终会花更多的时间来评估候选人,但仍会错过他们性格和工作流程的一些重要方面。
“如何”和“什么”一样重要
对于数据分析来说,书面报告的确是一个重要的可交付成果,但它只显示了最终结果,你会错过导致最终结果的所有过程。这个过程很重要。
有太多的路线可以到达同一个最终目的地,候选人会有不同的方法来完成你的任务。有些人会超级高效,有些人会使用过于复杂的方法,有些人会使用他们几乎不理解的算法。这些信息和他们能提供的一样有价值。你刚刚错过了。
存在替代方案
幸运的是,有一些替代方法可以更全面地评估候选人的技能,同时尊重他们的时间。这里有一些例子。
付费试用
了解一个人的最好方法是和他一起工作。选择你的最佳候选人,给他们提供一个短期(例如一两周)项目合作的机会,并支付报酬(通常报酬较低但还不错)。你有一个独特的机会看到这个人是如何工作的,例如,他们如何定义一个项目,他们如何在团队中合作,他们交付了什么,以及他们如何传达他们的结果。
在这个过程的最后,你可能会对自己是否愿意继续与这个人共事有一个相当好的理解。在补偿应聘者时间的同时,你也为公司做出了一些有用的东西。
候选人也会很好地了解如何与你合作,如果他们喜欢他们所看到的,他们会更有信心加入你。他们还会喜欢别人对他们实际技能的评价,并会对整个技能评估过程更加欣赏。总之,您的员工保留率将受到积极影响。
例如,自动机器人正在做这件事。
实时编码会话
如果你没有准备好付费试用,仍然有比任务更好的方法来了解候选人如何工作,现场编码会议就是其中之一。
在现场编码会议期间,您向候选人发送一个数据集和一个要解决的给定问题,他们将有固定的时间(通常为一小时)来解决问题,同时分享他们的屏幕并解释他们的思维过程。他们可以使用本地环境或托管的 Jupyter 笔记本系统,如 Colab 。
现场编码会议是评估以下内容的有效方法:
- 候选人的编码技能。他们是否纠结于简单的概念,表现出缺乏经验?他们的代码看起来干净吗?他们使用了最佳实践吗?如果该职位要求编写生产就绪的代码,这样的面试可以提供很多价值。
- 他们的思维过程。如前所述,有多种方法可以获得类似的结果,当候选人向您介绍他们的方法和对数据的探索时,您可以理解他们为什么选择一种方法而不是另一种方法,他们做出的假设是什么,以及他们是否测试过这种方法……如果您对他们的方法有疑问,您可以直接询问他们,而不必仅根据最终的交付件进行猜测。
- 他们的交流。在任何时候,你都可以询问关于他们技术或方法选择的一些细节,你将能够评估他们对问题和所选方法的理解,以及他们就此进行清晰沟通的能力。
从候选人的角度来看,这似乎是一种更公平的评估方式,因为它涵盖了更多的最终报告,并且不会比正常的面试花费更长的时间。但是,说实话,这也更有压力,你可能会无意识地选择那些在这种情况下表现不好的特定候选人。
项目驱动的面试
对于一些职位来说,编写干净和优化的代码并不是首要任务,因为重点将更多地放在使用正确假设下最相关的方法,并快速交付可操作的结果。对于与运营、战略或研究联系更紧密的职位来说,情况就是如此。或者,更一般地说,角色更倾向于业务方面而不是工程方面。
由于编码能力不是问题,你可以利用这个机会更深入地讨论方法、项目框架和结果的解释。
为此,一个好方法是让候选人谈论他们最近的一个项目(以及如何选择的一些潜在标准)。问他们:
- 他们如何定义这个项目?他们单独定义的吗?他们与利益相关者沟通了多少?他们的流程是什么,以确保最终结果符合利益相关者的最初期望?
- 他们选择了哪种方法?这更多地是关于技术,例如,他们尝试了哪种算法、哪种技术堆栈以及为什么?
- 为什么他们更喜欢这种方法而不是另一种方法?他们选择一种算法仅仅是因为这是他们唯一知道的算法,还是他们有一些强有力的论据来支持他们的决定?潜在地询问他们为什么不选择另一种特定的算法(根据情况,这可能是更好或更坏的选择)。
- 他们选择的算法是如何工作的?如果他们使用随机森林,询问他们该算法在细节方面如何工作,以及它与其他基于树的方法相比如何。这允许发现候选人使用他们几乎不懂的算法。
- 这种方法的局限性是什么?这是非常重要的一点,因为你必须明白,例如,如果你的方法将在一个稍微不同的问题上工作,并且这是起作用的主要假设。
- 最终的结果是什么,他们是如何与利益相关者沟通的?
这种方法通常对候选人来说压力较小,因为他们大多需要讨论他们已经做过的事情。你可以把他们推出舒适区,在他们前进的过程中询问更多的技术细节。
准备好停止分配任务了吗?
带回家的作业要求应聘者投入不公平的时间,最终会损害你作为雇主的形象。它们没有提供评估候选人技能的最佳方法。他们当然允许筛选掉没有动机的候选人,但拥有许多公开机会的顶级候选人也会避免那些要求苛刻的过程。
但是,好消息!有一些面试选择至少同样有效,并提供更好的候选人体验。如果你仍然使用带回家的任务,你现在没有借口不选择更好的方法,如付费试用、现场编码会议或项目驱动的面试。
用 Python 和 Matplotlib 一步一步地绘图
从基础到更高级的特性,掌握 Matplotlib 的指南…以我希望被解释的方式。
Paola Galimberti 在 Unsplash 上拍摄的照片
Matplotlib 是 Python 中最流行的图形库。例如,如果我们在 StackOverflow 中查找,我们可以看到有大约 47k 个问题标记有 Matoplotlib,而如果我们查找其他类似的库,如 Seaborn 或 Plotly,我们分别得到大约 4k 和大约 7k。
因此,如果你开始使用 Python 绘图,我强烈建议你使用 Matplotlib,不仅是因为受欢迎的原因,还因为 Seaborn 是建立在 Matplotlib 之上的,所以如果你理解如何在 Matplotlib 中绘图,那么你将很容易理解如何处理 Seaborn(反之则不同)。
如果你已经使用 Matplotlib,我也鼓励你留在这里,因为你可能会发现关于用 Python 绘图的新东西。
所以在这篇文章中,我想告诉你如何在 Matplotlib 中绘图,从简单的绘图到更复杂的东西,给你必要的工具去发现如何自己绘制新的东西。
你不需要下载任何数据集。我们将在文章中创建必要的内容,这样你就可以复制、粘贴并自己尝试,比如在 Juptyer 笔记本中。
先睹为快,我们将看到从第一部分的第一个基本图到第二部分末尾的直方图所需的所有工具。
第一基本情节
直方图—第二部分结束
本文的结构
第一部分:基础知识
- 导入必要的包
- 更改地块大小
- 从第一个基本情节开始
- 更改线条样式、标记和宽度
- 散点图
- 条形图
- 将几块地合二为一
- 修改轴
- 添加网格和图例
- 关于修改轴的更多信息
第二部分:添加额外功能
- 在绘图顶部添加值
- 在绘图顶部添加线条
- 直方图
- 奖励:双斧
让我们现在就开始吧!
第一部分:基础知识
导入必要的包
首先,我们必须导入 Matplotlib 包,并内联运行神奇的函数%matplotlib。这个魔法功能就是让情节出现在你的 Jupyter 笔记本上的那个。
import matplotlib.pyplot as plt
%matplotlib inline
例如,Matplotlib 预安装在 Anaconda 发行版中,但是如果前面的命令因为您没有安装 Matplotlib 包而失败,您可以在您的终端中运行以下命令:
python -m pip install -U pip
python -m pip install -U matplotlib
此外,为了遵循我们将经历的所有示例,您必须执行以下基本导入:
import pandas as pd
import numpy as np
地块大小
如果我们想改变地块大小输出,我们需要改变从属性。首先,我们可以看到当前的属性是什么:
# get current size
fig_size = plt.rcParams["figure.figsize"]
print ("Current size:", fig_size)# let's make the plots a bit bigger than the default
# set figure width to 14 and height to 6fig_size[0] = 14
fig_size[1] = 6plt.rcParams["figure.figsize"] = fig_sizeprint ("Current size:", fig_size)
第一基本情节
正如我们所说的,我们将生成自己的数据集来工作。
# datasets x and y are lists, but they can also be, for instance, numpy arrays or pd.Series.x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]# plot
plt.plot(x, y)
由此,我们得到了简单的情节:
情节 1
对您正在处理的数据进行快速检查是很有用的,但是,如果您要将它放入演示文稿中,这并不能真正提供信息。我们继续,看看怎么给它上类固醇。
更改颜色、线条样式和标记
假设我们希望线条变粗变短,标记变圆变大,颜色为绿色。然后,我们在 x 和 y 之后加上你能看到的五个参数。
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]plt.plot(x, y, color='green' , marker='o', markersize=20, linestyle='--', linewidth=4)
现在我们得到了以下结果:
情节 2
这些只是您可以修改的一些参数,但是您可以在 Matplotlib 文档中找到完整的列表(我将在文章底部添加链接)。
散点图
如果您正在绘制一些离散数据,您可能只想绘制点(或者更一般地说,标记)。然后,您可以使用 plt.scatter,它的工作方式几乎与 plt.plot 相同,例如:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]# specifying the type of marker (dots) and its sizes
plt.scatter(x, y, marker='o', size=150)
给予的结果:
情节 3
条形图
如果我们想将数据绘制成柱状图,我们也可以这样做:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]# specifying the bar colors
plt.bar(x, y, color=’green’)
得到如下图:
情节 4
将几块地合二为一
值得指出的是,我们可以将这三者结合起来,这样做:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]plt.plot(x, y, color=’blue’, lw=4, alpha=0.5)plt.scatter(x, y, marker=’o’, s=400, color=’red’,alpha=0.5)plt.bar(x, y, color=’green’,alpha=0.5)
给予的结果:
地块 5
你可能想知道那个新变量 alpha 是什么:它是一个代表透明度水平的参数。alpha=0 是完全透明的,alpha=1 就好像我们没有设置任何 alpha 参数一样(完全不透明)。正是因为这种透明度,你可以看到横条后面的红点,横条上面的蓝线也是如此。你可以用 alpha=1 做同样的尝试,你会看到不同之处!
修改轴
之前的情节很好,但我们还有很多可以做的,特别是如果我们想让情节更容易理解或用于演示。也许我们想设置一些限制,或者从中扩展坐标轴。因此,让我们运行以下命令并检查结果:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]plt.plot(x, y)# here we modify the axes, specifying min and max for x and y axes.
plt.axis(xmin=-1, xmax=10, ymin=0, ymax=40)
地块 6
那是一个简单的方法。但是如果你想指定什么在 x 轴上或者在 y 轴上呢?例如,我们可以看到 x 轴上的数字是 2 乘 2。我们可以用 x 轴或 y 轴来改变它:
plt.plot(x, y)
plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)plt.xticks(np.arange(0,13,1))
情节 7
添加网格和图例
有时候我们会有点迷失在剧情中,我们需要一个网格。那很简单!我们必须像这样添加 plt.grid 方法:
plt.plot(x, y)
plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)
plt.xticks(np.arange(0,13,1))plt.grid(True)
地块 8
添加到情节中的另一个有用的功能是添加图例,特别是如果您计划添加到演示文稿中以交流发现,并且您希望观众尽快理解情节。这很简单,是这样的:
plt.plot(x, y,**label='Nice Blue Line'**)
plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)
plt.grid(True)plt.legend(loc='upper right',prop={'size':24})
情节 9
在上面的 plt.legend()示例中,我从中选择了大小和在图中的位置(右上)。
关于修改轴的更多信息
现在让我们回到坐标轴,让我们一次修改一些东西:
- 更改 x 刻度名称(在本例中是日期)
- 旋转轴
- 从轴中的 x 刻度和 y 刻度开始增加大小
- 给轴添加标签(用 labelpad 选择字体大小和与轴的间距)
- 给情节添加标题
plt.plot(x, y,label='Nice Blue Line')
plt.axis(xmin=0, xmax=6, ymin=0, ymax=40)# here we change the x-ticks
plt.xticks(x, ('2019-02-01', '2019-02-02', '2019-02-03','2019-02-04', '2019-02-05'))# here we rotate the x-ticks
plt.xticks(rotation=45)# here we change the size from both axes ticks
plt.tick_params(axis='both', labelsize = 16)# here we add the axes labels
plt.ylabel('Y Label', size=16, labelpad=20)
plt.xlabel('X Label', size=16, labelpad=20)# here we add the title
plt.title('Plot Title', size=18)# here we add the grid
plt.grid(True)
地块 10
当 x 轴上有日期时,如本例所示,或者一个值可以超过前一个值和下一个值的较大数字时,旋转标签特别有用。
现在,我们已经使我们的情节整洁,并有明确的参考,让我们继续第二部分。
第二部分:添加额外功能
这第二部分是关于增加额外的功能到情节本身。
我将向您展示如何添加指示图中值的数字,如何添加线条,最后作为奖励,在直方图中显示所有这些的示例。
在绘图顶部添加值
# we use the same data points as before
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]# we plot the data points
plt.plot(x, y)
plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)
plt.title('Plot Title', size=18)# here we add the value in one data point
plt.annotate(str(25),xy=(1,25+1), fontsize=14)
地块 11
这就是我们想要的。
在 plot.annotate 方法中,我们将想要显示的字符串作为第一个参数传递(在本例中是字符串形状的值 25 ),然后是坐标❌1 和 y:25+1(我们添加了+1,这样数字就不会与线条重叠)。
这是没有用的,但是,如果我们有很多数据点,我们想在曲线的顶部绘制所有的值。让我们从另一个角度来看,为了自动计算所有的值:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]# we plot the data points
plt.plot(x, y)
plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)
plt.title('Plot Title', size=18)# here we add all the values
# we also specify font size and colorfor i,j in zip(x,y):
inplot = plt.annotate(str(j)
,xy=(i,j+1)
,fontsize=12
,color='black')
结果,得到了这个图:
地块 12
如你所见,我们为循环做了一个来绘制所有的值。
我们使用了 zip 函数,这是一个非常有用的函数,它允许我们在每个循环中获取多个变量。
添加线条
当您想要突出显示图中的一个值时,这是很方便的。如果要突出显示 x 轴值,可以使用垂直线;如果要突出显示 y 轴值,可以使用水平线。出于本例的目的,让我们同时使用这两种方法:
x = [1, 2, 3, 4, 5]
y = [25, 32, 34, 20, 25]plt.plot(x, y)
plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)
plt.title('Plot Title', size=18)# here we add the vertical line
plt.axvline(x[1], color='red',linestyle='--',linewidth=1)# here we add the horizontal line
plt.axhline(y[1], color='orange',linestyle='--',linewidth=1)
第 13 号地块
直方图(并将所有数据放在一起)
正如在第二部分开始时所承诺的,我们将创建一个直方图。我们将添加一条累积曲线,我们将使用之前解释的所有额外特征,显示所有这些特征如何向图中添加有价值的信息。
我们的目标是从一个非常简单的直方图开始,比如这个:
为了(剧透警报)这一个:
或者这一个(具有双轴,以便曲线和条具有相同的高度):
我要用下面的代码生成一个玩具数据集。您可以将其复制并粘贴到您的 Jupyter 笔记本中,继续学习本示例。我的目的是向您展示情节,而不是数据集的创建,因此,我不打算解释以下代码的细节。但是,如果你对此有任何疑问,请在评论中告诉我。
data = np.random.chisquare(2,5000)r = range(12)df = pd.DataFrame(data)values = np.histogram(df, bins=r)[0]bins = np.histogram(df, bins=r)[1].astype(int)df = pd.DataFrame({'bins_left': bins[:-1],'bins_right': bins[1:], 'values': values})df['values_norm'] = df['values'] / df['values'].sum()df['labels'] = df.apply(lambda x: str(int(x['bins_left'])) + '-' + str(int(x['bins_right'])),axis=1)df['cumulative'] = df['values'].cumsum()df['perc_cumulative'] = df['cumulative']/df.cumulative.max()df['perc_cum_label'] = df.apply(lambda x: str(round(x['perc_cumulative']*100,1)) + '%',axis=1)df = df[['labels','values','values_norm','cumulative','perc_cumulative','perc_cum_label']]
最后,我们有了这个数据框架:
df —刚刚生成的数据帧
因此,在这个数据数组中,我们有 5000 个值,为了这个例子,让我们说它们代表来自 5000 只兔子的社区的重量。因此,我们有 1935 只体重在 0 到 1 公斤之间的兔子,其中 1232 只体重在 1 到 2 公斤之间,只有 15 只体重在 10 到 11 公斤之间。因为它们中的大多数都很轻,我们可以假设这是一群小兔子,只有一些成年兔子在周围。
直方图 x 轴和 y 轴上唯一感兴趣的值是前两列(标签和值)。
其他列将有助于指示图中的其他特征。
现在,让我们去建立直方图。
我们可以做一个非常简单的,这样做:
pd.DataFrame(data).hist()
这将给我们一个情节,但远不是一眼就能完全理解的。
简单直方图——帮助不大——图 14
如果有人问我们,“兔子的体重在 0 到 1 公斤之间的百分比是多少?“看着这个情节很难回答。
如果我们想制作一个更有意义的直方图,让我们制作一个之前的(来自剧透警告的那个),只使用我们之前讨论过的工具:
# plot the bars from the histogram
x = np.array(df['labels'])
y = np.array(df['values_norm'])
plt.bar(x,y)# plot the cumulative curve
x = list(range(len(x)))
y = list(df['perc_cumulative'])
plt.plot(x,y,color='orange',marker='o')# change the y-axis limits
plt.axis(ymin=0, ymax=1.1)# plot the values from the cumulative curve on top of ity_labels = list(df['perc_cum_label'])for i,j in zip(x,y):
inplot = plt.annotate(str(y_labels[i])
,xy=(i-0.1,j+0.05)
,fontsize=12
,color='black')# draw vertical lines in 50, 75 and 90 percentile.
plt.axvline(np.percentile(data,50)-1, color='maroon',linestyle='--',linewidth=1)
plt.axvline(np.percentile(data,75)-1, color='maroon',linestyle='--',linewidth=1)
plt.axvline(np.percentile(data,90)-1, color='maroon',linestyle='--',linewidth=1)# indicate which percentile is each line
p_values = [np.percentile(data,50),np.percentile(data,75),np.percentile(data,90)]
p_numbers = ['p50', 'p75', 'p90']for i, j in zip(p_values, p_numbers):
inplot = plt.annotate(j
,xy=(i-1 + 0.05, 1.05)
,fontsize=12
,color='maroon')# some final tweaking in title, axis and labels
plt.title('Histogram and cumulative curve',fontsize=15)
plt.ylabel('Bins', size=15, labelpad=18)
plt.xlabel('Frequency', size=15, labelpad=18)
plt.tick_params(axis='both', labelsize = 14)
给予的结果:
直方图—图 15
奖励:双斧
我们可以做同样的图,但是让条形和曲线在同一水平上。我们通过生成双轴来实现这一点。
正如您将看到的,代码非常相似,特别是在开始时我们创建了双轴。然后,我们生成两个图(条形图和曲线),但不是以 plt 开始。,其中一个会从主机开始。还有另一个,跟不相上下。
# we import this package for the twin axes
from mpl_toolkits.axes_grid1 import host_subplot# we generate the twin axes
host = host_subplot(111)
par = host.twinx()# we plot the bars (use host. instead of plt.)
x = np.array(df['labels'])
y = np.array(df['values_norm'])
p1 = host.bar(x, y, color='steelblue')# we plot the curve (use par. instead of plt.)
x = list(range(len(x)))
y = list(df['perc_cumulative'])
p2 = par.plot(x, y, color='darkorange',marker='o')# we plot the values from the cumulative curve on top of ity_labels = list(df['perc_cum_label'])for i,j in zip(x,y):
inplot = par.annotate(str(y_labels[i])
,xy=(i-0.1,j+0.05)
,fontsize=12
,color='black')# some final tweaking in title, axis and labels
plt.title('Histogram and cumulative curve',fontsize=15)
par.axis(ymin=0, ymax=1.1)
host.set_xlabel("Bins", size=13, labelpad = 16)
host.set_ylabel("Frequency", size=13, labelpad = 16)# draw vertical lines in 50, 75 and 90 percentile.
plt.axvline(np.percentile(data,50)-1, color='maroon',linestyle='--',linewidth=1.5)
plt.axvline(np.percentile(data,75)-1, color='maroon',linestyle='--',linewidth=1.5)
plt.axvline(np.percentile(data,90)-1, color='maroon',linestyle='--',linewidth=1.5)# indicate which percentile is each line
p_values = p_values = [np.percentile(data,50),np.percentile(data,75),np.percentile(data,90)]
p_numbers = ['p50', 'p75', 'p90']for i, j in zip(p_values, p_numbers):
inplot = plt.annotate(j
,xy=(i-1 + 0.05, 0.39)
,fontsize=10
,color='maroon')
最终,得到想要的情节:
双轴直方图—图 16
我们到达了终点!我希望您喜欢这篇文章,并且您已经学到了很多(或者至少一些)新东西来提高您的 Python 绘图技能。
如果你想在以后看到更多类似的文章,请关注我。
你也可以看看我以前的文章,比如:
- 掌握 Pandas(和 Python)中的日期和时间戳(与当前文章风格相同)
其他文章:
正如在第一部分开始时所承诺的,一个到 Matplotlib 文档的链接,在那里你可以看到关于标记、线条样式等的一切:
Matplotlib 文档
感谢阅读!
MATLAB/Octave 中的绘图图例
作者图片
让你的情节成为传奇
地块图例对于正确注释图形至关重要。幸运的是,MATLAB/Octave 包含了 legend() 函数,它为生成图例提供了一些灵活易用的选项。在本文中,我将介绍 legend() 函数的基本用法,以及一些我经常使用的特殊情况。
包含示例的源代码可以在 GitHub 库中找到。
情节图例的基本用法
MATLAB/Octave 中的 legend() 函数允许您向绘图添加描述性标签。使用该函数最简单的方法是为绘图上的每一行传递一个字符串。基本语法是:图例('描述 1 ','描述 2 ',… ) 。
对于本节中的示例,我们将使用以下代码生成一个样例图形。
x = 0 : 0.1 : ( 2pi );
plot( x, sin( x ), 'rx', 'linewidth', 2 );
hold on;
plot( x, cos( x/3 ) - 0.25, 'bs', 'linewidth', 2 );
plot( x, cos( 4x )/3, '^g', 'linewidth', 2 );
hold off;
xlim( [ 0, 2*pi ] );
grid on;
title( 'Sample Plot' );
set( gca, 'fontsize', 16 );
可以使用以下命令添加图例。
legend( 'Line 1', 'Line 2', 'Line 3' );
除了将标签指定为单独的字符串之外,通常将字符串收集在单元数组中也很方便。这在以编程方式创建图例字符串时最有用。
legStr = { 'Line 1', 'Line 2', 'Line 3' };
legend( legStr );
为了方便起见,这个方法将用于其余的例子。得到的数字如下所示。
作者图片
默认情况下,图例位于右上角。但是,可以使用“位置”关键字来调整它的显示位置。使用基于基本方向的约定来选择位置。可以使用以下关键词:北、南、东、西、东北、西北、东南、西南。以下代码片段会将图例放在绘图的顶部中间。
legend( legStr, 'location', 'north' );
类似地,所有其他关键字都可以用于在绘图周围放置图例。下面的动画演示了所有不同的关键字。
作者图片
关键字“outside”也可以附加到所有位置,以将图例放置在绘图之外。下图显示了图例位于外部东侧的示例。
legend( legStr, 'location', 'eastoutside' );
作者图片
除了设置位置, legend() 功能还允许您将图例的方向设置为“垂直”(默认)或“水平”。以下示例将图例定位在底部,在绘图之外,水平方向。
legend( legStr, 'orientation', 'horizontal', ...
'location', 'southoutside' );
作者图片
使用“显示名称”属性
添加图例标注的另一种简便方法是在绘制线条时设置线条的“显示名称”属性。这可以在 plot() 调用期间或使用手柄上的 set() 完成。在这两种情况下,设置完属性后,您需要不带任何参数的所有 legend() 来打开图例。
plot() 的语法应该如下所示。
plot( x, sin( x ), 'rx', 'linewidth', 2, 'DisplayName', 'Line1' );
hold on;
plot( x, cos( x/3 ) - 0.25, 'bs', 'linewidth', 2, 'DisplayName', 'Line2' );
plot( x, cos( 4*x )/3, '^g', 'linewidth', 2, 'DisplayName',
'Line3' );
hold off;
set() 的语法应该是这样的。
% Plot the lines
h1 = plot( x, sin( x ), 'rx', 'linewidth', 2, 'DisplayName', 'Line1' );
hold on;
h2 = plot( x, cos( x/3 ) - 0.25, 'bs', 'linewidth', 2, 'DisplayName', 'Line2' );
h3 = plot( x, cos( 4*x )/3, '^g', 'linewidth', 2, 'DisplayName', 'Line3' );
hold off;% Update the display names
set( h1, 'DisplayName', 'Line1' );
set( h2, 'DisplayName', 'Line2' );
set( h3, 'DisplayName', 'Line3' )
然后,在没有任何输入参数的情况下,通过调用 legend()来打开图例。
legend();
结果图将与本教程前面显示的第一个示例相同。
使用图例手柄
与所有其他图形对象类似,图例属性可以使用 set() 和 get() 函数进行调整。下面的代码将捕获图例句柄,然后设置一些图例属性。在这个特定的例子中,位置被设置为东北,方向被设置为垂直,字体大小被设置为 16,并且框轮廓被移除。
hl = legend( legStr ); % save the legend handle
set( hl, 'orientation', 'vertical' ); % set the orientation
set( hl, 'location', 'northeast' ); % set the location
set( hl, 'fontsize', 16 ); % set the font size
set( hl, 'box', 'off' ); % turn off the box lines
作者图片
在 legend 对象上还可以设置许多其他属性。只需在命令窗口中调用 get(hl) 来显示它们。这些属性中最方便的一个是“位置”属性。position 属性允许我们通过指定图例的水平原点(h0)、垂直原点(v0)、宽度(w)和高度(h)来设置图例的确切位置。如果你不熟悉在图形对象上设置 position 属性,你可以在这里看到的简要描述。
每当你使用支线剧情或图形用户界面时,通常需要为图例指定一个准确的位置。这可以确保您的图例不会与其他元素重叠或产生奇怪的间距。在前一个示例的基础上,我们可以模拟一个示例 GUI,其中包括我们的绘图、几个按钮和一个手工放置在图上的图例。
% Set up the subplot
set( f, 'units', 'normalized' );
a = subplot( 'position', [ 0.1, 0.1, 0.5, 0.8 ] );% Build our sample plot
x = 0 : 0.1 : ( 2*pi );
plot( x, sin( x ), 'rx', 'linewidth', 2 );
hold on;
plot( x, cos( x/3 ) - 0.25, 'bs', 'linewidth', 2 );
plot( x, cos( 4*x )/3, '^g', 'linewidth', 2 );
hold off;
xlim( [ 0, 2*pi ] );
grid on;
title( 'Sample Plot' );
set( a, 'fontsize', 16 );% Add some buttons
a = uicontrol( f, 'units', 'normalized', 'position', [ 0.7, 0.7, 0.2, 0.1 ], ...
'style', 'pushbutton', 'string', 'Press Me' );
a = uicontrol( f, 'units', 'normalized', 'position', [ 0.7, 0.5, 0.2, 0.1 ], ...
'style', 'pushbutton', 'string', 'Click Here' );% Add a legend manually
hl = legend( legStr );
set( hl, 'fontsize', 16 );
set( hl, 'position', [ 0.74 0.25 0.12721 0.14310 ] );
作者图片
向图例添加特定线条
不需要为绘图上的所有线条提供字符串,可以指定应包含在图例中的特定线条对象的数组。我发现这对于你只需要注意图上几行的情况特别有用。当我绘制大量的例子或某种实现,并且想要覆盖和突出一些统计数据或度量时,我倾向于使用这个特性。
以下示例创建了一些随机数据,并从中计算了一些统计数据。所有实现都绘制为细灰色线,统计数据叠加为更粗的彩色线。
% Generate some random data and calculate a few statistics
t = sqrt( 0.5 ) * randn( 100, 500 );
ts = sort( t, 2 );
t10 = ts( :, floor( 0.10 * size( t, 2 ) ) );
t90 = ts( :, floor( 0.90 * size( t, 2 ) ) );% Plot realizations as thin gray lines and statistics as thicker, colored lines
plot( t, 'linewidth', 0.2, 'color', [0.5 0.5 0.5] );
hold on;
l1 = plot( mean( t, 2 ), 'linewidth', 4, 'color', [0, 1, 0] );
l2 = plot( t10, 'linewidth', 4, 'color', [1, 0, 0] );
l3 = plot( t90, 'linewidth', 4, 'color', [0, 0, 1] );
hold off;
grid on;
ylim( [-3, 3] );
title( 'Statistics' );
set( gca, 'fontsize', 16 );% Add a legend to just the statistics
hl = legend( [ l1, l2, l3 ], 'Mean', '10th Prct', '90th Prct', ...
'orientation', 'horizontal', 'location', 'north' );
set( hl, 'fontsize', 16 );
作者图片
结果是一个非常好的情节(在我看来)。如果我们没有传入行句柄,那么 legend() 函数会尝试为每个实现添加一个图例标签。当然还有其他的用例;然而,这是我经常画的一种类型的图。
摘要
在本文中,我介绍了在 MATLAB/Octave 中使用 legend() 函数的几种方法。这里有一些需要记住的关键事情。
- 图例标签可以作为单个字符串或单元格数组传入。
- 有许多位置和方向选项可用于将图例放置在图形的某些部分。
- 图例图形句柄可用于直接设置任何图例属性,包括手动设置图中任何位置。
- 线图形手柄可用于指定将在图例中标注的线的子集。
编码快乐!
画出我心的形状
两个简单的功能如何形成一个美丽的象形图
一个由基本功能构成的美丽的心脏象形图
让我们来探索形状是如何构造的。
将顶部和底部分开
形式为y=*f*(x)
的函数不能同时接受两个值。所以我们的计划将不得不涉及一个以上的功能。注意,x 轴整齐地将象形图分成两部分,每个可以用y=*f*(x)
表示。
象形图由两个功能组成
形成顶部罩杯
对于顶部,我们寻找两个半径为 1 的半圆。
我们如何得到它们?
创建一个半圆
让我们从半径为 1 的单个半圆开始。我们对它了解多少?嗯,在半径为 1 的圆里,我们总是可以构造一个最长边长为 1 的直角三角形。想起毕达哥拉斯,我们知道 T2。让我们利用这一点。
Upper half circle as prescribed by the [Pythagorean Theorem](https://medium.com/swlh/why-the-pythagorean-theorem-is-true-1d4c8a508510): y=sqrt(1-x²)
让我们求解y
,我们得到y = ±sqrt(1-x²)
。如果我们只使用正解,有效地选择我们圆的上半部分,我们可以把它标为y=sqrt(1-x²)
。
请注意高于和低于 1 的 x 值是如何定义的。我们要求一个负数的平方根,我们不能用实数来做这个。
移动杯子
如果我们能把我们的形状向右移动 1,我们就得到我们想要的函数的一半。我们想要移动我们的图表,这样我们现在得到的任何f(x)
都应该返回 x 值,再向上一个单位。我们本质上想要f(x-1)
。在我们的函数中,我们得到了y=sqrt(1-(x-1)²)
。
Graph of y=sqrt(1-(x-1)²)
镜像杯子
我们现在想要另一个杯子,就像我们得到的那个一样,但是在左边。我们注意到我们的函数只为正的 x 值定义。如果我们能让它被定义为相同范围的负值,这将实现我们的目标。我们希望定义f(x)
和f(-x)
并产生相同的 y 值。换句话说,我们想要f(|x|)
。让我们把它代入我们的函数,我们得到y=sqrt(1-(|x|-1)²)
。
Graph of y=sqrt(1-(|x|-1)²)
这样,我们心脏的上部就准备好了。让我们移到底部。
形成底线
对于底部,我们也有左右对称。中间有一个间断。让我们回忆一下底部是什么样子的。
我们在寻找一个描绘心脏底部的函数
先搞清楚左边。
寻找形状
我们正在寻找一个两边都接近垂直的函数,它被定义在跨越两个单位的范围上,并且它的 y 值范围看起来可疑地可能是 3.1415… =π。
这听起来像三角函数或其逆函数。我们来看看arccos(x)
y=arccos(x)具有我们需要的所有性质
看起来我们有一场不错的比赛。我们需要把它向下推π,这样图形就落在 x 轴下面,我们需要把我们的 x 向左变换一个单位。
考虑到这一点,我们将y=arccos(1+x)-π
作为我们的左侧。
y=arccos(1+x)-π forms our left side on the bottom
镜像形状
现在我们有了左侧,我们需要将函数扩展到右侧。该函数应该假设 x 的正值与左边的负值相同
注意我们左边的值是负 x 的结果。因此,我们不能简单地通过用 x 的绝对值代替 x 来扩展我们的函数。这在将图表的右侧镜像到左侧时是有效的。现在有点棘手了。
我们正在寻找一个表达式,使得 arccos 产生对称值。对于x≤0
,它应该等于1+x
——这是我们保持现有形状的方式——并且对于x>0
,它应该等于1+(-x)=1-x
——这对于正值x
产生相同的结果。
看来我们可以用:1-|x|
。对于负 x 值,它相当于1+x
,对于正 x 值,它变成1-x
。
我们到达:y=arccos(1-|x|)-π
y=arccos(1-|x|)-π nicely forms the bottom of our heart
结论
所以,当下一个情人节到来时,如果你和一个数学或科学类的人约会,你知道该在礼物的卡片上写些什么。
Plotly Dash 与 Streamlit——哪个是构建数据仪表板 web 应用程序的最佳库?
用于共享数据科学/可视化项目的两个顶级 Python 数据仪表板库的比较-包括示例代码。
Dash 还是 Streamlit 来生成 web 应用?(作者图片,右图来自星史)
他们说总有适合工作的工具。
但是我们都知道实用性不是空谈,在大多数情况下,找到合适的工具并不是一项简单的任务。
这甚至适用于 Python 数据可视化的相对利基领域。有大量令人惊叹的工具,如 Plotly 、 Altair 、 Bokeh 以及 matplotlib、seaborn 和 ggplot 等经典工具。这还没算上其他语言的,比如 D3.js,ggplot2 或者 Highcharts。
找到它们,然后继续对每个包进行研究,比较它们的优缺点,这可能会令人筋疲力尽。
此外,如果您希望以 web 应用程序的形式共享您的输出或增加交互性,搜索和研究标准会变得更加复杂,从而进一步增加搜索和研究的难度。
因此,在这篇文章中,我比较了几个领先的数据仪表板包,即 Plotly 的 Dash ,它可以说是该领域的行业领导者,以及该领域的一个新参与者,名为 Streamlit 。
本着数据可视化的精神,下一张图讲述了迄今为止的故事。
Plotly Dash 和 Streamlit 的 GitHub 明星历史(明星历史)
在正常情况下,Dash 出现的时间并不长。但是在数据科学和开源软件的背景下,3 年是一生。在此期间,它在 GitHub 上累积了超过 12k 颗星。
与此同时,Streamlit 成立还不到一年,在此期间,它的受欢迎程度迅速上升。从这个指标来看,它还没有 Dash 那么受欢迎,也没有 Dash 的社区,但 Streamlit 的发展势头令人印象深刻。
换句话说,它们都是非常受欢迎的库,正如您将看到的那样,这是有充分理由的。
主要差异
Streamlit 的重点是快速原型制作,而 Dash 侧重于生产/企业设置。
在这一点上,有必要指出这两个库在理念上的两个关键差异,一些设计决策就是从这两个差异中衍生出来的。
dash vs Streamlit——网站讲述故事(图片由作者提供,截图来自plotly.com/Streamlit . io)
首先,Streamlit 似乎更倾向于快速原型化应用,而 Dash 似乎更倾向于生产/企业环境。对他们网站的观察(2020 年 7 月下旬的截图)确实证实了这个假设。正如您将在下面看到的,这导致了一些设计决策和功能差异。
其次,Dash 主要设计用于 Plotly,而 Streamlit 对底层可视化库(如 Altair、Bokeh、Plotly、Seaborn 或 Matplotlib)更不可知。
注意 :我在本文的早期版本中错误地指出 Dash 只与 Plotly 兼容——这是不正确的。使用第三方库的例子可以在 这里 找到。
这意味着我认为它们都是令人惊奇的工具,为稍微不同的目的而制造。因此,正确工具的选择可能取决于您的用例以及您的背景。
让我们深入研究一下细节。
在开始之前
接下来,安装几个包— plotly
、dash
、dash-bootstrap-components
和streamlit
。用一个简单的pip install [PACKAGE_NAME]
安装每一个(在你的虚拟环境中)。
为了评估仪表板方面,我们只需从 JSON 文件中加载一个现有的图形,并围绕它构建我们的 web 应用程序。这是剧本。
*import* json
*import* plotly.graph_objects *as* go
*with* open("srcdata/fig.json", "r") *as* f:
fig = go.Figure(json.load(f))
如果想看代码,可以在这里找到附带的 git repo。
Dash vs Streamlit —战斗!
让我们开始认真的比较。如果你在这里,你可能对这两者都相对陌生(或者至少是其中之一)。那么学习曲线是什么样的呢?
学习曲线——绝对最小值
他们都做了很好的工作,让开发一个 web 应用程序变得非常简单。
这是生成一个功能完整的 Dash 应用程序所需的代码:
*import* dash
*import* dash_html_components *as* html
*import* dash_core_components *as* dcc
app = dash.Dash(__name__)
app.layout = html.Div([dcc.Graph(id="main-graph", figure=fig)])
*if* __name__ == '__main__':
app.run_server(debug=*True*)
这是在 Streamlit 中生成相同图形所需的代码,
*import* streamlit *as* st
st.write(fig)
注意,Streamlit 脚本需要使用 shell 命令在外部运行:
streamlit run [app_name]
一个网络应用——只有几行代码(图片由作者提供)
令人惊讶的是,这两个库都能够用不到十行代码开发出一个正常运行的 web 应用程序。
学习曲线——为了可观的功能
上面的内容很可爱,但这并不代表真正的 web 应用。
一个真正的网络应用不仅仅服务于一个数字。让我们开始给这个应用程序添加一些零碎的东西,看看这两个库是如何处理它们的。
为了近似真实仪表板可能包含的元素,我们将添加一个标题、一些占位符文本和一个交互组件,我们将使用它来改变图形的颜色。
这就是 Dash 和 Streamlit 之间的不同哲学开始显现的地方。这是我在 Dash 中添加了这些元素后的代码:
我们的基本网络应用程序,由 Dash 构建(图片由作者提供)
这是用 Streamlit 构建的等效应用程序:
我们的基本 web 应用程序,现在使用 Streamlit(图片由作者提供)构建
您会注意到,Streamlit 版本的脚本更短,它更线性地流动(我不需要构建额外的函数或回调——我们稍后将回到这一点),并且函数与用于呈现页面的底层 html 无关。
Dash 比 Streamlit 略显冗长,而 Streamlit 会为您处理更多的决策,因此用户可以专注于构建数据科学/分析方面。
例如,你会注意到在 Streamlit 中,命令st.write()
可以用来添加一个绘图图形,或者简单地添加正文文本。
需要明确的是,没有一种哲学天生就比另一种更好——这些是设计选择。话虽如此,就学习曲线而言,这意味着 Streamlit 更容易掌握和使用。
作为另一点,如果你正在寻找使用一个不是 Plotly 的图形包,采用 Streamlit 意味着你可以继续使用它,而不必学习 Plotly。
学习曲线:优势简化
美学
Streamlit 专注于快速原型的一个结果是,它比 Dash 更加固执己见。
在基本层面上,Streamlit 应用程序看起来比 Dash 应用程序更好看。上面,我通过添加一个预编程的样式表稍微作弊了一下。这是同一个应用程序,但在 Dash 中是“开箱即用的”(即没有应用格式)。
开箱即用(即无风格)的美学——破折号(图片由作者提供)
看看那些默认的 HTML 格式——把我们直接带回到 1995 年!!
但是,这是由设计(没有双关语)。虽然 Dash 为像我这样的懒人提供了默认的样式表,但是它是与样式无关的,这意味着您可以应用您喜欢的任何样式。事实上,像dash-bootstrap-components这样的第三方库使得应用现有的引导样式表变得轻而易举。
这里只有一两行代码:
*import* dash_bootstrap_components *as* dbc
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.**FLATLY**])
我们可以应用 Bootwatch 主题中的任何主题。这意味着您不必重新发明轮子,并且您可以利用 Bootstrap 社区已经完成的惊人工作。仅仅通过编辑上面代码的最后(粗体)部分,我就可以彻底改变应用程序的外观。
快速更改主题!(图片由作者提供)
在企业环境中,你可能会遇到公司风格指南,Dash 是这两者中的唯一选择。
所以对我来说,虽然 Streamlit 提供了一个非常好的开箱即用的外观,Dash 的卓越灵活性和利用现有引导主题的能力使它获得了认可。
美观:优势仪表板,灵活性&自举兼容性
速度(性能)
这是两个项目的不同理念对最终产品行为产生重大影响的另一个领域。
正如你可能在我上面的代码中看到的,Dash 使用了“回调的概念来增加交互性。需要设置函数,以便每当链接的输入改变时,它触发该函数(就像上面的update_fig()
函数)。
这可能更冗长,但是 Dash 应用程序只需要重新运行已经调用的函数。另一方面,Streamlit 的配置使得任何时候输入发生变化,所有的应用程序都会重新运行。
现在,它确实提供了一个缓存功能来规避其中的一些问题,但这肯定是一个限制,每次输入改变时都必须重新运行整个脚本。我会再次给达什点头。
性能:基于回调的模块化优势突进
功能
我这里所说的“功能”基本上是指你可以用哪个库做更多的事情,或者构建更好的应用。
Streamlit 自夸它与机器学习领域的许多巨头兼容——看看他们的网站:
Streamlit 的“兼容”应用列表(截图来自 streamlit.io
现在,其中一些很明显是在做标记——所有的 python 库,比如 Streamlit,都将与……python 兼容。其中许多甚至不是图形库。
尽管如此,你可以把 Altair、Bokeh 和 Plotly 构建的可视化混合并匹配到一个演示文稿中,这仍然是一个很大的优势,因为它们各有优缺点,在团队环境中,每个成员可能更喜欢彼此合作。
这不是达什的强项。虽然在某种程度上可以使用第三方库(参见这个概念验证库),但它是由 Plotly 主要为 Plotly.py 库构建的。但是,Dash 的亮点在于它为其构建的健康的现有模块库。
由于它的成熟、社区支持和企业采用,它有时间建立一整套模块,不仅用于交互性或 html 包装,还用于在生物信息学空间和网络可视化的 DAQ 整合的专门模块。
用 Dash 构建的网络可视化示例(图片来自作者,来自我的文章)
我称之为平局,每个库在关键领域都比另一个强。
功能:Tie,Streamlit 兼容 viz 库,Dash 拥有强大的模块库
证明文件
这个问题很容易回答,尽管可能不公平——原因如下。Dash 轻松获胜;这是一场不公平竞争的原因是,Dash 是一个非常非常成熟的产品,拥有庞大的用户群体,它的历史意味着人们有大量的时间来提问和获得答案。
不仅如此,由于 Plotly 的成熟,它还有一个庞大的第三方回购库、教程和文章库。这些都作为补充文件,是 Plotly 的另一个优势来源。
这并不是说 Streamlit 的文档不好。这很好,而且他们还有一个论坛,在那里你可以问问题或者分享展示。它只是没有 Dash 的文档好,也没有它的论坛大。
Streamlit 能在一两年内赶上吗?可能吧。就目前而言,Dash 在这方面远远占优。
文档:Dash
结论
这一切给我们带来了什么?
我在上面提到过,在这两者之间选择合适的工具可能取决于您的用例以及背景。
如果你的目标是快速构建一个好看的数据可视化应用程序,用于原型制作和与他人共享,或者如果你决定使用 Plotly 以外的东西作为底层可视化包,Streamlit 是一条好路。
这两者中 Streamlit 更容易识别。它的设计目标是快速原型化,因此更简洁。它还兼容多种可视化软件包,如 Bokeh、Altair 或 matplotlib。
但是,如果你正在寻找建立一个企业级的应用程序,如果性能是最重要的,如果你正在寻找你喜欢的应用程序风格(例如,企业风格指南),Dash 是正确的选择。
此外,可用模块、应用回购和文档的深度和广度意味着应用变得越复杂,使用 Dash 可能就越有利。
如果你喜欢这个,比如说👋/关注 twitter ,或点击此处获取更新。
编辑:我正在为所有事情数据和可视化开始一个子任务——这将是我与你直接接触的一种方式。我希望你能加入我。
数据无处不在,并且随着媒体对“大数据”的大量报道而呈指数级增长
visualnoise.substack.com](https://visualnoise.substack.com/p/coming-soon)
我也写了一点关于为什么我要开始一个子栈。
ICYMI:我也写过这篇关于用 Dash 构建 web 数据仪表板的文章。
[## 使用 Python 在几分钟内构建一个 web 数据仪表板
通过将您的数据可视化转换为基于 web 的仪表板,以指数方式提高功能和可访问性…
towardsdatascience.com](/build-a-web-data-dashboard-in-just-minutes-with-python-d722076aee2b)
如果你对我如何制作我在这里使用的篮球形象感兴趣:
用 hexbin shot 图表分析体育数据,用 Plotly 和 Plotly Express 分析气泡图(源代码&我自己的数据…
towardsdatascience.com](/interactive-basketball-data-visualizations-with-plotly-8c6916aaa59e)
Plotly 是数据科学及其发展方向的创始人
苹果 | 谷歌 | SPOTIFY | 其他 | 剪辑
克里斯·帕默在 TDS 播客
编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:
很容易把数据科学家想成“探索数据并为数据建模的人”。但实际上,这份工作的描述要灵活得多:作为一名数据科学家,你的工作是解决人们在数据方面的实际问题。
你会注意到我写的是“人们实际存在的问题”,而不是“建立模型”。人们遇到的问题实际上很少需要使用预测模型来解决。相反,一个好的可视化或交互式图表几乎总是解决问题过程的第一步,也可能是最后一步。
你知道谁非常非常了解可视化策略吗?阴谋地,就是他。Plotly 是一家开发大量优秀的开源可视化、探索和数据基础设施工具的公司。今天,他们的工具被全球超过 5000 万人使用,他们开发了许多工具和库,现在已经成为行业标准。所以你可以想象我和 Plotly 联合创始人兼首席产品官 Chris Parmer 交谈时有多兴奋。
Chris 分享了一些关于数据科学和分析工具的深刻见解,包括他认为该领域未来的发展方向。但正如他的职称所暗示的那样,他还关注所有伟大的数据科学家早期开发的另一个关键特征:产品本能(又名:“知道下一步要做什么”)。以下是我在对话中最喜欢的一些观点:
- 自动驾驶和复制一些规范的“数据科学生命周期”流程是危险的。相反,在你开始任何工作或个人项目之前,你能做的最重要的事情是问自己:交付你的工作产品意味着什么? 它应该采取什么形式?这可能会节省大量时间:它可以避免你花费数周时间优化复杂的机器学习模型,而你真正需要的只是一个简单的可视化或绘图。
- 这就是产品感变得至关重要的地方。正如我们在节目中一次又一次地说的那样,生活不是一场猜谜比赛,在这场比赛中,给你一个数据集和一个精确的数字来优化,你所要做的就是建立和改进模型。产品意识是决定你的模型应该优化什么的因素,更重要的是,你是否首先需要一个模型。
- 随着时间的推移,数据科学工具——尤其是 Plotly 的工具——已经抽象出越来越多曾经被认为是“数据科学”生命周期一部分的东西。例如,今天,大多数数据科学家可以不需要学习任何 Javascript 就可以完成工作,而十年前,如果你想与他人分享你的工作成果,进行基本的全栈 web 开发的能力更为重要。
- 随着新工具的不断出现,让数据科学家跟上步伐变得比以往任何时候都更加重要。(补充说明:我认识的许多数据科学家都将他们工作周的一小部分专门用于此。)
- 尽管数据科学工具的发展可能会继续朝着这个方向发展,但产品直觉以及从数据中得出结论并清晰传达数据的能力肯定会成为数据科学家未来的关键技能。
- 项目是给雇主留下深刻印象的好方法,但它们需要展示产品感:除了展示你知道如何回答这些问题之外,你还需要展示你能够提出正确的问题。
你可以在这里的推特上关注 Plotly,你也可以在这里的推特上关注我。
夹子
我们正在寻找能与我们的观众分享有价值的东西的客人。如果你碰巧知道谁是合适的人选,请在这里告诉我们:publication@towardsdatascience.com。
从前到后:条形图和折线图
让我们了解一下基本情况。包括源代码。
几天前,我宣布了一个关于 Plotly 库的即将到来的系列,涵盖了让你的可视化更上一层楼所需要知道的一切。虽然我建议你从一开始就阅读这个系列,但文章是以这样的方式形成的,每篇文章都是独立的,不要求你阅读之前的所有内容。
今天,您将阅读该系列的第二篇文章,其中第一篇文章仅涉及为什么您应该考虑 Plotly 的原因:
[## 我选择 Plotly 作为主要可视化库的 4 个原因
把你的想象带到 21 世纪
towardsdatascience.com](/4-reasons-why-im-choosing-plotly-as-the-main-visualization-library-dc4a961a402f)
因此,如果您正在努力选择一个数据可视化平台,或者只是想尝试一些新的东西,一定要去看看。
今天,我们将关注两种非常常见的图表类型:
- 条形图
- 折线图
第一个是你几乎在任何时候想要看到类别之间的区别时都会用到的,这个阶梯基本上存在于任何仪表盘中,通常代表某种时间序列数据。
事不宜迟,让我们开始吧。
导入和数据集—条形图
在导入方面,我们只有几个库,其中两个出现在每个数据科学笔记本中,最后两个来自 Plotly:
import numpy as np
import pandas as pd
import plotly.graph_objs as go
import plotly.offline as pyo
至于数据集,我们将在 Pandas 中声明一些随机的东西,比如说一年中不同月份的各种账单金额:
df = pd.DataFrame(data={
‘Month’: [‘January’, ‘February’, ‘March’],
‘Water’: [50, 61, 43],
‘Electricity’: [100, 88, 112],
‘Heat’: [86, 92, 104],
‘Total’: [236, 241, 259]
})
好了,我们有一切可以开始了。让我们探索绘制该数据集的所有不同方式。
条形图
今天我们将讨论三种类型的条形图:
- (正常)条形图
- 嵌套条形图
- 堆积条形图
你不需要事先知道它们之间的区别,因为所有的东西都会在文章中解释。这个想法是向您展示用条形图表示数据的每一种方法,然后由您选择最合适的选项。
普通条形图
好了,让我们在 x 轴上画出Month
,在 y 轴上画出Total
。像往常一样,您将声明一个figure
对象并将数据和布局放入其中。这个非常简单,不需要进一步解释:
fig = go.Figure(
data=[
go.Bar(
x=df[‘Month’],
y=df[‘Total’]
)
],
layout=go.Layout(
title=’Total Monthly Cost’
)
)pyo.plot(fig)
如果您要执行这段代码,将会出现这样一个图:
好吧,还不错,但也不太好——这里我们只显示了总量,这并没有向我们展示全貌。我们来看看如何改进。
嵌套条形图
在这个例子中,我们将能够可视化每笔费用而不仅仅是总额。请记住,如果您有大量的数据和大量的类别,这种类型的图表可能会变得难以查看——但它对我们的情况来说是很好的。
这样做真的很简单——我们只需要在我们的数据数组中为我们想要可视化的每个类别放置一个go.Bar
对象。代码如下:
fig = go.Figure(
data=[
go.Bar(
x=df[‘Month’],
y=df[‘Water’],
name=’Water’,
marker=dict(color=’#3498db’)
),
go.Bar(
x=df[‘Month’],
y=df[‘Electricity’],
name=’Electricity’,
marker=dict(color=’#f1c40f’)
),
go.Bar(
x=df[‘Month’],
y=df[‘Heat’],
name=’Heat’,
marker=dict(color=’#e74c3c’)
)
],
layout=go.Layout(
title=’Monthly Cost per Item’
)
)pyo.plot(fig)
执行上面的代码会产生一个好看的图表:
如果你问我的话,我会说这比我们以前有了很大的进步。在结束本节之前,让我们探索另一个选项。
堆积条形图
在大多数情况下,这是我最喜欢的选择。占用的空间与常规条形图一样少,显示的信息与嵌套条形图一样多。代码与嵌套条形图的代码非常相似,唯一的区别是您需要在布局中指定barmode='stack'
。
让我们来看看实际情况:
fig = go.Figure(
data=[
go.Bar(
x=df[‘Month’],
y=df[‘Water’],
name=’Water’,
marker=dict(color=’#3498db’)
),
go.Bar(
x=df[‘Month’],
y=df[‘Electricity’],
name=’Electricity’,
marker=dict(color=’#f1c40f’)
),
go.Bar(
x=df[‘Month’],
y=df[‘Heat’],
name=’Heat’,
marker=dict(color=’#e74c3c’)
)
],
layout=go.Layout(
title=’Monthly Cost per Item’,
**barmode=’stack’**
)
)pyo.plot(fig)
执行上面的代码将会产生如下所示的图表:
太好了!我们已经深入讨论了条形图,现在让我们看看可以用折线图做什么!
数据集-折线图
对于这一部分,我们只需要两个Numpy
数组:
x = np.linspace(0, 49, 50)
y = np.random.randint(10, 20, 50)
就这样——我们可以开始探索。
折线图
与条形图一样,我们将讨论两个选项:
- 基本折线图
- 带标记的折线图
- 多折线图
如果你不清楚第二个选项是什么意思,请耐心听我说。
基本折线图
下面是我们如何从这两个先前声明的 Numpy 数组制作一个基本的折线图:
fig = go.Figure(
data=[
go.Scatter(
x=x,
y=y,
mode=’lines’
)
],
layout=go.Layout(
title=’Line Chart’
)
)pyo.plot(fig)
执行此代码将产生以下图表:
这不是最好看的折线图,我们将在文章结尾探索如何调整视觉效果。
带标记的折线图
如果没有太多的数据点,有时在线旁边显示小的标记会很方便。代码方面的变化很小——因为我们只需要调整模式选项,并将其设置为mode='lines+markers'
。让我们看看代码:
fig = go.Figure(
data=[
go.Scatter(
x=x,
y=y,
**mode=’lines+markers’**
)
],
layout=go.Layout(
title=’Line Chart’
)
)pyo.plot(fig)
执行上面编写的代码将会得到这个图表:
很好。现在让我们看看如何将多条线组合成一个折线图。
多折线图
现在让我们看看将多条线组合成一个图表是多么容易。就像条形图一样,我们将把多个go.Scatter
对象放到我们的数据数组中。
为了增加一点趣味,我们添加了一些布局选项,只是为了让你从外观上感受一下可以做些什么。
代码如下:
fig = go.Figure(
data=[
go.Scatter(
x=x,
y=y,
mode=’lines’,
name=’Lines’,
line=dict(
color=’#3498db’,
width=4
)
),
go.Scatter(
x=x,
y=y + 10,
mode=’lines+markers’,
name=’Lines + Markers’,
line=dict(
color=’#f1c40f’,
dash=’dash’,
width=4
)
)
],
layout=go.Layout(
title=’Line Chart’,
hovermode=’x’
)
)pyo.plot(fig)
执行该代码将生成以下图表:
厉害!今天这样就够了。
在你走之前
这篇文章比我平时写的要长得多。但我希望你能理解。我们今天谈了很多,但还有更多的要谈。
接下来的文章将涵盖散点图和气泡图,如果你对此感兴趣,请继续关注。
感谢您的阅读,请一如既往地提出您的问题和评论。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
从前到后:散点图和气泡图
知道其中的区别——并在这个过程中让他们看起来很棒!
距离我在 Plotly 库上的上一篇文章已经过去了几天,上一篇文章介绍了棒线图表和线图表。虽然我建议在此之前先阅读那篇文章,但这绝不是强制性的,因为每篇文章都是为了讲述完整的故事。
如果你想跟进这个系列,这里有一些链接:
好了,事不宜迟,让我们花点时间讨论一下这篇文章涵盖的内容。主要想法是获取一个数据集(一个众所周知的数据集,稍后会详细介绍)并探索如何可视化它——使用散点图和气泡图。
所用的数据集很小,但足以满足我们的需求,可以从这个 URL 下载。你不需要下载它,因为我们会直接从网站导入它。
好吧,没有任何麻烦,让我们开始吧!
导入和数据集
导入方面,我们需要两个库——Pandas
和Plotly
——下面是如何导入它们:
import pandas as pd
import plotly.graph_objs as go
import plotly.offline as pyo
至于数据集,我们直接从 GitHub 抓取。这是众所周知的汽车数据集,非常适合我们将要进行的可视化类型:
df = pd.read_csv(‘[https://raw.githubusercontent.com/plotly/datasets/master/mtcars.csv'](https://raw.githubusercontent.com/plotly/datasets/master/mtcars.csv'))
df.head()
好了,就这样,现在让我们开始吧。
散点图
现在,散点图背后的基本思想是可视化一组变量之间的关系。就关系而言,我们指的是变量之间的依赖性——或运动——如果第一个变量运动了一定量,第二个变量会发生什么——或者用最简单的方式说,相关性。
用 Plotly 制作散点图非常简单——几乎任何东西都是如此(除了地图,这些可能是底部的痛苦,但在两篇文章中会有更多的讨论)—因为我们需要定义众所周知的data
和layout
选项。
就像线图一样,我们将使用go.Scatter()
作为我们的数据,但是我们需要指定mode='markers'
来使一切正常工作。
下面是你如何在 x 轴上可视化属性hp
(马力),在 y 轴上可视化属性mpg
(每加仑英里数):
fig = go.Figure(
data=[
go.Scatter(
x=df[‘hp’],
y=df[‘mpg’],
mode=’markers’,
)
],
layout=go.Layout(
title=dict(
text=’Horsepower vs. MPG’,
font=dict(size=20)
),
hovermode=’closest’
)
)pyo.plot(fig)
没有太多的代码行,但是让我们看看它会产生什么结果:
我知道你现在在想什么:
- 基础点太无聊了
- 颜色很糟糕
- 马克笔太小了
好吧,让我们来解决这些问题:
fig = go.Figure(
data=[
go.Scatter(
x=df[‘hp’],
y=df[‘mpg’],
mode=’markers’,
**marker=dict(
size=16,
symbol=’x-dot’,
color=’#d35400'
)**
)
],
layout=go.Layout(
title=dict(
text=’Horsepower vs. MPG’,
font=dict(size=20)
),
hovermode=’closest’
)
)pyo.plot(fig)
我们之前遇到的每个问题都可以通过调整标记参数来解决,通过marker
参数。我们的图表现在看起来是这样的:
尽管不是最漂亮的,但现在也可以了。现在让我们探索散点图的更大的兄弟——气泡图——看看显示附加信息和将大小设置为动态是多么容易。
气泡图
以防你没有阅读整篇文章,而是跳到这一部分,这里有你应该知道的:我们通常使用气泡图来显示额外的信息,或者通过尺寸显示,或者通过颜色显示。
例如,假设我们仍然希望将hp
(马力)放在 x 轴上,将mpg
(每加仑英里数)放在 y 轴上——但是我们还希望将大小设置为动态的——假设根据wt
(重量)属性,并且还将颜色设置为动态的——根据气缸数量cyl
。
对于单个图表来说,这听起来像是太多的信息,但实际上不是。下面是如何用代码实现上述内容:
fig = go.Figure(
data=[
go.Scatter(
x=df[‘hp’],
y=df[‘mpg’],
mode=’markers’,
marker=dict(
size=df[‘wt’] * 10,
color=df[‘cyl’]
)
)
]
)pyo.plot(fig)
如果您想知道,我们将wt
属性的值相乘,因为否则,它会太小。您可以随意调整这个值。图表如下所示:
很好。我们在两个轴上都有与简单散点图相同的信息,但这里显示了两条附加信息。
如果你问我,这种黄色有点太多了,所以让我们看看如何改变调色板:
fig = go.Figure(
data=[
go.Scatter(
x=df[‘hp’],
y=df[‘mpg’],
mode=’markers’,
marker=dict(
size=df[‘wt’] * 10,
color=df[‘cyl’],
**colorscale=’Viridis’**
)
)
]
)pyo.plot(fig)
图表如下所示:
如你所见,这种视觉化对眼睛来说更容易一些。你可以参考这个页面来获得可用调色板的完整列表。
我想今天这样就够了。
在你走之前
我希望这对你来说不会太难——我知道这种复杂的语法可能需要一段时间来适应——但如果你问我,这是值得的。
接下来的文章将涵盖饼状图和树形图——如果你对此感兴趣,请继续关注。
感谢阅读。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
帕尔默企鹅的熊猫
PalmerPenguins——虹膜替代品!!
作品鸣谢:@ Allison _ horsthttps://github.com/allisonhorst/palmerpenguins
PalmerPenguins 数据集
最近推出了 PalmerPenguins 数据集,作为常用于数据探索、可视化和分类建模的Iris
数据集的替代。
帕尔默彭金斯的数据由Kristen Gorman博士和LTER 南极洲帕尔默站收集和提供,帕尔默站是长期生态研究网络的成员。
palmerpenguins 包包含两个数据集,可以从最初的 GitHub 链接—https://github.com/allisonhorst/palmerpenguins下载,也可以从 Kaggle 访问。
两个数据集都包含 344 只企鹅的数据。在这个数据集中有 3 种不同种类的企鹅,它们是从南极洲帕尔默群岛的 3 个岛屿上采集的。
使用 Pandas 和 Plotly 进行数据探索和可视化!
现在让我们使用 Pandas 和 Plotly 对 palmerpenguins 数据集进行探索性数据分析和可视化。
要安装 Pandas 和 Plotly,请在终端中运行以下命令:
**pip install pandas
pip install plotly**
导入所需的库
# importing dependencies here
import pandas as pd
import os# importing plotly modules for visualizations
import plotly.graph_objects as go
from plotly.offline import init_notebook_mode, iplot
读取数据集
# reading the penguins dataset
penguins_df = pd.read_csv(path to your dataset)# checking the top 5 rows
penguins_df.head()
企鹅数据集
作品鸣谢:@ Allison _ horsthttps://github.com/allisonhorst/palmerpenguins
检查行数和列数
# checking the number of rows and columns
penguins_df.shape**Output:** (344, 17)
检查数据集中的列名
# checking the column names
penguins_df.columns**Output:** Index([‘studyName’, ‘Sample Number’, ‘Species’, ‘Region’, ‘Island’, ‘Stage’, ‘Individual ID’, ‘Clutch Completion’, ‘Date Egg’, ‘Culmen Length (mm)’, ‘Culmen Depth (mm)’, ‘Flipper Length (mm)’, ‘Body Mass (g)’, ‘Sex’, ‘Delta 15 N (o/oo)’, ‘Delta 13 C (o/oo)’, ‘Comments’, ‘Color’], dtype=’object’)
检查数据集统计数据
# checking the dataset stats
penguins_df.describe()
检查计数和数据类型
# checking counts and data type for each column
penguins_df.info()
检查数据集中是否存在空值
# checking for the presence of null values
penguins_df.isnull().sum()
某些列存在缺失值/空值
处理空值
**# Using Scikit Learn to substitute the null values with the most frequently occurring value in that column.**from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy=”most_frequent”)
penguins_df.iloc[:, :] = imputer.fit_transform(penguins_df)# ensuring that there are no more null values left
penguins_df.isnull().sum()
不再有丢失的值
探索岛屿数据
# checking island data
island_df = pd.DataFrame(
penguins_df.groupby(["Island", "Species"])["Island"].count()).rename(columns={"Island": "Count"})island_df
为基于绘图的可视化准备数据
请注意,如果我们可以首先使用 Pandas Dataframes 聚集每个可视化所需的数据,Plotly 可视化会更容易实现。
所以,首先,想想你想要的视觉效果,然后相应地创建数据框架。这将有助于在创建地块时高效快速地设置地块轨迹。
**# since each of the 3 penguin species present in the dataset has a particular color associated, adding a color column to the dataset to keep track of the color associated with a specie.**cls = {
"Adelie Penguin (Pygoscelis adeliae)": "darkorange",
"Gentoo penguin (Pygoscelis papua)": "teal",
"Chinstrap penguin (Pygoscelis antarctica)": "mediumorchid",
}penguins_df["Color"] = penguins_df["Species"].apply(lambda x: cls[x])
C 为每一个物种创建单独的数据帧,以便于下面的可视化!!
阿德利企鹅
adelie_df = penguins_df[
penguins_df[“Species”] == “Adelie Penguin (Pygoscelis adeliae)”
].reset_index(drop=True)adelie_df.head(2)
阿德利企鹅数据
adelie_count_df = (
pd.DataFrame(adelie_df.groupby([“Species”, “Color”])[“studyName”].count())
.sort_values(“studyName”, ascending=False)
.reset_index()
.rename(columns={“studyName”: “Count”})
)adelie_count_df
巴布亚企鹅
gentoo_df = penguins_df[
penguins_df[“Species”] == “Gentoo penguin (Pygoscelis papua)”
].reset_index(drop=True)gentoo_df.head(2)
巴布亚企鹅数据
gentoo_count_df = (
pd.DataFrame(gentoo_df.groupby([“Species”, “Color”])[“studyName”].count())
.sort_values(“studyName”, ascending=False)
.reset_index()
.rename(columns={“studyName”: “Count”})
)gentoo_count_df
下颚带企鹅(南极企鹅)
chinstrap_df = penguins_df[
penguins_df[“Species”] == “Chinstrap penguin (Pygoscelis antarctica)”
].reset_index(drop=True)chinstrap_df.head(2)
下颚带企鹅数据
chinstrap_count_df = (
pd.DataFrame(chinstrap_df.groupby([“Species”, “Color”])[“studyName”].count())
.sort_values(“studyName”, ascending=False)
.reset_index()
.rename(columns={“studyName”: “Count”})
)chinstrap_count_df
基于物种的性别计数
female_df = (
pd.DataFrame(
penguins_df[penguins_df[“Sex”] == “FEMALE”][[“Species”, “Sex”, “Color”]]
.groupby(“Species”)[“Sex”]
.count()
)
.sort_values(“Sex”, ascending=False)
.reset_index()
.rename(columns={“Sex”: “Female”})
)female_df
male_df = (
pd.DataFrame(
penguins_df[penguins_df[“Sex”] == “MALE”][[“Species”, “Sex”]]
.groupby(“Species”)[“Sex”]
.count()
)
.sort_values(“Sex”, ascending=False)
.reset_index()
.rename(columns={“Sex”: “Male”})
)male_df
奇妙的视觉效果
现在,当我们已经很好地处理了我们的数据,将它们聚合成数据帧,记住具体的可视化,让我们开始在企鹅数据集上构建 Plotly plots!!
企鹅物种计数图
# Penguin Species Count
trace1 = go.Bar(
x=adelie_count_df[“Species”],
y=adelie_count_df[“Count”],
marker=dict(color=adelie_count_df[“Color”], line_color=”rgb(0,0,0)”),
name=”Adelie Penguin”,
)trace2 = go.Bar(
x=gentoo_count_df[“Species”],
y=gentoo_count_df[“Count”],
marker=dict(color=gentoo_count_df[“Color”], line_color=”rgb(0,0,0)”),
name=”Gentoo penguin”,
)trace3 = go.Bar(
x=chinstrap_count_df[“Species”],
y=chinstrap_count_df[“Count”],
marker=dict(color=chinstrap_count_df[“Color”], line_color=”rgb(0,0,0)”),
name=”Chinstrap penguin”,
)data = [trace1, trace2, trace3]layout = dict(
title=”<b>Penguin Species Count</b>”,
showlegend=True,
xaxis=dict(showgrid=False, title=”Species”),
yaxis=dict(title=”Count”),
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
基于企鹅种类的性别计数图
# penguins gender count based on speciestrace1 = go.Bar(
x=female_df[“Species”],
y=female_df[“Female”],
text=”Female”,
textposition=”outside”,
marker=dict(color=species_df[“Color”], line_color=”rgb(0,0,0)”),
)trace2 = go.Bar(
x=male_df[“Species”],
y=male_df[“Male”],
text=”Male”,
textposition=”outside”,
marker=dict(color=species_df[“Color”], line_color=”rgb(0,0,0)”),
name=”Male”,
)data = [trace1, trace2]layout = dict(
title=”<b>Penguin Gender-Based Species Count</b>”,
showlegend=False,
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
相关图
下面我们将看到如何创建基于 Plotly 的相关图的长度和脚蹼长度(相对于身体质量)。可以遵循相同的步骤为其他特征创建相关图。
# checking for correlation between features
penguins_df.corr()
列之间的相关性
身高(mm)与体重(g)的相关图
# Culmen Length (mm) vs. Body Mass (g)trace1 = go.Scatter(
x=adelie_df[“Culmen Length (mm)”],
y=adelie_df[“Body Mass (g)”],
text=adelie_df[“Species”],
mode=”markers”,
marker=dict(size=10, symbol=”circle”, line=dict(color=”rgb(0,0,0)”, width=0.5)),
marker_color=adelie_df[“Color”],
name=”Adelie Penguin”,
)trace2 = go.Scatter(
x=gentoo_df[“Culmen Length (mm)”],
y=gentoo_df[“Body Mass (g)”],
text=gentoo_df[“Species”],
mode=”markers”,
marker=dict(size=10, symbol=”square”, line=dict(color=”rgb(0,0,0)”, width=0.5)),
marker_color=gentoo_df[“Color”],
name=”Gentoo penguin”,
)trace3 = go.Scatter(
x=chinstrap_df[“Culmen Length (mm)”],
y=chinstrap_df[“Body Mass (g)”],
text=chinstrap_df[“Species”],
mode=”markers”,
marker=dict(
size=12, symbol=”triangle-up”, line=dict(color=”rgb(0,0,0)”, width=0.5)
),
marker_color=chinstrap_df[“Color”],
name=”Chinstrap penguin”,
)data = [trace1, trace2, trace3]layout = dict(
title=”<b>Culmen Length (mm) vs. Body Mass (g)</b>”,
showlegend=True,
xaxis=dict(title=”Culmen Length (mm)”),
yaxis=dict(title=”Body Mass (g)”),
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
鳍状肢长度(毫米)与体重(克)的相关图
# Flipper Length (mm) vs. Body Mass (g)trace1 = go.Scatter(
x=adelie_df[“Flipper Length (mm)”],
y=adelie_df[“Body Mass (g)”],
text=adelie_df[“Species”],
mode=”markers”,
marker=dict(size=9, symbol=”circle”, line=dict(color=”rgb(0,0,0)”, width=0.5)),
marker_color=adelie_df[“Color”],
name=”Adelie Penguin”,
)trace2 = go.Scatter(
x=gentoo_df[“Flipper Length (mm)”],
y=gentoo_df[“Body Mass (g)”],
text=gentoo_df[“Species”],
mode=”markers”,
marker=dict(size=9, symbol=”square”, line=dict(color=”rgb(0,0,0)”, width=0.5)),
marker_color=gentoo_df[“Color”],
name=”Gentoo penguin”,
)trace3 = go.Scatter(
x=chinstrap_df[“Flipper Length (mm)”],
y=chinstrap_df[“Body Mass (g)”],
text=chinstrap_df[“Species”],
mode=”markers”,
marker=dict(
size=11, symbol=”triangle-up”, line=dict(color=”rgb(0,0,0)”, width=0.5)
),
marker_color=chinstrap_df[“Color”],
name=”Chinstrap penguin”,
)data = [trace1, trace2, trace3]layout = dict(
title=”<b>Flipper Length (mm) vs. Body Mass (g)</b>”,
showlegend=True,
xaxis=dict(title=”Flipper Length (mm)”),
yaxis=dict(title=”Body Mass (g)”),
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
可视化分布
下面我们将看到如何创建基于 Plotly 的脚蹼长度和 Culmen 长度的分布图。可以按照相同的步骤为其他胎儿创建分布图。
脚蹼长度分布图
# Flipper Length Distributionstrace1 = go.Violin(
x=adelie_df[“Species”],
y=adelie_df[“Flipper Length (mm)”],
box_visible=True,
meanline_visible=True,
points=”all”,
line_color=”darkorange”,
name=”Adelie Penguin”,
)trace2 = go.Violin(
x=gentoo_df[“Species”],
y=gentoo_df[“Flipper Length (mm)”],
box_visible=True,
meanline_visible=True,
line_color=”teal”,
points=”all”,
name=”Gentoo penguin”,
)trace3 = go.Violin(
x=chinstrap_df[“Species”],
y=chinstrap_df[“Flipper Length (mm)”],
points=”all”,
box_visible=True,
meanline_visible=True,
line_color=”mediumorchid”,
name=”Chinstrap penguin”,
)data = [trace1, trace2, trace3]layout = dict(
title=”<b>Flipper Length (mm) Distribution for the 3 Species</b>”,
showlegend=True,
xaxis=dict(title=”Penguin Species”),
yaxis=dict(title=”Flipper Length (mm)”),
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
秆长分布图
# Culmen Length Distributionstrace1 = go.Violin(
x=adelie_df[“Species”],
y=adelie_df[“Culmen Length (mm)”],
box_visible=True,
meanline_visible=True,
points=”all”,
line_color=”darkorange”,
name=”Adelie Penguin”,
)trace2 = go.Violin(
x=gentoo_df[“Species”],
y=gentoo_df[“Culmen Length (mm)”],
box_visible=True,
meanline_visible=True,
line_color=”teal”,
points=”all”,
name=”Gentoo penguin”,
)trace3 = go.Violin(
x=chinstrap_df[“Species”],
y=chinstrap_df[“Culmen Length (mm)”],
points=”all”,
box_visible=True,
meanline_visible=True,
line_color=”mediumorchid”,
name=”Chinstrap penguin”,
)data = [trace1, trace2, trace3]layout = dict(
title=”<b>Culmen Length (mm) Distribution for the 3 Species</b>”,
showlegend=True,
xaxis=dict(title=”Penguin Species”),
yaxis=dict(title=”Culmen Length (mm)”),
plot_bgcolor=”rgba(0,0,0,0)”,
paper_bgcolor=”rgba(0,0,0,0)”,
)fig = dict(data=data, layout=layout)iplot(fig)
参考
https://github.com/allisonhorst/palmerpenguins
Plotly Python:散点图
用 Plotly 深入研究散点图
在我上一篇关于 Plotly 的文章中,我们对这个库本身做了一个简单的介绍——包括一个图形对象的一般结构,使用跟踪,以及通过 update_trace 和 update_layout 函数进行定制。
今天,我们将更深入地了解如何使用 Plotly 库构建和定制散点图。
对于我们今天的数据集,我从 Kaggle 选择了一个 Steam 游戏数据集,我有兴趣看看游戏的价格和平均游戏时间之间是否有任何关系。
加载到数据帧中,我们可以看到头部如下:
现在,如果你不熟悉 Steam,你可能不会意识到这个平台上有多少游戏。在这个数据集的例子中,如果我们计算 name 列中的条目数,我们得到 27075 个标题。
然而,由于任何人都可以在 Steam 上发布内容,因此也有许多异常游戏会影响我们的结果——即平均游戏时间为 0 的游戏数量。
如果我们运行steamdf[steamdf[‘average_playtime’] == 0].count()
我们可以看到有 20905 个游戏没有游戏时间!
一旦我应用了以下标准:
steamdf = steamdf[(steamdf['average_playtime'] != 0) &
(steamdf['average_playtime'] < 40000)] steamdf = steamdf[steamdf['price'] < 80]
我还剩下 6160 场比赛可以看。
所以,让我们把它扔进一个快速散点图,看看初始数据是什么样的。
import plotly.graph_objects as gofig = go.Figure(data=go.Scatter(
x=steamdf['price'],
y=steamdf['average_playtime'],
mode='markers'
))fig.show()
非常基本——但是,嘿,我们需要从某个地方开始,对吗?
让我们继续添加标题和轴图例。
现在,让我们来设计一下我们的标记。它们看起来有点平淡,融合在一起。
import plotly.graph_objects as gofig = go.Figure(data=go.Scatter(
x=steamdf['price'],
y=steamdf['average_playtime'],
mode='markers',
marker_size=steamdf['ratio'],
marker=dict(
color='rgb(255, 178, 102)',
size=10,
line=dict(
color='DarkSlateGrey',
width=1
)
)
))fig.update_layout(
title='Price vs. Average Playtime',
xaxis_title='Price (GBP)',
yaxis_title='Average Playtime (Minutes)'
)
fig.show()
我们从使用marker=dict()
开始,然后它接受我们用来样式化我们的标记(数据点)的所有参数。marker 中还有一个附加的 dict,它对应于标记边框的样式选项。
请注意我们如何使用 RGB 值给标记本身着色,而我们使用 CSS 颜色代码给标记轮廓着色。两者都是完全可以接受的——你甚至可以用 RGBA 来设置 alpha。
标记大小通过marker_size
属性进行调整。我创建了一个新的数据框架列“ratio ”,在那里我计算了正评级/负评级的比例值。
气泡越大,表示游戏正面与负面评分的比率越高;我们预计这些游戏通常会有更长的平均游戏时间。
现在,当我们悬停在这些点上时,我们只能看到 x 和 y 值,这并不十分有用。然而,很容易添加适当的悬停文本到我们的点,所以我们可以看到我们正在看的游戏的名称。
通过在我们的图形对象中包含下面的代码,我们可以将上面的悬停数据变成更好的东西!
hovertext=steamdf['name'],
hoverlabel=dict(namelength=0),
hovertemplate='%{hovertext}<br>Price: %{x} <br>Avg. Playtime: %{y}'
这可能看起来有点让人不知所措,但是让我们来分解一下:
**hovertext**
是我们定义的用于模板的变量**hoverlabel**
主要是为了美观。如果你把它放在里面,你可以在工具提示框的旁边看到跟踪号。我不喜欢,所以这段代码会删除它。**hovertemplate**
允许你创建一个模板字符串来呈现你想要出现在悬浮框上的任何信息。
使用%{variable}格式添加变量,您可以使用 HTML 标签,如
、、等。
你可能会花上数不清的时间去尝试所有可用的风格选项。
如果你觉得自己缺少一些灵感,可以使用 fig.update_layout()中的 template 属性。
示例:fig.update_layout(template=’plotly_white’)
选项有:“plotly”、“plotly_white”、“plotly_dark”、“ggplot2”、“seaborn”、“simple_white”
然而,我很快就做了一些东西,向你展示一些你想做的最常见的定制。
也就是说,我们改变了:
-轴网格/零/绘图边框线(颜色、宽度和可见性)
-绘图背景和纸张背景颜色
-标题字体、大小、颜色
-自定义范围
fig = go.Figure(data=go.Scatter(
x=steamdf['price'],
y=steamdf['average_playtime'],
mode='markers',
marker_size=steamdf['ratio'],
hovertext=steamdf['name'],
hoverlabel=dict(namelength=0),
hovertemplate='%{hovertext}<br>Price: %{x:$}
<br>Avg. Playtime: %{y:,} min',
marker=dict(
color='rgb(255, 178, 102)',
size=10,
line=dict(
color='DarkSlateGrey',
width=1
)
)
))fig.update_layout(
title='Price vs. Average Playtime',
xaxis_title='Price (GBP)',
yaxis_title='Average Playtime (Minutes)',
plot_bgcolor = 'white',
paper_bgcolor = 'whitesmoke',
font=dict(
family='Verdana',
size=16,
color='black'
)
)fig.update_xaxes(showline=True,
linewidth=2,
linecolor='black',
mirror=True,
showgrid=False,
zerolinecolor='black',
zerolinewidth=1,
range=[-1, 65])fig.update_yaxes(showline=True,
linewidth=2,
linecolor='black',
mirror=True,
showgrid=True,
gridwidth=1,
gridcolor='grey',
zerolinecolor='black',
zerolinewidth=1,
range=[-2000, 40000])fig.show()
我希望这足以让你对使用 Plotly 创建和定制散点图充满信心!
情节九情节解构:正则化逻辑回归诊断情节
解释对桶样品进行分类的逻辑回归模型的交叉验证结果
注意,这是我写的另一篇文章 与 的姊妹篇,虽然我试图让这篇文章自成一体,但那篇文章确实更详细地讨论了统计数据。本文重点介绍如何使用 *plotnine*
制作诊断图。如果你不熟悉 plotnine, 这篇文章 提供了很好的库介绍。和我的任何文章一样,你也可以阅读关于 Github*的文章。另外,这是我写的关于用 Python 制作朱朱情节的* 系列文章 的一部分。
介绍
统计学中的一个主要问题是欠确定性:缺乏数据使我们无法在模型之间进行选择,因为它们基于该数据共享相同的性能。正则化通过向问题中引入额外的信息来帮助我们做出选择。回归中正则化的一种常见形式是将模型系数的范数添加到标准的平方差损失函数中。这种技术的各种形式被称为套索、脊回归或弹性网调整。
当将正则化引入我们的回归方法时,我们需要做出的一个主要选择是正则化强度,即我们在多大程度上关注模型系数的范数与损失函数的平方差部分。选择这个超参数的一个常用方法是使用交叉验证。选择产生最佳交叉验证性能的超参数设置。
本文的目标是构建一些诊断图,帮助你解释交叉验证的正则化强度的结果。在本文中,您将了解以下主题:
如何预处理交叉验证结果以便在 plotnine 中绘图
- 多维数组到平面数据帧
- 对分类数据进行排序以影响绘图中的绘图顺序
- 将列
MultiIndex
展平到仅一个索引级别
如何使用 plotnine 生成图
- 堆叠多个几何图形
- 对不同的几何图形使用不同的数据
- 使用自定义标签功能
在继续构建诊断图之前,我们将从对案例研究的简单介绍开始这篇文章。
分类鼓形样品
人类真的很擅长识别声音。根据一种声音,我们可以识别附近是否有鸟或熊。非常熟练的听众甚至能认出他们听到的鸟的类型。将这种技能转移到数学模型中并不简单。在本文中,我们将重点放在识别鼓的声音上:特别是底鼓(或低音鼓)、小鼓和小军鼓。每个声音都存储在一个 wav 文件中,例如:
**
我们可以看到左右声道的振幅随时间的变化。为了训练模型,我们需要采取一些步骤:
- 将数据读入内存
- 基于每个鼓样本构建特征
- 使用交叉验证来确定正则化强度
GitHub 上的[generate_drum_model.py](https://github.com/PaulHiemstra/lasso_tsfresh_article/blob/master/generate_drum_model.py)
脚本执行所有这些步骤,并将结果转储到 disc。在下一节中,我们将获得交叉验证结果,并构建一些有用的诊断图。
诊断图
诊断图的基础是使用[LogisticRegressionCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html)
生成的交叉验证结果。它们与原始训练数据一起被输入到诊断图中。我们可以从文件中读取这些,因为它们已经通过运行[generate_drum_model.py](https://github.com/PaulHiemstra/lasso_tsfresh_article/blob/master/generate_drum_model.py)
生成:
交叉验证对象的主要结果是选择的正则化强度,存储在C_
属性中:
注意,我们将这里的C_
属性转换为来自 sklearn 的LogisticRegressionCV
与正则化强度相反。我使用 Lasso 使用的𝛼α定义,它等于1/2C
。我添加了对数转换,使数字更易读。
基于cv_result
,我将构建两个诊断图:一个关注模型的系数,另一个关注模型的整体性能。在这两种情况下,我将首先展示情节,然后慢慢分解制作情节所需的每个步骤。
图 A:系数的发展
在交叉验证过程中,我们使用了 L1 范数,这意味着随着正则化强度的增加,模型中的系数被推至零。如果你不清楚前面一行说了什么,我建议你读一下我写的关于公司的文章,这篇文章详细解释了统计数据。
为了直观显示这种效果,我们使用 GitHub 上的helper_functions.py
文件中的plot_coef_paths
绘制了系数值与正则化强度的关系图:
在这里,我们可以看到kick
模型的系数如何随着正则化强度的变化而变化。请注意,图中有五条线,每条线对应一个交叉验证折叠。
在接下来的部分中,我将展示生成上面的图所需的每个步骤。
步骤 1:宽到长的数据
该图的主要数据源是LogisticRegressionCV
对象的coef_paths_
属性。该属性存储一个字典,其中包含模型可以预测的每个可能类别的条目:在我们的例子中是 kick、snare 和 tom。字典中的值是[numpy.ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html)
对象,存储所有的系数值。ndarray
的形状是:
对于kick
模式。第一个轴是交叉验证折叠,第二个轴是正则化强度,第三个轴是特征。
Plotnine 不与ndarray
对象一起工作,但需要一个长格式,其中每一行由单个系数值及其相关的正则化强度、系数名称和模型组成。基于ndarray
生成这种长格式数据帧的一种非常有效的方法是将整个ndarray
展平成一列,并添加一个MultiIndex
,显示系数属于哪个折叠、正则化强度和特征。
首先,我们使用reshape
生成单列数组,它展平为(number of samples, 1 column)
:
接下来,我们使用[pd.MultiIndex.from_product](https://pandas.pydata.org/docs/reference/api/pandas.MultiIndex.from_product.html)
生成适当的MultiIndex
:
注意,我们传递给pd.MultiIndex.from_product
的嵌套列表中的大小与多维数组的形状完全匹配。这是因为index
中的每个元素都包含了single_column_array
中相应元素的元数据。元数据列表index
中的每个元素包含三个元素(如index[0]
)。它们分别是元数据元素折叠、正则化强度和特征名称。
现在我们可以将单列数组与MultiIndex
组合成一个DataFrame
:
产生的DataFrame
有四列。前三个是来自MultiIndex
的元数据,最后一个是来自单列数组的系数值。
注意我们使用.reset_index()
,因为我们需要索引中的列作为绘图的普通列。在 github 的[helper_functions.py](https://github.com/PaulHiemstra/lasso_tsfresh_article/blob/master/helper_functions.py)
中,你可以找到执行这个操作的ndarray_to_long
函数。
步骤 2:将 C 转换为α并应用 log
sklearn 中的逻辑回归函数与正则化强度相反。然而,我发现直接使用正则化强度更直观。在这篇文章中,我用𝛼定义的正则化强度,等于1/2C
。此外,我对这些值进行了对数变换,以提高图的可读性,因为为正则化强度选择的值遵循对数分布。更多细节,我参考 Github 上的[generate_drum_model.py](https://github.com/PaulHiemstra/lasso_tsfresh_article/blob/master/generate_drum_model.py)
脚本。
第三步:丢弃不相关的特征
使用 tsfresh,我们为模型生成了 328 个特征。在一个图中绘制所有这 328 个特征,包括它们的系数的发展,简直太多了。因此,我们只选择平均系数大小高于某个阈值的系数。选择这个截止值需要一点反复试验。这里我选择了一个截止值1e-8
:
注意abs(coef_value)
的使用,它确保我们也包括具有负系数的特征。如果你为自己的分析绘制图表,你可能想要试验截止值的精确值。
步骤 4:从高到低系数值对特征进行排序
在这个图中,我们为每一个可能的特征使用了一个子图网格。我们希望最重要的功能放在网格的左上角,因为这是大多数用户最先关注的地方。此外,我们希望特性的重要性决定进一步从左到右和从上到下的排序。我们将特征的重要性定义为其系数的平均绝对值。
为了强制 plotnine 使用这个顺序,我们需要对 feature names 列进行排序。我们使用pd.Categorical
函数来实现这一点:
其中通过查看每个特征(.groupby(['feature_name']
)的绝对值(.abs
)平均值(.mean
)系数值来进行类别排序。实际上,对值的排序是使用.sort_values
完成的。还要注意,我们使用.index
来返回特性名称,而不是排序后的系数值。基于从重要到不重要排序的系数名称,我们构建分类变量。这里要记住的重要一点是plot_data['feature_name']
中的实际值是不排序的:元数据中的类别顺序是排序的。
步骤 5:计算数据的最小值/最大值
在图中,我们使用了一组线,交叉验证中的每个折叠一条线,以及一个覆盖线的最小值和最大值的带。所以每个面上有一条带子和五条线。
在准备geom_ribbon
时,我们需要预计算最小值/最大值:
请注意,.agg
会在列上产生一个MultiIndex
。为了在plotnine
中绘图,我只想有一个扁平的列名列表。为了使MultiIndex
变平,我使用:
在这里我用一个_
连接了MultiIndex
中的所有关卡。比如('coef_value', 'min')
展平为coef_value_min
。
最后,我去掉了行索引,因为我希望这些列可以在plotnine
中使用:
步骤 6:在 plotnine 中打印
现在我们已经准备好了所有的数据,我们可以绘制图表了:
请注意,我:
- 使用两种几何图形:基于数据最小值和最大值的带状几何图形,以及交叉验证中各个折叠的线几何图形。
- 带状几何图形有自己的数据(
plot_data_min_max
),不使用主绘图数据(plot_data
)。这很有用,因为带状几何体不使用每个折叠的所有原始数据,而只使用数据的最小值和最大值。 - 在线上使用 0.3 的 alpha。这确保了线条不会主导情节,我们可以发现重叠的线条。
- 在
facet_wrap
中使用自定义标签功能。由 tsfresh 生成的特性名称非常大,大到无法放入一个方面的标题框中。为了缓解这个问题,我使用了一个自定义标签函数:helper_functions.py
的abbreviate_label
。这将使用...
将绳子的头和尾连接起来。这大大提高了面标签的可读性。 - 使用
( )
左右代码。这是为了不用每次都包含\
就能使用换行符。
图 B:总体绩效与正规化强度
在前面的图中,我们看到了特征的系数值如何随着正则化强度而变化。在第二个情节中,我们关注情节的整体表现。在这种情况下,性能意味着预测的准确性,即模型预测正确的钢轮类型的时间比例。我们使用helper_functions.py
中的plot_reg_strength_vs_score
函数绘制该图:
请注意,精确度涵盖了每个正则化强度的一系列值,因此使用了geom_ribbon
。拥有一个值范围源于拥有交叉验证中每个折叠的结果。从图中可以明显看出kick
型号性能最好,tom 型号性能最差。接下来我们分解这个情节,告诉你如何制作。
从宽到长
正如前面的图一样,我们需要将数据转换为 plotnine 所需的长格式。为此,我们使用与[helper_functions.py](https://github.com/PaulHiemstra/lasso_tsfresh_article/blob/master/helper_functions.py)
相同的ndarray_to_long
函数,例如对于kick
型号:
请注意,我们这里有两个轴:交叉验证折叠和正则化强度。
接下来,我们希望将所有三个模型的数据合并到一个大的数据框架中。为此,我们构建了一个助手函数,它为每个子模型使用ndarray_to_long
,并添加子模型的名称。接下来,我们使用 list comprehension 和 helper 函数来获取所有子模型的数据。最后,我们使用pd.concat
将所有子模型数据粘合成一个大的DataFrame
:
在我看来,这种列表理解和pd.concat
的结合是一种非常有效的技术。
步骤 2:将 c 转换为𝛼并应用 log
与前面的图一样,我们转换了正则化强度:
步骤 3:计算数据的最小值/最大值
作为geom_ribbon
的输入,我们需要最小/最大精度数据:
步骤 4:在 plotnine 中打印
现在我们已经将所有数据排列好了,我们可以使用 plotnine 绘制图表:
请注意,我同时使用了带和段几何图形。使用两种几何图形是因为我发现这样绘制的图形可读性更好。
承认
我要感谢伯恩哈德·特拉克为本文的草稿提供了宝贵的反馈。
Plotnine 情节解构:使用等级情节可视化公告牌 100 大热门
如何使用 Python、Pandas 和 Plotnine 高效地可视化较长时间范围内的等级
这是我写的一系列文章中关于用 Python 制作朱朱情节的一部分。
介绍
就我而言,ggplot2 是最好的绘图软件。所以当我发现有一个针对 Python 的图形语法的实现时,我抓住了这个机会。ggplot2 buffs 过渡到 plotnine 应该没问题,语法很像。
图形语法的优势在于对情节推理的一致性。这种一致性使得将情节想法表达成代码变得容易。与 Seaborn 尤其是 matplotlib 相比,plotnine 更容易使用。然而,习惯 plotnine 的语法和思维方式确实需要一点时间,尤其是在标准散点图和条形图之外的时候。
本文的目标不是提供 plotnine 教程,而是探索更高级的 plotnine 概念。如果你需要的话,这篇文章的确提供了很好的介绍。这些是我将在本文中讨论的主题:
- 数据的智能选择
- 对绘图中的不同图层使用不同的数据源
- 巧妙重新排列文本标签以避免标签重叠
- 使用刻面技术水平堆叠时间序列数据
这篇文章的核心是以下情节:
2015 年公告牌 100 首热门歌曲中的前 15 首
该图显示了一首歌曲的图表位置(y 轴)在整个 2015 年的 Billboard Hot 100 轴)中如何变化。一条彩色线代表歌曲的轨迹,歌曲的标题由文本标签给出。红色箭头有助于识别哪个标题属于哪个行。解读剧情的最佳方式是挑选一首特定的歌曲,沿着彩色的线去探索它在一年中的轨迹是如何演变的。我从一本关于制作情节的教科书中获得了这个情节的想法,尽管我忘了是哪一本。在网上我可以找到类似的图这里和这里。
在文章的其余部分,我将分解这个情节是如何构建的,以及我做出了哪些选择以及为什么做出这些选择。我假设您熟悉 ggplot2 或 plotnine、Python 和 Pandas 的语法基础。完整的 Python 笔记本可以在这里找到。
情节的分解
为了生成该图,我使用了 Billboard top 100 的以下数据集:
它包含以下列:
数据集中的每一行都是某首歌曲在特定一周的排行榜位置。与特定歌曲相关的所有行代表该歌曲的轨迹。我们的图的重要列是:
- WeekID,每周图表位置的时间戳
- 周位置,实际图表位置
- 宋,宋的名字
以下代码获取这些数据,执行一些预处理并生成绘图:
智能数据选择
在代码的第一部分(第 4-10 行),我对绘图数据进行了预处理。这里我做了两个重要的选择:
- 我只使用前 100 名中前 15 名的数据
- 我会丢弃那些只在数据中出现了 4 周或更短时间的歌曲
通过从数据中删除不太重要的歌曲,我们减少了混乱,让我们更容易专注于 2015 年的最佳歌曲。
为了丢弃这些罕见的歌曲,我首先计算一首歌曲在数据中出现的频率:
这是使用groupby
和count
的组合来完成的,两者都是熊猫函数。计算完计数后,我选择出现次数超过 4 次的歌曲(第 2 行),并使用这些歌曲作为热门 100 首歌曲的子集。
使用不同的数据源
在我们的情节中,一首歌的标题只显示一次,而不是像geom_text
默认的那样显示在数据集中的每个点上。为了达到这个效果,我们需要为geom_text
使用一个单独的数据源。
我选择了一首歌在排行榜上的最高位置来放置歌名。我使用以下代码构建了数据:
在自定义函数 highest_rank 中,我选择了Week Position
中最小值的索引。注意,最小值是最高等级。接下来,我选择最高等级的Week ID
和Week Position
并返回它们。注意,我们不能简单地使用groupby
和min
( plot_data.groupby([‘Song’]).min()
),我们需要属于最高图表位置的Week ID
和Week Position
在正确的位置绘制文本标签。最后,我们将函数应用于每首歌曲的数据。现在我们有了数据,我们只需要改变geom_text
的数据源,每首歌只打印一次标签。
每个 geom 图层使用不同的数据源是一个强大的技巧。例如,通过这种方式,您还可以仅通过提供引用这些面的数据,仅在绘图中所有面的子集内绘制一些几何图形。
智能地重新排列文本标签
通过减小文本标签的字体大小和限制标签的数量,我们已经消除了文本标签中的许多潜在重叠。但这种情况仍有可能发生。为了完全消除这种重叠,我使用了geom_text
的adjust_text
参数。这使用[adjustText](https://github.com/Phlya/adjustText)
包智能计算标签的位置。以下代码设置了一些参数,并将其传递给geom_text
:
expand_text
控制标签在 x 和 y 方向可以移动的距离,而arrowprops
控制从标签到其所属点的箭头外观。
时间序列的堆叠
到目前为止,我们只处理了一年的数据。要将其扩展到多年,我们可以使用facet_wrap
将这些图堆叠起来:
这种方法相对于简单地绘制多个单独的图的优势在于,可以保证颜色在多年内保持不变。上面的图是使用以下代码生成的:
前面代码中需要进行的更改用注释标记。这些变化是:
- 显然,选择多个年份的数据。**
- 使用年份变量扩展数据,在为标签生成最高等级时,也不要忘记包括年份。请注意,在
groupby
中包含“年份”可确保标签在每年的最高级别绘制。这对跨越多年的歌曲很有帮助。 - **跨年度变量facet _ wrap。记住使用
scales=’free_x’
专门绘制每个方面的时标。
我很喜欢 plotnine 允许我用代码表达我的可视化想法的方式。尤其令人高兴的是,表现力扩展到了 plotnine 最复杂的部分。希望你学会了几个新招!
策划一条摆脱危机的道路
处理数字不足以将数据转化为行动
在上周我在网上举办的关于透视人工智能的 SwissCognitive 会议上,我研究了屏幕右侧几十名观众的脸。尽管在我的演讲中,他们的声音很小,但每个人都向我提供了他们过去经历的线索,对他们如何度过当前危机的见解,以及他们对未来的展望。
当前新冠肺炎疫情的社会和经济后果为我们提供了一个“决定性的时刻”,我们可以选择改变历史的进程。不确定时期的决策需要超越数字来理解数据是如何构建的,我们是如何定义视野的,以及在什么条件下人工智能可以增强人类的智能。
视角的重要性
人的视角是数据和行动之间缺失的一环
像许多过去几周呆在家里的人一样,我每天早上打开电脑,阅读当前疫情影响的统计数据。国际货币基金组织上周预测,欧元区的年增长率将下降 7.5%,而美国的年增长率将下降 5.9%。联合国贸易和发展机构得出结论,冠状病毒的爆发可能会给全球经济造成至少 1 万亿美元的损失。预计仅在未来三个月就将裁员近 2 亿全职员工。无论你选择哪一个数字,我们都需要问它们在多大程度上反映了现实,更重要的是,它们在多大程度上代表了行动的号召。
在接下来的几周里,我们将选择闭上眼睛,一切照旧,或者抓住机会改变我们看待周围世界的方式。鉴于当前危机带来的不确定性,我们如何做出更好的决策,以扩大我们对替代未来的展望?分析目前的病例、死亡人数或工作岗位最多只是一个开始。如果人工智能(机器学习,有限人工智能)预测未来的能力取决于过去经验的相关性,那么抓住决定性时刻取决于我们重新构建我们所见的能力。
为什么决策者需要超越数据,从更广阔的角度来看待替代未来?如果机器学习有助于我们识别当前危机中的模式,那么理解例外就取决于人类的睿智。在大约 700 年前的黑暗时代的危机中,莱昂·巴蒂斯塔·阿尔伯蒂在 De pittura 中认为,将数字纳入视角取决于我们的参考框架,我们如何绘制现在和未来之间的地平线,并推动我们的观众将数据转化为行动。让我们来探讨一下今天把数据放在一个角度来看意味着什么。
数据
“数据的相关性取决于使用它的上下文”
当前社会和经济危机的数字到底意味着什么?今天的数字是否为我们提供了预测未来的方法?数据本质上是现实的近似值;数据科学的目标不是提高数字,而是唤起个人和集体的行动。数字的精确性、粒度和准确性有助于我们分析过去、描述现在和预测未来的能力。在随机决策环境中,受众对数字的信任对于预测和影响未来都至关重要。
从这些数字中学习,首先要质疑它们在多大程度上代表了我们所面临的危机的性质。阿尔贝提从观察者的角度分析了前景——对于决策者来说,数字代表了什么,更重要的是,它们如何影响行为?今天,符号学家在三个不同的层面上研究数字的意义。语义指的是数字和它们应该代表的事件之间的关系的质量。Syntactics 分析每个职业、组织或市场中的人物之间的一致性。最后,语用学探索了数字与使用它们的消费者、经理或股东的行为和反应之间的关系。数据从来都不是客观的,事实是有后果的。
图片来源:CSSE,约翰·霍普斯金大学
2020 年 5 月中旬,全球有 440 万例新冠肺炎病例,欧洲 GDP 下降 7.5%,美国失业率接近 15%。然而,疫情最大的后果不在于数字本身,而在于在描绘走出危机的道路时围绕数字的风险、模糊性和不确定性。当查看统计数据时,我们需要问它们是如何计算的,它们的置信区间是多少,以及它们如何代表行动的号召。在匆忙赶新闻和报道周期的最后期限时,我们放大了数字,却没有审视已被证明不适应应对危机的更大的社会和经济进程。
框架
价值和价值观从来不是在数据本身中找到的,而是在数据是如何构建的
当我们处理数据集时,我们处理的是为了特定目的而拼凑起来的数据。阿尔贝提认为,框架在人类的视角中起着重要的作用,它帮助我们关注框架内相对于框架外重要的东西。在分析中,框架用于将数据置于上下文中,建议记录之间的关系,并帮助理解如何在实践中利用数据集。元数据是描述捕获数据的基本条件、获取数据的方式、准确性和编译方法的信息框架。数据分类法或命名系统是语义框架,用于将数据分为反映贸易或业务逻辑的类别和子类别。本体是知识的模型,是用来识别概念和实体之间的属性和关系的框架。
在当前的危机中,发病率和福利、就业和失业以及作为和不作为之间的区别取决于我们如何构建数据。元数据、分类法和本体论不仅决定了我们如何构建这些概念,还决定了我们从每个数据集得出什么类型的结论。这些框架,而不是数据本身,帮助我们决定哪些模式、变量和属性与解决所研究的问题相关,哪些属于其他背景和经验。在人类和“自动化”决策中,框架在三个特定层面上起作用:作为数据是什么的思维模式,作为一套应该如何解决问题的信念,以及作为检测违反传统组织实践的离群值的基准。
图片来源:克里斯托弗·巴尔
世界范围内新冠肺炎病例数量、欧洲 GDP 下降和美国失业率的重要性取决于我们用来获取和限定数据的框架。尽管卫生条件、经济衰退和社会动荡有时会为变革提供动力,但它们总是产生变革的社会和经济体系的表征。变革管理的支持者认为,重塑我们对常态的认知,而不是处理数字,是帮助社区和经济发展的必要条件。这些“激进的框架”,或一系列可供选择的价值观和兴趣,允许决策者使用数据以不同的方式思考未来。我们对福祉、繁荣和就业的定义在今天是否仍然有意义,或者这场危机是否会促使我们重新思考如何在今天和可预见的未来利用数据来煽动集体行动?
地平线
因为世界不是平的,绘制从现在到未来的路线图需要的不仅仅是将点点滴滴连接起来
未来会怎样?在发展我们对我们的企业和我们的社区的观点时,我们固定了一个思维视野,将我们现在所看到的与我们视线之外的未来挑战区分开来。对阿尔贝提来说,视野相当于人类感知可以归纳或推断的界限。今天,我们通过使用试探法和算法定义地平线上的点来形成我们的观点。我们根据具体的决策环境,在感知、预测、评估和洞察力方面利用人类和/或机器智能。然而,我们对风险的认知、偏见和信念给我们的视野蒙上了一层阴影。超越地平线始于认识到阻碍我们观点的障碍。
可惜,人生不是一条直线。在现实世界中,我们在应对复杂多变的决策环境时面临着艰难的战斗。数据、算法和业务逻辑中隐含的偏见限制了我们对所见和所做的感知。语义漂移已经改变了构成我们使用的数据的福祉、生产力和价值的概念。对风险、不确定性和模糊性的认知阻碍了我们承担将数据转化为行动的责任。我们对人工智能投资的渴望让我们看不到开发人类智能其他维度(情感、人际甚至精神)的重要性。最后,我们关于可接受的数据实践的道德框架决定了我们如何使用数据来激励个人和集体行动。
图片来源:iStock
在我们今天所面临的系统性危机中,对疫情的卫生、经济和社会影响的理解让决策变得复杂。Covid 测试的相关性,社会追踪的适当性,以及个人自由和集体福祉之间的平衡的机会都是公开的问题。决策环境即使不是混乱的,也是复杂的——因为原因和解决方案都是未知的。我们对福祉、生产力和价值的构建受到围攻,受到我们生活环境的挑战。即使在深度学习的背景下,算法概率也只能证明与我们过去的经验一样相关。难怪我们许多人可能只是盯着数字,即使这意味着完全看不见地平线。
神谕的作用
人工智能本身解决不了问题,人来解决
在实践中,机器学习产生了新的数据点,这些数据点可能会降低决策中的风险、不确定性和模糊性。可以利用数据来提高我们在以下三个方向做出更好决策的能力。放大是指在聚焦一组事件时,一个视角的澄清和应用。沟通涉及在解决一个问题时将两个或两个偶然但不相关的愿景联系在一起。扩展代表着努力扩大视角的边界,以包含更大社区的观点、兴趣或情感。然而,正如彼得·沃森(Peter Wason)关于确认偏差的工作令人信服地证明的那样,数据经常被解释为仅仅支持长期存在的信念。
AI 在什么条件下可以丰富人类的智力?推动、激励和激励决策者根据他们看到的数据采取行动的需求是一个由来已久的问题。在阿尔贝提研究的文艺复兴前绘画的例子中,经常有一只手、一只鸟或一个符号从画面中脱离出来,伸向观众。这些“神谕”象征着对行动的召唤,提供了一条超越框架的视觉线,邀请观众去看艺术家对未来的愿景。如今,数据和行动之间的这种联系在我们的数据集、电子表格和预测中明显缺失,这让许多个人和组织无法将数据应用到自己的组织环境和经验中。
图片来源:亨利·卡蒂埃·布列松-海耶斯
当前疫情面临的挑战不是拉平曲线,而是推动我们的社区走出危机。关于危机可能造成的社会和经济后果的数据本身并不说明问题,而是体现在我们围绕数据讲述的故事中,或者体现在那些负责产生结果的人的技能和能力中。回归、分类、聚类和异常检测中使用的算法可以提供新知识的来源,但开发人类智能需要使用这些结果来绘制不同于我们进入危机时的路径。激励个人和组织沿着这些道路果断行动将取决于发展人类的视角,将数据置于背景中,理解如何构建现在,关注未来,并重构未来。
提高人类智力
描绘一条走出危机的道路不会在网上完成,而是要积极果断地从我们今天所处的位置向我们明天想要达到的位置前进
当我完成了关于人工智能在丰富人类智力方面的作用的在线讨论时,我最后看了一眼屏幕右侧观众的客串。从他们的眼神和问题中,我了解到他们过去经历的线索,了解到他们如何过现在的生活,以及他们如何准备危机后的生活。我知道,仅仅展示卫生、社会和经济危机的数据不足以揭示机器和人类智能能够抓住这一决定性时刻的条件。我的结论是,正如我们在关于人工智能的管理挑战的培训中所做的那样,绘制一条通向未来的道路需要掌握正确看待数据的技巧。
商业分析研究所(http://baieurope.com)提供高管培训、辅导和指导,以应对人工智能、数字伦理和“聪明公民”发展的管理挑战。
Lee Schlenker 是 BAI 的负责人,也是商业分析和社区管理教授。他在 LinkedIn 上的个人资料可以在查看
使用 Seaborn 绘制图表
使用 Python 库创建吸引人的图表。
Seaborn 是一个强大的 Python 库,旨在增强数据可视化。它为 Matplotlib 提供了大量的高级接口。Seaborn 可以很好地处理数据帧,而 Matplotlib 不行。它能让你以更简单的方式绘制出引人注目的图表。
为了更好地理解这篇文章,你需要了解熊猫和 matplotlib 的基础知识。如果没有,你可以参考以下关于同一的文章:
- 用于数据分析的熊猫
- 【Matplotlib 可视化
确保您的系统中安装了必要的库:
使用 conda:
conda install pandas
conda install matplotlib
conda install seaborn
使用画中画:
pip install pandas
pip install matplotlib
pip install seaborn
让我们首先导入所需的 Python 库和数据集。
你可以在这里找到这个教程的 CSV 文件。
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as snsdf = pd.read_csv('Pokemon.csv', index_col = 0, encoding='unicode-escape')
df.head()
作者图片
在上面的代码中,我们将 index_col 设置为 0,这表示我们将第一列视为索引。
使用 seaborn 和口袋妖怪数据集的属性,我们将创建一些非常有趣的可视化效果。我们首先要看的是散点图。
散点图
散点图使用点来表示不同数值变量的值。水平轴和垂直轴上每个点的位置表示单个数据点的值。它们用于观察变量之间的关系。使用“lmplot”函数在 seaborn 中制作散点图只需要一行代码。为此,我们将 dataframe 传递给 data 参数,然后传递 x 轴和 y 轴的列名。
默认情况下,散点图还会显示一条回归线,这是一条最符合数据的直线。
sns.lmplot(x=’Attack’, y=’Defense’, data=df)
plt.show()
作者图片
在这里,您可以看到我们的散点图,它显示了攻击得分与防御得分的对比。
回归线基本上向我们展示了两个轴之间的相关性。在这种情况下,它向上倾斜。这意味着当攻击得分变高时,防御得分也变高。要删除回归线,我们可以将' fitreg '参数设置为 false。
此外,我们可以设置色调参数,根据口袋妖怪的进化阶段给各个情节着色。这个色调参数非常有用,因为它允许您用颜色表达第三维信息。
sns.lmplot(x=’Attack’, y=’Defense’, data=df, fit_reg=False, hue=’Stage’)
plt.show()
作者图片
散点图看起来与之前的一样,只是现在它的中间没有回归线,而且每个点的颜色也不同。这些颜色只是显示了每个口袋妖怪的阶段。阶段只是我们之前看到的数据中的另一个属性。
看这个情节,我们可以得出结论,第一阶段的口袋妖怪或者蓝点通常比更高阶段的口袋妖怪得分低。
箱线图
箱线图是常用于显示数据分布的重要图之一。在 seaborn 中,使用 boxplot 函数显示一个 boxplot 只需要一行代码。在本例中,我们将使用整个数据帧,除了 total、stage 和 legendary 属性。
df_copy = df.drop([‘Total’, ‘Stage’, ‘Legendary’], axis=1)
sns.boxplot(data=df_copy)
作者图片
这里我们可以看到每个属性都有自己的箱线图。
箱线图基于 5 个数字的汇总,每个数字显示为不同的线条。中间的线是中间值,是数据集中的点。朝向箱线图末端的最下面和最上面的线是四分位数 1 和 4 的中间值,其基本上显示了分布的最小值和最大值。中间的另外两条线是四分位数 2 和 3 的中间值,显示了这些值与中间值的差异。超出此范围的单点表示数据中的任何异常值。
小提琴情节
小提琴图类似于箱线图。小提琴图是箱线图非常有用的替代品。他们显示了小提琴厚度的分布,而不仅仅是汇总统计数据。众所周知,Violin plots 在分析和可视化数据集中不同属性的分布时非常方便。
在本例中,我们将使用与上例相同的数据帧拷贝。
sns.violinplot(data=df_copy)
plt.show()
作者图片
我们可以观察口袋妖怪的每个属性的值的分布。小提琴较厚的区域意味着价值密度较高。小提琴图的中间通常较粗,这意味着那里有高密度的值。接下来,我们将可视化的攻击分数的分布比较口袋妖怪主要类型。要做到这一点,让我们使用相同的小提琴绘图方法。
plt.figure(figsize=(10,6))\sns.violinplot(x='Type 1', y='Attack', data=df)
plt.show()
作者图片
该图显示了每个口袋妖怪主要类型的攻击得分分布。正如你所看到的,“龙”型口袋妖怪有最高的攻击分数,但他们也有较高的方差,这意味着他们也有攻击分数很低。“重影”主要类型具有非常低的方差,这意味着它们的大部分数据值都集中在中心。
热图
热图帮助您可视化矩阵类型的数据。例如,我们可以将口袋妖怪的不同属性之间的所有相关性可视化。
让我们通过调用“corr”函数来计算数据帧的相关性,并使用“热图”函数绘制热图。
corr = df_copy.corr()
sns.heatmap(corr)
作者图片
上面的热图显示了我们的数据框架的相关性。
方框的颜色越浅,这两个属性之间的相关性越高。比如一个口袋妖怪的 HP 和整体速度的相关性很低。因此,黑色的盒子。血量和防御速度之间的相关性相当高,因此我们可以在热图中看到红色块。我们可以看到,当一个属性变得更高时,其他属性也变得更高,例如防守速度。
直方图
直方图允许您绘制数值的分布。如果我们使用 matplotlib 来创建直方图,与使用 seaborn 创建直方图相比,需要更多的工作。使用 seaborn,只需要一行代码就可以创建一个发行版。
例如,我们可以创建一个直方图来绘制攻击属性的分布值。
sns.distplot(df.Attack, color=’blue’)
作者图片
我们可以看到大多数口袋妖怪都在 50-100 的范围内。这里我们看到的攻击值大于 100 或者小于 50 的口袋妖怪少多了。
校准图
与条形图类似,calplots 可以让您直观地看到每个类别变量的分布。我们可以使用 calplot 来查看每个主要类型中有多少个口袋妖怪。
sns.countplot(x=’Type 1', data=df)
plt.xticks(rotation=-45)
作者图片
我们可以看到,“水”类的口袋妖怪最多,而“精灵”类和“冰”类的口袋妖怪最少。
密度图
密度图显示两个变量之间的分布。例如,我们可以使用密度图来比较口袋妖怪的两个属性:攻击值和防御值。我们将使用“jointplot”功能来完成这项工作。
sns.jointplot(df.Attack, df.Defense, kind=’kde’, color=’lightblue’)
作者图片
“kde”表示我们想要一个密度图。
如您所见,绘图区的黑暗程度取决于该区域中有多少个值。黑色区域代表着非常强烈的关系。从这个图中我们可以看到,当攻击值在 50 到 75 之间时,防御值在 50 左右。
这篇文章大概就是这样。我希望您喜欢使用 seaborn 可视化数据。
你可以在这里找到这篇文章的代码和数据集。
谢谢你阅读它!
参考
1 Seaborn 文件:【https://seaborn.pydata.org/
用 Folium 在 Python 中绘制 Google Sheets 数据
使用 Flask 在 web 应用程序上实时绘制 Google 工作表数据
在这段代码中,我们将尝试模拟世界各地的志愿者通过 Google Sheet 报告地震的工作流程,数据将在 web 地图上实时更新。
将使用用于 Python 的 Flask web 框架创建 web 平台,并使用优秀的leaf库绘制数据,该库是用于leaf JS 的 Python 包装器。将使用gspread库从谷歌工作表中检索数据,该库是谷歌工作表 API 的 python 包装器。
1900-2017 年世界地震地图(来源:维基共享资源)
这段代码应该可以在 Linux 和 macOS 系统上正常工作。对于使用 Windows 系统的用户,只需要修改终端中正在运行的命令。
设置虚拟环境并安装必要的软件包
- 创建项目文件夹:
mkdir flask_webmap
cd
进入文件夹并设置虚拟环境- 我们将使用 virtualenv 作为我们选择的虚拟环境工具。在项目目录中运行
virtualenv venv
,创建一个名为 venv 的环境 - 通过运行
source venv/bin/activate
激活环境 - 通过运行
python -m pip install -U Flask folium gspread
安装所需的模块
向 Google 注册我们的应用程序以使用 Google Sheets API
要访问来自 Google sheets 的数据,我们需要向 Google Sheets API 注册我们的应用程序。为此,请遵循以下步骤
- 从网络浏览器打开这个谷歌控制台链接
- 在 Google 控制台仪表板中,单击顶部栏中的新项目链接
- 给你的项目命名,然后点击创建
- 点击启用 API 和服务
- 在打开的列出各种可用 API 的新页面中,选择 Google Sheets API 并启用它
- 启用 API 后,单击出现的创建凭证警报
- 填写必要的细节:对于问题选择 Web 服务器您将从哪里调用 API?
- 在下一部分中,输入服务帐户的任何名称。角色选项可以留空,但选择 JSON 作为您的密钥类型,然后单击继续
- 将下载一个包含您的凭证的新 JSON 文件
- 将 JSON 文件复制到项目目录中
设置一个最小的烧瓶应用程序
-
通过运行
touch app.py
创建一个新的 Python 文件 -
在您选择的编辑器中打开 python 文件,并创建一个最小的 Flask 应用程序
-
在您的终端中,通过运行
export FLASK_APP=app
导出 Flask app 环境变量 -
默认环境设置为生产。通过运行
FLASK_ENV=development flask run
将此更改为开发 -
运行
flask run
启动应用程序 -
新的应用程序现在应该可以在 localhost:5000/ 获得
-
该页面应该打印“Hello World”消息
用种子数据创建 Google 表单
访问 Google 工作表数据
使用 gspread 库访问这些数据
- 创建一个新的 python 文件:
touch read_sheet.py
- 并复制以下代码
理解代码
- 首先,我们定义授权的全局变量。用从 Google API 控制台下载的 JSON 文件的名称替换“带凭证的 JSON 文件的名称”。
- 然后,我们打开 google 工作表,使用它的 URL 存储数据
- 接下来,我们使用索引位置获取存储数据的特定工作表。第一页索引为“0”
- 最后,我们定义了一个新函数,该函数从特定的工作表中获取所有记录并将其返回
测试 Google 工作表访问
-
重新打开 app.py 并用以下代码替换代码
-
flask run
从您的终端 -
localhost:5000 现在应该返回数据的 JSON 表示
绘制地图
现在我们已经验证了对 Google Sheets 数据的访问,让我们在地图上绘制数据。
touch plot_map.py
从您的终端- 打开 plot_map.py 文件,复制以下代码
理解代码
- 首先,我们定义了 basemap_layer 函数,它使用
folium.Map()
绘制了一个基本地图。除了设置为 2 的 min_zoom 属性外,地图设置为使用所有默认值 - 接下来,我们使用
folium.Marker()
定义一个函数来绘制地震位置,并将其添加到上一步生成的父地图中
从 Flask 应用程序渲染地图
打开app.py
文件并复制以下代码
理解代码
- 从之前创建的 plot_map.py 文件中,我们导入了 basemap_layer 和地震 _plot 函数
- 首先在 flask 应用程序视图中,我们通过调用
basemap_layer()
函数来创建地图 - 对于步骤 2 中生成的地图,我们添加了通过调用
earthquake_plot()
函数生成的地震位置。步骤 2 中的地图和地震数据作为参数提供 - 地图在模板文件夹中保存为map.html**
- 最后,我们返回index.html**
创建 index.html 文件
mkdir templates
和touch templates/index.html
- 打开index.html并复制以下代码
理解代码
- 我们在模板文件夹中创建index.html文件
- 在 HTML 样板文件的主体中,我们包含了使用 Jinja 模板引擎从 flask 视图生成的map.html**
Flask 应用程序生成的地图视图
该地图如何帮助绘制众包数据?
此应用程序的有趣之处在于,由于映射函数是从 Flask 视图中调用的,因此每次在浏览器中刷新 web 地图时,都会对数据进行 API 调用。因此,任何添加到 Google sheet 的新记录都由应用程序在页面刷新时实时呈现。
所以让我们试试这个功能。我们都知道 2015 年的尼泊尔地震。让我们更新工作表中的数据。
要查找尼泊尔的坐标,请访问 latlong.net 并在搜索框中输入尼泊尔。将纬度和经度值复制到 Google 工作表中。
维基百科也暗示这次地震的震级为 7.8 级。因此,用这个值填充幅度列。
现在返回地图视图并刷新页面。您会发现新位置的标记已添加到地图上。
添加新数据后刷新的地图视图
这段代码到此结束。如果你觉得这篇文章很有用,请留下你的赞赏或评论。
原载于 2020 年 4 月 18 日https://www . sourav 90 . in。
用 Geopandas & Geoplot 绘制地理数据
用 Geopandas 和 Geoplot 制作美丽的地图
来源:地图 UI 模式
背景
回到 2019 年 2 月,我一直在一家媒体公司担任研究员。我的工作涉及大量的数据和电子表格来存储和分析。但是对于这样一个快速发展的行业,每天都有数百万行新数据流入,Microsoft Excel 很难完成这项工作。仅仅更新几个电子表格就很容易花费一整个上午的时间,因为在处理大量数据时,Excel 的性能会迅速下降。我想为我的作品学习一些新工具,我在代码学校找到了一个关于 Python 的数据分析课程。对于像我这样有商业背景的人来说,编码就像火箭科学一样,我认为我不会感兴趣。然而,由于 Excel 经常崩溃,我感到沮丧,我还是试了一下,天哪,真是令人兴奋。从那时起我就开始写代码,现在我在我学习第一行代码的学校教书。
我在工作中经常遇到的一个话题就是处理地理数据。在我了解 Python 之前,在地图上绘制数据是一项非常具有挑战性的任务。我们的选择之一是 Tableau 和它的地图功能(来源: Tableau )。然而,Tableau 的一个局限性是,它只能按越南各省绘制数据,而许多项目要求数据按地区显示。
填充多边形选项不适用于越南地区。来源: Tableau 社区论坛
在这方面,Python 提供了更大的灵活性,在地图上绘图时也提供了更多的定制选项。
地质公园&地质公园
Geopandas 和 Geoplot 是两个允许我们处理和可视化地理数据的 Python 库。 Geopandas 基于 pandas 并扩展其特性以允许操作几何数据类型。 Geoplot 同样建立在 matplotlib 之上,用于制图绘图。如果你对熊猫和 matplotlib 不熟悉,我建议你从这两篇文章开始:
一些背景
towardsdatascience.com](/a-quick-introduction-to-the-pandas-python-library-f1b678f34673) [## 使用 Matplotlib 实现数据可视化
数据可视化是商业活动的重要组成部分,因为现在的组织收集了大量的数据…
towardsdatascience.com](/data-visualization-using-matplotlib-16f1aae5ce70)
在这个例子中,我将在 Colab 笔记本环境中运行代码。首先,我们需要安装 Geopandas 和 Geoplot:
安装 geopandas 和 geoplot
导入必要的库:
导入库
世界各国的几何数据在互联网上随处可见。它们有许多不同的格式,其中大部分都受 geopandas 的支持。越南各省的几何数据可在 HCMGIS OpenData 上获得。在这个例子中,我将使用 JSON 格式。 Geopandas 具有函数 read_file 从源中读取几何数据。这将返回一个包含许多列的 Geopandas 数据帧,但是我们只需要关心名称(地区)、省份和几何列。
越南几何数据
注意几何栏中的数据:它们是多多边形格式,代表世界地图上该地区的形状和位置。
我们还需要一些数据来绘制在地图上。我准备了一些. csv 文件形式的示例数据。它包含了越南一些地区的数字数据和他们所属的省份。我们可以用熊猫读文件。
阅读示例数据
这是我们要在地图上说明的数据。
为了在地图上展示我们的示例数据,我们需要将它与几何数据结合起来。这可以通过方法合并来实现。
将示例数据与其各自的几何数据相结合
我们现在可以用 geoplot 制作一张地图。在这个例子中,我制作了一个 Choropleth 地图,其中变量数据(色调)的值由多边形的颜色表示。地质公园有更多地图类型选项,查看他们的文档。
绘制一张 choropleth 地图
这是我们的结果!
这张地图看起来不太好,但我们可以添加一些调整来使它变得更好!
改进情节
把越南的所有区都画在一起可能不是一个好主意,因为越南的区太多了。相反,我们可以按地区细分国家。例如,通过对数据框应用过滤器,我可以只选择四个省份进行绘图。
更改 figsize 参数来配置贴图的大小,更改 cmap 参数来配置用于多边形的调色板。
现在看起来好多了!
我们经常想要做的是给绘图添加一些标签(例如,地区的名称和变量的值)。由于 geoplot 构建在 matplotlib 之上,我们可以使用 plt.text 来完成此操作。但是,它需要标签在图上的位置。方法 centroid 返回多边形中心点的 x-y 坐标,这正是我们需要的!
生成每个多边形的中心点
现在我们可以用 matplotlib 给绘图添加一些标签。
将地区名称及其各自的数据值添加到地图中
注意到右下角那些杂乱的文本簇了吗?就是因为胡志明市的区太小,彼此标签太靠近。一个解决办法是从地图上去掉胡志明市的行政区划。
排除胡志明市的区域标签
我们的最终结果!
geopandas 的另一个非常有用的特性是将多个多边形组合成更大的块。例如,当您希望按省份而不是按地区显示数据时,这尤其有用。 geopandas 具有 dissolve()函数,可以将地区数据行聚合到相应的省份中。
使用 Sum 函数将地区聚合为省份
聚集数据框架
各省现在都在数据框的索引中,所以绘制标签的代码需要做一些小的修改。
我们需要再次生成 x-y 坐标
这些地区现在被分成几个省
结论
Geopandas 和 Geoplot 是两个非常有用的可视化地理数据的 Python 库。他们需要更多的努力,但也允许对情节有更多的控制。
使用 Cartopy 打印地理空间数据
用于制作高质量静态地图的 Python 库
随着大数据时代的到来,也迎来了大地理空间数据时代。研究人员越来越多地获得地理信息,这些信息可以做任何事情,从跟踪濒危物种的迁移模式到绘制该国每一家甜甜圈店的地图。
为了帮助可视化这些信息,python 库 Cartopy 只用几行代码就可以创建专业的可发布地图。构建时考虑了 Matplotlib,它的语法熟悉且易于理解。
简单地图
首先,我们将创建尽可能简单的世界地图。在编写任何繁重的代码之前,应该安装 python 中的 Cartopy 和 Matplotlib 库。
import cartopy.crs as crs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
以上,先决条件库被导入并准备好使用。
# Initialize the figure
figure = plt.figure(figsize=(8,6))# use the Mercator projection
ax = figure.add_subplot(1,1,1, projection=crs.Mercator(())# Add feature to the map
ax.stock_img()plt.show()
有纹理的世界地图。图由作者制作。
接下来的几行代码介绍了创建地图的基本过程:
- 初始化地图,并使用 figsize 参数指定图形的大小
- 添加指定所用投影的子图
- 向支线剧情添加特征
只用几行代码,就创建了一个最小的地图。不幸的是,这张地图相当平淡,所以我们将添加一些额外的功能。
figure = plt.figure(figsize=(8,6))ax = figure.add_subplot(1,1,1, projection=crs.Mercator())# adds a stock image as a background
ax.stock_img()# adds national borders
ax.add_feature(cfeature.BORDERS)# add coastlines
ax.add_feature(cfeature.COASTLINE)plt.show()
有边界的世界地图。图由作者制作。
通过修改变量 ax,更多的信息被添加到地图中。在这种情况下,国界和海岸线被画出来。其他功能,如主要河流和湖泊地图也可以用同样的方法包括在内。完整的功能列表可以在这里找到。
不同的预测
上面的地图使用了著名的墨卡托投影。虽然在大多数情况下完全足够,但墨卡托地图扭曲了土地大小,因为它将一个球体拉伸到一个正方形上。幸运的是,Cartopy 支持其他投影。
figure = plt.figure(figsize=(8,6))# set the projection to Mollweide
ax = figure.add_subplot(1,1,1, projection=crs.Mollweide())
ax.stock_img()plt.show()
莫尔韦德投影。图由作者制作。
通过改变 crs 的投影参数。墨卡托()至 crs。Molleweide(),创建一个不同的、更椭圆(更精确)的投影。
figure = plt.figure(figsize=(8,6))# Set the projection to Interrupted Goode Homolosine
ax = figure.add_subplot(1,1,1, projection=crs.InterruptedGoodeHomolosine())
ax.stock_img()plt.show()
间断古德同亏线投影。图由作者制作。
通过在 projection 参数中使用 InterruptedGoodeHomolosine(),可以产生更精确的投影。
这些绝不是 Cartopy 可以绘制的唯一投影。完整列表可在这里找到。
放大和缩小片段
虽然世界地图在许多情况下都很有用,但通常数据需要在一个局部区域中表示,例如一个大陆或一个国家。
figure = plt.figure(figsize=(8,6))ax = figure.add_subplot(1,1,1, projection=crs.Mercator())
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.STATES)# Zoom in on the US by setting longitude/latitude parameters
ax.set_extent(
[
-135, # minimum latitude
-60, # min longitude
20, # max latitude
55 # max longitude
],
crs=crs.PlateCarree()
)plt.show()
一张简单的美国地图。图由作者制作。
在初始化地图并添加了常用的要素之后,还使用 set_extent()方法修改了 ax 变量,该方法指定了经度和纬度的范围,以便在地图上设置焦点。
请注意参数 crs,它指定了要使用的坐标系。一般惯例是使用板块 Carrée 投影的坐标,即使它与地图的投影不一致,因为它产生更可预测的结果。
散点图
到目前为止,我们只讨论了生成通用地图,但现在我们将讨论在这些地图上绘制数据。唯一的要求是每个观测值都有一个有效的纬度和经度。为了演示,从 Kaggle 中提取了美国 341 个机场的源文件,并将其绘制在散点图上。
import pandas as pd# Read the data
df = pd.read_csv("airports.csv")
在进行任何映射之前,Pandas 库被导入到结构化数据中,CSV 文件被读入到数据帧中。
figure = plt.figure(figsize=(8,6))ax = figure.add_subplot(1,1,1, projection=crs.PlateCarree())
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.STATES)ax.set_extent(
[-135, -60, 20, 55],
crs=crs.PlateCarree()
)# modify the plot by adding a scatterplot over the map
plt.scatter(
x=df["LONGITUDE"],
y=df["LATITUDE"],
color="red",
s=4,
alpha=1,
transform=crs.PlateCarree()
)plt.show()
美国大陆的机场。图由作者制作。
实际上,创建地图后,Matplotlib 会在图像上添加一个散点图。对于熟悉流行图形库的人来说,语法应该非常熟悉。对那些不是的人来说,争论的分类是:
- x:x 轴上的数据。在这种情况下,经度
- y:y 轴上的数据。在这种情况下,纬度
- 颜色:标记的颜色
- 学生:马克笔的大小
- alpha:标记的透明度,从 0 到 1
可以在散点图上传递额外的参数来进一步定制它。完整的名单可以在这里找到。
还要注意 transform 参数,按照惯例,它使用 Plate Carrée 投影的原因与 set_extent()类似。
结论
Cartopy 是一个多样化的地图库。考虑到各种投影和坐标系统,它支持从生态跟踪到商业智能的广泛的可能用例。
然而,它与 Matplotlib 的集成是一个令人难以置信的数据分析工具。虽然只演示了一个简单的散点图,但它对 Matplotlib 的广泛而灵活的使用意味着可以将不同数量的绘图和图形投影到一个地图上。
使用 Pandas & Matplotlib 绘制 Google 趋势数据
辅导的
分析谷歌趋势的前五名民主党总统候选人数据,找出辩论季最受欢迎的候选人。
如果你一直在观看过去一年的民主党总统候选人辩论,你可能会同意这有点像一场狂野之旅。从咬手指到紧张的辩论后对峙。我们知道乔·拜登是民主党的假定提名人。但是我想看看数据。我们将使用 pandas 和 matplotlib 库从 Google Trends 中以搜索兴趣数据的形式遍历和探索这些数据。我们将重新创建谷歌趋势图,以可视化它们随时间的流行程度。我们来看看——谁是最受欢迎的候选人?
谷歌趋势
Google Trends 提供了一个在特定时间范围内对 Google 的搜索请求示例。谷歌将搜索数据标准化,使术语之间的比较更容易。根据某个主题在所有主题的所有搜索中所占的比例,这些数字的范围是 0 到 100。—trends.google.com
这意味着,当查看这些值时,值 100 表示在给定的时间范围内,这些术语(候选项)中的最高流行度。值为 50 意味着该术语的流行程度是一半。
注:我知道谷歌搜索实际上并不是流行度的黄金标准。然而,它确实反映了人们每天在谷歌上进行的搜索。
数据 本项目使用的候选人是那些参加了前 10 场辩论的候选人。乔·拜登,伯尼·桑德斯,伊丽莎白·沃伦,艾米·克洛布查尔,皮特·布蒂吉格。数据是从 2019 年 4 月到 2020 年 5 月的谷歌趋势中拉出来的。
1|导入必要的库,导入和检查数据
候选人. csv
数据作为一个大列读入,标签为“类别:所有类别”。我们可以通过传递行索引来指定哪一行应该是我们的标签,从而改变这一点。
2|使用正确的标签重新读入数据集
candidates.csv 读入正确的标签
很好——现在我们已经读入了带有正确标签的数据帧。现在,开始一些争论。首先——看看这些标签……当在 pandas 中操作数据时,易于使用的列标签可以使工作更快,更少令人沮丧。我们可以通过缩短它们,去掉空格,并使它们都变成小写来清理这些标签。
3|调整列标签
列表理解将用于调整标签。如果你需要的话,这是一个很好的教程来温习列表理解。
清洁的色谱柱标签
不错!使用列标签要容易得多。让我们再看看我们的数据。
4 |检查数据
候选数据帧的数据类型
好的——我们看到“joe”和“bernie”列具有整数数据类型,但是其余的列是“object”类型。这是因为当谷歌趋势有一个非常低的数字,而不是“0”,他们把术语“<1’. The ‘
5| Replace the ‘<1’ string with an integer
Great, the ‘
6 |调整列的类型&设置索引
不错!现在,我们的 DatetimeIndex 显示了数据的日期范围,每一列都是数字类型。我们来看看数据!
7 |绘制数据
2015 年 4 月至 2020 年 5 月的谷歌搜索趋势
我们看到伯尼从 2016 年 2 月到大约 5 月底主导结果。这是在桑德斯 2016 年竞选总统期间。
让我们从民主党初选辩论之前的时间开始放大,覆盖民主党初选辩论的日期。民主辩论时间:2019 年 6 月 26 日,2019 年 7 月 30 日,2019 年 9 月 12 日,2019 年 10 月 15 日,2019 年 11 月 20 日,2019 年 12 月 19 日,2020 年 1 月 14 日,2020 年 2 月 7 日,2020 年 2 月 19 日,2020 年 3 月 15 日。然而,我们数据中的日期是每周的——在绘制数据之前,我们需要将日期调整为代表辩论发生的那一周的日期。
8|调整辩论日期然后绘图
标有辩论日期的趋势数据
我们可以看到每个辩论日期前后都有峰值。在第一次辩论之前有几个峰值-我们看到皮特·布蒂吉格在 2019 年 4 月有一个小峰值。2019 年 4 月 15 日 Buttigieg 宣布竞选。伊丽莎白·沃伦在【22,2019 年 4 月 22 日有一个较小的峰值,对应于她的取消学生债务和取消大学学费的计划的发布。乔·拜登于 2019 年 4 月 25 日宣布竞选总统。
有些峰值并不与辩论日期直接对应。例如,我们看到拜登和桑德斯在 9 月和 10 月的活动之间有一个高峰。2019 年 9 月 25 日拜登在吉米·基梅尔直播 接受了采访,他在采访中讨论了弹劾调查。2019 年10 月 2 日桑德斯因动脉阻塞住院。
最热门人选?
那么,谁是最受欢迎的候选人?我们看到,在 2 月底/3 月初,拜登和桑德斯都大幅上升,他们看起来势均力敌。让我们通过在熊猫数据框架上使用 rolling() & mean()函数来消除由短期运动引起的噪声。
首先,我想指出一些有趣的事情——皮特的旅程。他在 2019 年 4 月/5 月有一个大的初始攀升(不久后超过桑德斯),然后在年底(12 月/1 月)有一个较小的攀升,其他候选人开始下降或持平。
至于最热门的人选,会不会是拜登?看起来拜登在大多数辩论中都保持了一定的领先优势。还是桑德斯?到了新年,伯尼一直领先,直到他退出比赛。
你认为谁是更受欢迎的候选人?
你可以在 Github repo 中找到完整的代码。另外——我花了一些时间使用这些数据通过 Tableau 构建了带有注释的相同情节。在这里看一下互动 viz。
完成互动的静态照片,即此处。
熊猫里的情节越来越漂亮了
创建丰富的可视化和仪表板与熊猫绘图后端为 Plotly 和 Bokeh
流行的 Python 数据分析库熊猫中的绘图功能一直是我获取超级快速图表的常用方法之一。然而,可用的观想总是相当基础的,并不特别漂亮。
我经常发现自己使用 Pandas 绘图功能快速执行一些可视化数据探索。但是,当涉及到向利益相关者展示见解时,可以使用 Plotly 或 Bokeh 等“更漂亮”的绘图库重新进行可视化。
自从最新的 Pandas 版本 0.25.3 以来,这不再是必要的,您现在可以使用第三方可视化库作为 Pandas 绘图功能的后端。流行的基于网络的交互式可视化 Python 库 Plotly 最近也发布了一个熊猫绘图后台。
让我们来看看如何使用 Plotly 和 Bokeh 后端创建更丰富的可视化效果。
使用不同的后端
为了激活绘图功能的不同后端,在导入 pandas 之后,添加这行代码。
pd.options.plotting.backend = 'plotly'
当前可用的后端有:
- Plotly
- 全息视图
- Matplotlib
- 熊猫 _bokeh
- Hyplot
Plotly 后端
Plotly 是一个 Python 库,支持各种丰富的交互式可视化。Plotly 包的一个好处是它建立在 Javascript 版本的库之上,这意味着图表是基于 web 的,可以显示为 HTML 文件或嵌入到基于 Python 的 web 应用程序中。您还可以下载高质量图像文件的可视化效果,因此非常适合在文档或论文中使用。
让我们快速浏览一些熊猫绘图功能的后端使用示例。
如果你还没有安装它,你将需要安装 Plotlypip intsall plotly
。如果你在 Jupyterlab 中使用 Plotly,你将需要执行几个额外的安装步骤来显示可视化。首先,安装 IPywidgets。
pip install jupyterlab "ipywidgets>=7.5"
然后运行这个来安装 Plotly 扩展。
jupyter labextension install jupyterlab-plotly@4.8.1
为了说明绘图后端的用法,我将使用一个来自 openml.org 的数据集,名为“葡萄酒”数据集。可以在这里找到,但是也可以使用下面的代码直接导入到笔记本中(你需要先安装 Scikit-learn)。
葡萄酒数据集由葡萄酒类型的许多特征和相应的标签组成。数据集的前几行如下所示。
让我们使用 Plotly 后端来稍微探索一下数据集。
绘图功能的工作方式与标准的 Pandas 绘图功能非常相似,只是现在可视化以丰富的 Plotly 显示。以下代码绘制了数据集中两个要素之间的关系。
如果我们将鼠标悬停在图表上,可以看到我们可以选择将图表下载为高质量的图像文件。
我们可以将 Pandas groupby 函数结合起来,创建一个条形图来总结不同类别之间平均色调的差异。
让我们向之前创建的散点图中添加类。Plotly 允许您轻松地将不同的颜色应用到每个类,以便我们可以可视化的分离。
散景后端
Bokeh 是另一个 Python 绘图库,提供丰富的交互式可视化。可视化可以在网络浏览器中查看,嵌入到网络应用程序中,或者用于创建交互式仪表板。Bokeh 甚至有一个流式 API,因此可以为流式数据(如金融市场)创建实时可视化。
熊猫散景的完整文档可以在这里找到。该库可以被 pip 安装pip install pandas-bokeh
。
为了在 Jupyterlab 中显示散景可视化,我们需要安装两个新的扩展。
jupyter labextension install @jupyter-widgets/jupyterlab-managerjupyter labextension install @bokeh/jupyter_bokeh
让我们使用散景后端重新创建之前创建的散点图。
可视化如下所示。散景图是自动交互的。
散景有一个 plot_grid 功能,可以为多个图表创建类似仪表板的布局。下面的代码以网格布局创建了四个图表—显示在代码下面。
内置的 Pandas 绘图功能增加了多个第三方后端,大大增强了这个库的数据可视化能力。本文只给出了 Plotly 和 Bokeh 后端的一些功能。如果您仔细阅读文档,您会发现使用这一新功能可以创建丰富的仪表板和 web 应用程序。
感谢阅读!
我每月都会发一份简讯,如果你想加入,请点击此链接注册。期待成为您学习旅程的一部分!