本月关于 AI/ML 的令人印象深刻的中型文章
罗马卡夫在 Unsplash 上拍摄的照片
意见
随着七月的结束,这里有一些你不应该错过的与人工智能和机器学习相关的顶级文章。
每个月底,我都会反思我读过的媒体文章,并尝试重读和保存那些提供了某种形式价值的文章。
现在,我决定分享我 7 月份在 Medium 上看到的前四篇文章。
Medium 上有大量有趣且写得很好的文章,这四篇只是我个人认为突出的,所以请考虑我的选择纯粹是主观的。
如果你遇到任何人工智能/人工智能相关的文章,希望我阅读并包含在我的每周/每月文章报道中,请随时在文章的评论部分给我加标签,我会做一个评论。或者在 LinkedIn 上连接并发送文章链接
了解雇主对 2020 年数据科学家的期望
Shareef Shaik 撰写的这篇令人印象深刻的文章以直观的视觉形式展示了印度数据科学就业市场的现状。
说从 Shareef 的文章中可以获得大量信息是一种保守的说法。本文利用搜集的数据提供以下统计信息:
- 招聘信息在印度的什么地方发布?
- 按公司分类的职位发布数量的百分比
- 需要特定的数据科学角色,以及雇主通常需要的技能。
Shareef 是一位自学成才的数据科学家,他通过一个标准的数据科学项目管道来呈现这些信息。
本文的内容对目前正在印度找工作的数据科学行业的任何人都很有用。这篇文章告诉你在哪里寻找角色,更有用的是,你应该掌握什么技能来快速找到一个职位。
除了本文的信息价值之外,Shareef 还展示了用于收集、预处理、标记和分析聚合数据的所有代码。因此,本文既是一篇关于数据科学技能利用的实用演示,也是一篇信息丰富的文章。
这篇文章非常适合阅读:
- 印度的数据科学家
- 数据科学家
整个分析是从 1000 多份最近的数据科学家工作中完成的,这些工作是使用网络搜集从工作门户中提取的。
towardsdatascience.com](/know-what-employers-are-expecting-for-a-data-scientist-role-in-2020-65ad68553cc4)
忘掉编码吧,未来的工作是哲学,作者卢卡·罗西
本周我读过的最有趣的文章之一。卢卡·罗西(Luca Rossi)写了一篇文章,这篇文章将让大多数读者走上自我和环境意识的道路。
读完这篇文章后,我发现自己在质疑我的行为和贡献的影响,这些行为和贡献会导致这篇文章中所创造的想象世界。
Luca 的文章首先陈述了农业、工业和技术革命等全球性革命周期性发生所造成的失业影响。
然后,随着自动化使传统的手工劳动过时,他描绘了引入新的角色和工作的一线希望。但这一丝希望被他的个人意见挡住了。
卢卡在他的文章中指出,自动化和人工智能不会导致大量失业,这并不能保证或确保未来所有人都有大量工作。卢卡还表达了他对未来的担忧,在未来,自动化将统治生活的所有方面,使我们变得无用;他表达了对人类幸福和满足感的关注。
Luca 列出了一些最终将被淘汰的职业,当我往下读列表时,我很欣慰没有看到任何机器学习的角色。卢卡明确指出,由于机器和自动化,没有提到的职位仍然是即将到来的就业市场末日的受害者,这种宽慰很快就消失了。他甚至提供了一个人工智能通过艺术模仿人类创造力的例子来进一步支持他的观点。
没有人是真正安全的。除了哲学家
卢卡认为人工智能不能取代哲学家,因为哲学是人类对存在和生命的本质的模糊性的表达,这与“纯智能”无关。这是一个我不完全赞同的观点。在这个问题上,我可能会和卢卡好好辩论一番。
不管我对卢卡观点的反对意见,我仍然欣赏他对四个越来越相关的哲学主题的结构化方法:道德、意识、生命的意义和对齐问题。
卢卡将每个提出的哲学主题与当前技术(如自动驾驶汽车)和假设的未来技术(如心灵传输和心灵传输)的发生结合起来。
通过创建基于简单和复杂事件的场景,Luca 展示了人类可能做出的选择之间的差异,并展示了人工智能系统做出相同决定的可能性。
Luca 的文章可能不同于许多机器学习从业者习惯阅读的更具技术性和更直接的文章。但是我推荐定期阅读探索技术进步的哲学方面的文章。我知道的一本很好的书是尼克·博斯特罗姆的超级智慧:路径、危险、策略。
在结束一篇有趣的文章时,卢卡大胆地指出,世界的命运掌握在哲学家手中,这一观点令我着迷。
这篇文章很有意思,适合:
- 工艺师
- 对与 AI 交织在一起的哲学话题感兴趣的人
人工智能在不久的将来会带来四大问题。只有哲学可以拯救我们。
medium.com](https://medium.com/i-human/forget-about-coding-the-job-of-the-future-is-philosophy-33acadcee05a)
GPT-3:第一个人工通用智能?朱利安·劳蕾特
一站式获取有关 GPT-3 的历史、发展和潜力的信息。
简短评论
Julien Lauret 的文章全面总结了 GPT 3 号的研发历程。
Julien 已经成功地将多年来对建模语言和解决自然语言处理的方法和技术的开发和介绍总结为几个小而简洁的段落。
除了向读者提供 GPT-3 的背景资料,朱利安还用外交辞令回答了 GPT-3 是否是 AGI 的问题。他的回答真实地反映了问题本身的性质,因为无论是谁提出这个问题,这个问题都要服从于智力的定义。
详细审查
朱利安关于 GPT-3 的最新文章是一篇非常需要的文章,因为我觉得人工通用智能(AGI)的话题在我们的想象中几乎已经退居二线了。
朱利安在文章的开头指出了关于 AGI 的两种观点。一种观点暗示 AGI 还有几十年的时间;另一个问题是人类是否有可能到达 AGI。
Julien 的文章中充斥着一些技术和专业术语,但每个术语都附有简短的定义,在某些情况下还有广泛的背景知识。
诸如“深度神经网络”、“机器翻译”、“Word2vec”和“少量学习”等术语的呈现和定义方式为读者提供了语言建模和 NLP 的速成课程,以及有关 GPT-3 的主要信息。
这篇文章直到文章中途才提到 GPT-3,这是因为 Julien 巧妙地带领读者经历了一次概述对语言建模和 GPT-3 本身的进步做出贡献的关键发展和研究的旅程。
关于 GPT-3 和它的前身的内在特征*(重量数)*的信息被提供给读者,以使读者了解 GPT 模型在每个发布版本中所取得的进步水平。
本文的后半部分展示了早期 access API 用户开发的 GPT-3 的直观应用程序示例。
但更重要的是,朱利安回答了文章题目中提出的问题。朱利安对 GPT-3 是否是 AGI 的结论是,它不是。但更重要的是,问题本身的答案并不像人们想象的那样清晰。
这种不明确性源于这样一个事实,即智力的定义是模糊的,取决于个人的解释。
朱利安指出,讨论 GPT-3 的应用是一个重要的问题。我们已经看到了它生成诗歌、游戏场景和通过语言定义的 web 组件的能力。
我们现在需要观察的是,人工智能的最新成就如何应用于涉及视频和图像的更广泛的环境中。
朱利安的文章有趣地包含了一些哲学观点和陈述。例如,朱利安指出,我们无法确定 GPT-3 拥有的智力水平,就像我们无法确定一个瘫痪的人或身体有缺陷的人的认知功能水平一样。
这篇文章深入探讨了值得思考的人工智能领域。
这篇文章非常适合:
机器学习从业者 :所使用的语言和技术术语是任何一个参与机器学习的人都非常熟悉的。所包含的定义和论文允许进一步开发与 GPT-3 相关的主题
当历史学家回顾过去时,他们会选择 2020 年作为发明 AGI 的一年吗?
towardsdatascience.com](/gpt-3-the-first-artificial-general-intelligence-b8d9b38557a1)
让你在数据科学职业生涯中不可或缺的 4 种超能力
你已经阅读了 Miguel 的文章和上面的故事,并且你已经准备好从事数据科学方面的职业(或者至少对某个领域很好奇)。你应该给自己配备超能力。
加内斯·凯萨里的文章介绍非常生动,写得很有创意。我喜欢 Ganes 用来描述数据科学家的斗争的比喻。将与项目经理打交道和互动比作拔牙是很滑稽的。
Ganes 不得不描绘一个数据科学家的可怕工作生活,以展示你在面对邪恶的逆境时需要保持的四种超能力(我坚持超能力主题)。
但严肃地说,Ganes 提出了数据科学家普遍面临的四个挑战领域,并提供了帮助您在数据科学职业生涯中导航的提示和建议。
Ganes 包括如何处理杂乱数据的技巧和掌握的重要性。但是引起我共鸣的一个关键点是强调关注技术而不是工具和应用的重要性。我将保持简单,工具和应用程序来来去去,但技术保持不变。
最重要的是,数据科学家能够理解技术的基本原理,而不是利用几年后就过时的应用程序。
Ganes 为读者提供的所有超能力都是适用的,不管你在做什么项目。
如果你目前工作停滞不前,需要一些建议,请阅读这篇文章。
这篇文章非常适合阅读
- 数据科学从业者
了解数据科学行业的这些最大挑战,以避免职业生涯停滞不前
towardsdatascience.com](/4-superpowers-that-will-make-you-indispensable-in-a-data-science-career-6571e8e7d504)
以下是我在 7 月份注意到的其他文章的链接
增强你对最新炒作的人工智能模型:GPT 3 的了解
towardsdatascience.com](/articles-that-will-help-you-understand-gpt-3-610dedc37859) [## 本周(7 月 11 日)你应该阅读的有趣的人工智能/人工智能文章
发现提供在数据科学和机器学习领域导航成功职业生涯的建议和提示的文章。
towardsdatascience.com](/interesting-ai-ml-articles-you-should-read-this-week-july-11-bac8f2a65819) [## 本周(7 月 4 日)你应该阅读的有趣的人工智能/人工智能文章
找出为什么你应该换工作,成为一名哲学家
towardsdatascience.com](/interesting-ai-ml-articles-you-should-read-this-week-july-4-cad0d162e108)
利用脑-机接口和机器学习改进青光眼评估
理解大数据
我的研究使用多任务学习来提供快速的护理点诊断,以检测周边视觉损失
人类擅长捕捉引起剧烈变化或疼痛的感官线索。但是,当变化如此轻微和缓慢,以至于人们无法注意到它时,像视力退化这样的逐渐变化又如何呢?
在许多国家,医疗保健的基础是患者在出现症状时寻求医疗护理。使用这种方法,个人需要知道他们的症状是什么。但是无症状的疾病呢?直到晚期,人们才能注意到,那时他们的生活质量由于视力丧失而受到严重影响。
这是青光眼。
正常视力与青光眼视力的比较。[按作者、原始剪辑的来源组合]
随着老龄化人口的增加,我们面临着寻找解决方案来满足老年人医疗保健需求的挑战。随着年龄的增长,失明的几率也越来越高,世界上 80%以上的盲人年龄都在 50 岁以上。总体而言,75%的各种原因导致的失明是可以预防、治疗或治愈的。
我的研究工作通过在临床筛选和诊断程序中应用脑-机接口方法来解决这个问题。这可能为无症状病例的视觉功能检测提供更准确的诊断。
随着年龄的增长,失明的几率会越来越高。[ 来源 ]
关于青光眼及其影响
青光眼被誉为“沉默的视力窃贼”。这是一种随着时间的推移逐渐发展的眼病,会夺走视力。一点一点,你在慢慢变瞎,等你意识到的时候已经太晚了。青光眼导致的视力丧失一旦发生,将是永久性的。
它没有警告标志;从这个例子中可以看出,当两只眼睛有不同的受影响区域时,患者直到晚期才会注意到。
青光眼中的不对称视野丧失可导致迟发,因为患者双眼睁开时看不到缺损。[ 来源
世界卫生组织(World Health Organization)的一份报告称,预计 2020 年(7600 万)至 2030 年(9540 万)间,青光眼患者数量将增加 1.3 倍。随着老龄化人口的增长,患有这种疾病的人数也将大幅增加。
沉默的小偷没有任何警告信号。研究表明,50%未确诊的青光眼患者在诊断时已经有明显的视野缺损。由于患者通常不会注意到视觉功能的任何变化,他们不会寻求医疗护理。
青光眼对视神经的损害是不可逆的,在大多数情况下,如果没有适当的治疗,它会发展。因此,在初始阶段进行抢先筛查以发现患者至关重要。唯一的解决办法是早期治疗。
随着老龄化人口的增长,患有这种疾病的人数也将大幅增加。【来源
现有筛选方法的局限性
青光眼的诊断包括由医生通过视野测试来评估患者的视觉功能。视野测试是一种测量个人视力的方法——中央和周边视力。视野测试可单独绘制每只眼睛的视野图,并可检测盲点(暗点),以及视力模糊的更细微区域。该测试将检查患者视野和功能的任何缺陷,如丧失周边视觉。
青光眼筛查测试的目的是检测早期疾病,以便对这些患者进行治疗,降低失明的风险。
怎样才能测量视野?
标准自动视野检查,简而言之,我们称之为 SAP。这是目前医生用来评估视野中的盲区、监测青光眼患者视觉功能的方法。
考官会一次用一只眼睛进行测试。它将提供一系列的刺激(闪烁的光点),一次一个,同时患者必须在整个过程中保持注视中心目标。如果病人看到了光,他们就按下按钮。这些反应将被记录下来,并形成患者的视野图。
SAP 的视野测试是一项主观测试。
它要求患者理解测试说明,充分合作,并完成整个视觉测试,以提供有用的测试结果。
每只眼睛的测试过程大约需要 10 分钟。10 分钟绝对集中注意力在一个中心点上。10 分钟记住当刺激物出现时按下一个按钮。
因此,当患者按下按钮时,即使没有刺激存在,也经常会遇到假阳性结果。
当患者对本应检测到的光刺激没有反应时,假阴性率也很高。当患者在测试过程中注意力不集中时尤其如此。
由于疲劳和注意力分散,患者可能会失去对中心目标的固定,从而影响测试结果的可靠性。
所有这些相互矛盾的结果阻碍了医生做出诊断的决定。单次测量的可靠性可能很低;需要几次一致的测量来确定缺陷的存在。因此,需要更多的多重测试,这反过来会导致诊断延迟。
这些因素突出了对提供客观评估以改善视野检查的技术解决方案的需求。
我们如何从当前的视野检查程序中去除主观性?
我们可以用什么来更精确地测量视野,而不是依赖患者按下按钮?
能不能缩短视野评估的时间?
我们需要一种能够提供客观评估的解决方案来改善视野检查。[ 来源
结合脑机接口和机器学习
这张图说明了我们研究的主要观点。
患者将聚焦在一个中心点上几秒钟,我们将捕捉来自视网膜中央凹和周围邻近目标的信号。我们的模型预测了整个视野中所有 SSVEP 频率的存在。也就是说,如果患者在某个区域有盲区,那么从这些刺激中提取的信号是微弱的,它会反映在视觉反应图上。[ 作者图片
我们的目标是实现一个系统,该系统产生的视野测试结果更加可靠,因为我们消除了现有视野评估中的认知方面。通过这样做,患者不必学习使用该系统,并且测试结果不会受到患者分心或感觉不舒服的影响。
当我们看到闪烁的东西时,稳态视觉诱发电位(SSVEP)会产生大脑信号,频率通常在 1 到 100 赫兹之间。在这个实验中,这些闪烁的灯在屏幕上闪烁。这些闪烁的光被称为刺激。
患者将集中注意力在一个中心点上几秒钟,我们将通过脑电图 (EEG)捕捉他们的大脑信号。脑电图是一种记录大脑电活动的电生理监测方法。
成为 X 教授,解开我们心灵的秘密
towardsdatascience.com](/decoding-brain-signals-with-machine-learning-and-neuroscience-bee288c1d585)
我们的重点是从整个视野中检测多个信号来诊断青光眼患者。在我们的工作中,我们从 EEG 信号中提取特征。通过我们的方法,我们能够生成显示患者视野中可见部分的视觉反应图。
我们的模型预测了整个视野中所有 SSVEP 频率的存在。也就是说,如果患者在某个区域有盲区,那么从这些刺激中提取的信号就很弱,并且会在视觉反应图上反映出来。
由于我们的模型可以检测多种刺激的存在,我们可以减少视野评估时间,并产生可靠的测试结果。这可能潜在地适合于为青光眼患者提供快速的即时诊断。
他们如何相互激励、共同进步、相互受益
towardsdatascience.com](/the-fascinating-relationship-between-ai-and-neuroscience-89189218bb05)
模型架构
我们的模型由 3 部分组成,1) SSVEP 特征提取,2)特征学习,3)多任务学习模块。提议的网络图示如下。
我们的神经网络由四个卷积块组成,负责学习所有目标频率的 EEG 表示。第五卷积层是一个多任务学习模块,它学习区分多个 SSVEP 目标频率。[ 作者图片
SSVEP 特征提取
该组件的目的是将原始 EEG 信号解码为我们模型的输入特征。有两个卷积块(C1 和 C2);每个卷积块由一个卷积图层、一个批量归一化和一个指数线性单元组成。
C1 块设计用于提取 EEG 输入的频谱表示,因为它在时间维度上执行卷积,从每个 EEG 通道独立于其他通道捕获特征。
C2 块被设计用于进行空间滤波,因为它在信道维度上执行卷积。这一层的目标是学习每个时间样本的所有通道的权重。
特征学习
这里,有两个卷积块(C3 和 C4),这些块的目的是捕获每个提取的特征图中的时间模式。
我们还研究了 C3 和 C4 地块上不同的膨胀构型。由于信号所需的核大小比图像大得多,膨胀卷积允许我们扩展感受野,用较小的核大小执行特征学习,产生较小的模型,并潜在地提高性能。
多任务学习模块
第五个也是最后一个卷积模块学习区分多个 SSVEP 目标频率。我们利用多任务学习的方法,设计这个模块来产生多种输出。
在经典的多任务学习架构中,每个任务被分成特定于任务的层,每层负责学习识别每个任务。但我们不是为每个输出复制一个卷积层,而是按组执行卷积。我们实际上是在单个卷积层中执行单独的卷积。这使我们能够在单个 GPU 上高效地并行训练多个任务,并且我们可以有效地动态调整我们的模型规模以适应任意数量的任务,这可能适用于其他多任务学习应用。
我们的实现建立在 PyTorch 框架之上,并在我们的伙伴网站上公开发布。
[## 用于 SSVEP 检测和视觉反应映射的深度多任务学习
使用多任务学习有效地同时捕获来自视网膜中央凹和邻近目标的信号。
jinglescode.github.io](https://jinglescode.github.io/ssvep-multi-task-learning/)
结果(多标签)—视觉反应图
我们的模型是一个统一的系统,通过学习区分 40 种频率来生成视觉反应图。我们预测所有目标频率同时出现。因此,这使我们能够用视觉响应图来可视化用户所看到的内容。
一些视觉图的例子,其中较暗的阴影表示来自 SSVEP 刺激的较强信号。[ 作者图片
这是一些视觉地图的例子,其中较暗的阴影象征着来自 SSVEP 刺激的较强信号。我们选择了位于屏幕中心周围的 6 个目标,因为这是我们未来工作中视野评估的感兴趣区域。我们已经用留一个受试者的方法评估了我们的模型,以展示我们方法的通用性,并且我们的方法需要很少或不需要对新用户进行校准。
结果(多类)—分类
我们也可以使用我们的网络作为分类器。因此,我们可以将我们的模型与 SSVEP 分类准确性的现有技术方法进行比较。
将我们的模型与其他基于 CCA 的方法以及各种模型配置进行比较。[ 作者图片
对于每次试验 1 秒钟的信号,典型相关分析 (CCA)、滤波器组典型相关分析 (FBCCA)和任务相关成分分析 (TRCA)分别产生大约 59%、72%和 88%的准确度。使用我们提出的方法,我们可以有效地识别视网膜中央凹的目标频率,在遗漏一个受试者的情况下达到 92%的准确率。
显然,这一结果表明,我们的方法可以作为识别单个闪烁目标的替代方法,其中重点是提供在视网膜中央凹视觉上检测到的可靠的 SSVEP 响应。
结论
这项研究提出了一种深度学习方法,该方法可能使我们能够同时检测多种 SSVEP 刺激,从而绘制青光眼患者的视觉地图,减少视野评估时间,并产生可靠的测试结果。
鉴于最近在疾病爆发和大流行期间发生的事件,建议将非必要的医院预约保持在最低限度,这种评估方法可以减少所需的测试次数,从而最大限度地减少任何不必要或额外的测试。本质上,这项研究使我们未来的工作能够潜在地评估青光眼患者的视野,以检测周边视力丧失。为了提高评估结果的可靠性,利用 SSVEP 可以消除患者执行程序的能力和患者精神状态的可变性。通过同时检测多个 SSVEP 目标并生成视觉反应图,可以缩短评估时间。
我们的方法可能适用于为青光眼患者提供快速的即时诊断。
[## 用于 SSVEP 检测和视觉反应映射的深度多任务学习
使用多任务学习有效地同时捕获来自视网膜中央凹和邻近目标的信号。
jinglescode.github.io](https://jinglescode.github.io/ssvep-multi-task-learning/)
使用特征重要性提高模型性能
理解寻找特征重要性的不同方法
弗兰基·查马基在 Unsplash 上拍摄的照片
机器学习模型性能是选择特定模型的最重要因素。为了选择机器学习模型,我们可以查看某些指标,这些指标可以帮助我们选择具有最高准确性和最小误差的最佳模型。
无论是回归模型还是分类模型,特征变量在建立预测模型中起着重要的作用。拥有大量的特征并不好,因为这可能会导致过度拟合,这将使我们的模型特别适合它所训练的数据。此外,具有大量特征将导致维数灾难,即特征将增加问题的搜索空间的维数。
我们可以使用不同的技术和方法进行降维和特征选择,并且为了使这些步骤更加有效,我们可以使用特征重要性,这为我们提供了在预测目标变量时哪些特征最重要的洞察力。
特征重要性是一种为我们提供每个特征变量的相关分数的技术,我们可以用它来决定哪些特征最重要,哪些特征对预测目标变量最不重要。
在本文中,我们将探索不同的技术,我们可以使用的功能的重要性,并找出他们的相对重要性分数。
加载数据集
我们将使用包含目标变量为 0(非糖尿病)或 1(糖尿病)的糖尿病数据集进行分类问题,并使用不同的机器学习模型找出最重要的特征变量。
import pandas as pd
df = pd.read_csv('DIABETES.csv')
糖尿病数据集(来源:作者)
在找出重要的特性之前,我们需要定义特性和目标变量。
X = df[['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI','DiabetesPedigreeFunction','Age']]
y = df['Outcome']
特征重要性技术:
1.使用黄砖要素重要性
Yellowbrick 是一个可视化库,主要用于模型选择和模型性能可视化。它是使用 sklearn 构建的,基于 matplotlib 构建,用于可视化。
我们将使用 yellowbrick 的要素重要性可视化工具,它返回所有要素及其相对重要性分数。我们可以使用不同的模型来寻找特征的重要性。
#Creating SVM Model
from sklearn import svm
model = svm.SVC(kernel='linear')
#Importing Yellowbrick Feature Imporance and using it
from yellowbrick.model_selection import FeatureImportances
viz = FeatureImportances(model)
viz.fit(X, y)
viz.show()
SVM 特色重要性(来源:作者)
类似地,我们可以使用不同的模型,并找出该模型的特征重要性。让我们再看一个使用随机森林分类器的例子。
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
#Visualizing Feature Importance
viz = FeatureImportances(model)
viz.fit(X, y)
viz.show()
随机森林特征重要性(来源:作者)
2.排列重要性
置换重要性在 Sklearn 模型检验方法下定义。当数据是表格形式时,它可用于任何拟合的估计量。该函数计算给定数据集的估计量的特征重要性。n_repeats 参数设置随机洗牌的次数,并返回特征重要性的样本。
from sklearn.inspection import permutation_importance
from sklearn.naive_bayes import GaussianNB
import matplotlib.pyplot as plt#Defining the model
model = GaussianNB()
model.fit(X, y)# perform permutation importance
results = permutation_importance(model, X, y, scoring='accuracy')# get importance
importance = results.importances_mean# summarize feature importance
for i,v in enumerate(importance):
print('Feature: %0d, Score: %.5f' % (i,v))# plot feature importance
plt.bar([x for x in range(len(importance))], importance)
plt.xlabel=df.columns[:-1]
plt.show()
排列的重要性(来源:作者)
同样,我们可以使用不同的模型来找出该模型的特征的重要性。让我们使用不同的模型来找出特性的重要性。
from sklearn.linear_model import LogisticRegression#Defining the model
model = LogisticRegression()
model.fit(X, y)# perform permutation importance
results = permutation_importance(model, X, y, scoring='accuracy')# get importance
importance = results.importances_mean# summarize feature importance
for i,v in enumerate(importance):
print('Feature: %0d, Score: %.5f' % (i,v))# plot feature importance
plt.bar([x for x in range(len(importance))], importance)
plt.xlabel=df.columns[:-1]
plt.show()
物流特征的重要性(来源:作者)
我们在这篇文章中讨论的方法易于使用,因为你不必考虑不同的参数,应该有如果通过传统的。在某种程度上,这些技术自动化了特性的重要性,因此节省了时间和精力。
同样,您可以在具有不同模型的不同数据集上尝试这些技术。继续探索这些技术,并在这篇文章的回复中分享你的经验。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
通过人脸检测(人脸盒)改进人员重新识别
减少视点数量和创建高质量嵌入的简单方法
人的重新识别是一个有趣的并且没有完全解决的任务。它包括在图像中找到(定位)一个人,并为特定人的照片创建数字描述(矢量或嵌入),其方式是特定人的其他照片的矢量的距离比为其他人的照片生成的矢量的距离更近。
图片来自https://github.com/omoindrot/tensorflow-triplet-loss
人的再识别被用在许多任务中,包括购物中心的游客流分析、跨摄像机跟踪人、在大量照片中找到某个人。
近来,许多有效的模型和方法被创建来解决重新识别任务。这些型号的完整列表可以在这里找到。但是,即使是最好的模型也仍然面临许多问题,例如人的姿势和视点的变化,由于这些变化,从不同角度对一个人的照片的嵌入将彼此相距太远,并且系统可以判定这是不同人的照片。
最新的最先进的模型,如用于人员重新识别的视角感知损失和角度正则化,旨在处理上述问题,但我们在ai-labs.org提出了一种简单的方法,在某些情况下极大地简化了重新识别的任务。我将更详细地讨论这种方法。
让我们从解释大多数 re-id 框架如何检测图像中特定人的照片开始。最常用的物体检测模型,如更快的 R-CNN 或 EfficientDet ,用于创建整个人体的边界框。提取整个人体的照片后,将创建该照片的嵌入。
问题是,对象检测模型往往工作得太好了,它们从各种角度找到人们的照片,但并不总是最好的质量。基于这些照片的嵌入通常不允许正确地重新识别一个人,或者以这样一种方式生成,即从一个视点对特定人的照片的嵌入将接近于仅从相同视点对照片的嵌入,但是不接近于从不同视点和距离对同一个人的照片的嵌入。
这个问题可以通过减少我们必须处理的视点数量来解决。如果不是在整个身体图像上,而是仅在面部上进行人的重新识别,则可以实现这一点。在这种情况下,面部检测模型面部盒会有所帮助。这个模型已经在我们的工作中证明了自己,加上它可以完美地检测戴面具的人的脸,这在现在尤为重要。通过人脸检测,我们丢弃了很大一部分视点变化,如后视图,但如果我们只使用低质量图像中的人脸照片,那么我们可能没有足够的信息从特定照片中将其与成千上万的其他照片区分开来。
值得一提的是我们使用的facebox模型的实现特性。它不能检测小脸,这在我们的情况下从劣势变成了优势,因为我们会立即丢弃太小(30x30 像素或更小)的照片,这些照片上的信息不足以进行正确的重新识别。
我们还使用了一个小技巧,这将允许我们在不增加视点数量的情况下获得关于一个人的更多信息。我们将根据某种算法扩展人脸的包围盒,同时捕捉部分人体躯干。以下是该算法以函数形式实现的一个示例,该函数接收一帧作为输入,并使用来自 FaceBoxes 的 FaceDetector 对象返回检测到的人物图像列表:
import numpy as np
from PIL import Image
from face_detector import FaceDetector
MODEL_PATH = 'model/facebox.pb'
face_detector = FaceDetector(MODEL_PATH, gpu_memory_fraction=0.25, visible_device_list='0')def pad_faces(frame, tres = 0.5):
face_pad = []
width, height = frame.size
image_array = np.array(frame)
boxes, scores = face_detector(image_array, score_threshold=tres)
for b in boxes:
ymin, xmin, ymax, xmax = b.astype('int')
top_pad = int((ymax - ymin) * 0.4)
bottom_pad = int(ymax - ymin)
w_pad = int((xmax - xmin) * 0.4)
ymin = ymin - top_pad if ymin - top_pad > 0 else 0
ymax = ymax + bottom_pad if ymax + bottom_pad < height else height - 1
xmin = xmin - w_pad if xmin - w_pad > 0 else 0
xmax = xmax + w_pad if xmax + w_pad < width else width - 1
ratio = (ymax-ymin) / (xmax-xmin)
if ratio < 1.6:
xmin = xmin + w_pad
xmax = xmax - w_pad
ratio = (ymax-ymin) / (xmax-xmin)
face_pad.append(image_array[ymin:ymax, xmin:xmax])
return face_padimagex = Image.open('image1.jpg').convert('RGB')
padded_faces = pad_faces(imagex)
应用该函数后,我们得到以下格式的图像:
总的来说,我们选择了高质量的图像,并且主要是从一个角度。这将简化重新识别的任务,因为我们从一个视点接收特定人的嵌入,并且这样的嵌入将彼此接近。为了生成嵌入,我们可以使用锦囊妙计和一个强大的里德基线模型,它显示了各种数据集上的高质量嵌入。
这种方法并不适用于所有情况,但它可以在人们花费大量时间并且您从不同视角获得每个人的大量照片的区域中增加您的重新识别管道的准确性。
使用这个小 Python 语句提高您的分析质量和效率
如何使用 assert 利用 Python 和 SQL 中的测试驱动开发的实际例子
曾经写过 SQL 查询或者操作过熊猫数据帧吗?那么你就知道犯一个小错误很容易导致完全错误的结果。如果只是更新和重新运行一个旧的查询,情况会更糟。这些偏见最终会导致错误的(商业)决策,从而产生巨大的影响。
“你能快速重新运行你 6 个月前为我们做的分析吗?”-“嗯,好吧,我看看我是否能把它拼起来。”
与语法或语义错误不同,逻辑编程错误很难在分析结果中检测出来。但是幸运的是,使用 assert 语句可以大大降低产生这些错误的风险!
让我们快速看一下这个小陈述如何帮助提高你的分析质量和效率(以及对你的结果的信心)。
试驾开发快速入门
我在这篇文章中介绍的方法被广泛称为测试驱动开发 (TDD)。根据我的经验,TDD(不幸的是)在数据科学中并不常见,至少在进行探索性分析时是这样。同时,它作为一种确保高质量代码的机制,在软件工程领域被广泛使用。
这种方法背后的思想是,首先从实现您想要编写的代码的测试开始。该测试基本上定义了您想要实现的输出。然后,您开始编写和重新定义代码,直到它通过预定义的测试。
TDD 迫使程序员在编写第一行代码之前坐下来思考想要的输出。这样,一个人被迫关注实际目标,这自动有助于提高效率。
这种方法不仅有助于写作,也有助于更新你的代码。想象一下,你被要求重新进行一项半年前进行的分析。通常,您必须对您的旧代码进行一些修改。例如,可能需要更新一些参数,或者应该使用一些额外的指标来增强结果。再翻旧剧本总是一件麻烦事。所以更新脚本时很容易出错。但是您在第一次进行分析时定义的测试将帮助您快速识别那些错误,并确保您仍然产生相同的结果。
那么,在 Python 或 SQL 中进行分析时,TDD 到底可以如何使用呢?
什么是断言?
首先,Python 中有 pytest 这样的专用测试包。但是特别是当在笔记本上工作时,我认为 assert 是一种更轻量级和更直接的使用 TDD 的方法。
assert 语句测试条件是否为真。否则,它将引发 AssertionError。这使它成为调试代码的有益工具:
assert 语句引发 AssertionError。
因此,让我们看看在 Python 中处理数据时,如何将 assert 用于 TDD。
对 TDD 使用断言
为了展示如何将 assert 用于 TDD,我将使用一个实际的例子。我们有一个演员和他们出演的电影的表格(这是 Kaggle 的 2006-2016 年 IMDB 数据集)。目标是生成一个只包含每个演员的最后一部电影的表:
示例数据集的样本。
1.定义测试用例
首先要做的是定义我们想要获得的期望输出。因此,我们可以查看其中一个演员,并检查该演员制作的最后一部电影是哪部。对于布拉德·皮特来说,这个数据集中最新的电影是联盟:
布拉德·皮特的电影包含在按年份排序的数据集中。
现在我们知道我们的输出表必须包含一行,其中演员是布拉德·皮特,标题是盟军。此外,这必须是唯一一排有布拉德皮特作为演员。基于此,我们可以定义以下测试用例:
输出数据帧的测试用例。
这几行代码确保我们生成的数据帧满足定义的条件。如果没有,将引发 AssertionError 并停止笔记本的执行。
2.编写和验证代码
因此,让我们开始构建一个只包含每个演员的最后一部电影的表。为了做到这一点,我使用 Pandas 的 rank()函数按电影制作年份对每部电影进行排序,并按演员分组。然后用预定义的测试用例检查其输出:
测试函数抛出 AssertionError,因为输出表与定义的标准不匹配。
正如我们所看到的,生成的数据帧没有通过测试。布拉德·皮特似乎只有一部电影(这是第一个条件),但这部电影是巴贝尔而不是联盟。
从之前的探索性分析我们知道巴别塔实际上是布拉德·皮特在数据集中的第部电影。所以电影的排名肯定有问题。记住,rank()默认情况下按升序排列电影,我们必须更改这个参数,以便电影按年份降序排列:
输出表满足测试用例中定义的标准。
在这个小小的更新之后,表通过了测试用例!当然,我们通常会定义一个有更多条件的更复杂的测试用例,但是我认为这个小案例已经帮助传达了这个想法。
在 SQL 中测试
所提出的方法对于在 Python 中测试 SQL 查询也非常有用。如果我们连接到一个数据库,任何查询的输出都可以很容易地用完全相同的方法进行测试:
用 Python 测试 SQL 查询。
结论
我们已经看到了 TDD 如何帮助我们在处理数据时关注我们想要达到的目标,并确保我们产生正确的结果。所有这些都可以通过 Pythons 内置的 assert 语句轻松实现。
将来,这个测试用例将确保我们在更新表格后仍然获得预定义的结果。因此,尽管数据工作可能变得更加复杂,测试功能仍然有助于确保我们代码的一定质量。如果有人拿起代码并开始添加更新,这将变得更加有用:
带有更多过滤标准的最后一个电影表。
我只能鼓励每个人习惯用这种方法测试你的查询。它可以成为救命稻草!
请在 Github 上我的库中找到这个项目的代码。
喜欢这篇文章吗?那么你可能也会喜欢我的其他帖子:
脸书和他的同事如何在 AB 测试中克服互联用户的挑战。
towardsdatascience.com](/ab-testing-challenges-in-social-networks-e67611c92916) [## Jupyter 笔记本电脑的版本控制
为什么版本控制笔记本不是直截了当的,以及如何使用 nbdime 这样的工具来克服这些问题。
treatwell .工程](https://treatwell.engineering/version-control-for-jupyter-notebooks-4012646d1662)
使用配置文件改善您的数据科学管道
在过去的几个月里,我一直在用 Python 编写数据管道,为一个机器学习项目准备训练数据。为了试验不同版本的数据,有必要通过将用户定义的参数传递到管道的不同步骤来使管道尽可能灵活。在本文中,我将向您展示更好地将参数传递到数据管道所需的步骤。
一个期望的功能是能够容易地使用管道来生成数据集的不同版本。这些版本通常在以下方面有所不同:
- 用于计算特征的操作
- 要包括在特征计算中的数据子集,以及
- 将包含最终特征值的目标文件或表。
管道的早期迭代面临一些挑战:
1.我如何轻松地指定要设置哪些参数,以确保我以最小的出错几率执行正确的操作?
2.一旦管道完成执行,我如何快速返回并检查哪个数据集是使用哪个参数创建的?
我很快意识到最重要的问题是无法跟踪传递给管道组成模块的大量硬编码参数。为了改进我们繁琐而低效的方法,我从软件工程师那里得到了启示(这在处理大规模数据科学项目时非常有用),并修改了管道设计以使用配置文件。
在本文的剩余部分,我将回答以下问题:
1.什么是配置文件?
2.如何将配置文件加载到 Python 脚本中?
3.我如何利用配置的内容?
通过遵循下面的例子并利用配置文件,您将拥有更健壮、更易于维护的代码。
树立一个榜样。
在我们深入技术细节之前,让我描述一下示例场景,它将阐明我们将要讨论的要点。假设我们有一个存储为 CSV 文件的数据集(参见 Github 中的数据),它详细描述了客户执行的交易,并按项目细分。
我们示例数据的前十行。
我们希望编写代码,将原始形式的数据处理成具有客户级别功能的记录;我们称之为管道。为了达到这个目标,我们有一些中间阶段。首先,我们需要在事务级别聚合所有项目。其次,我们需要将交易记录聚合到客户级别。
除了多个阶段,我们还预计每个阶段的处理会根据业务需求而有所不同。对于事务级别的聚合,我们希望能够过滤掉某个价格范围之外的项目。我们还想以可修改的税率计算这些商品的已付税款。对于客户级别的聚合,我们需要能够根据日期过滤交易。我们还希望能够试验如何通过交易总数或每个客户购买的商品数量来标准化商品的总成本。在管道的两个阶段,我们都需要能够指定源数据以及将结果保存到文件中。
上述两个阶段是作为 Python 函数实现的。对于交易阶段:
请注意,该代码包含日期范围的筛选器,并根据 tax_rate 计算 final_price。每个客户交易记录都有日期、总价和交易中购买的商品数量。
对于客户阶段:
在这里,计算诸如 max_price、total_price 和 num_items 之类的特征。为 total_price 选择规范化的逻辑也在这里。
调用管道的代码在脚本的末尾。请注意函数的硬编码参数:
什么是配置文件?
配置文件指定应用程序的参数。这些参数通常以键值对格式编写,本质上是在参数名和该参数的关联值之间创建一个赋值。将参数值放在一个文件中可以提供一个集中的、组织良好的位置来指定应该将什么参数传递给程序。我们希望在配置中包含的数据管道参数示例包括:
- 数据源位置
- 阈值和过滤值
- 要选择的列列表
- 特殊控制标志。
让我们以 JSON 的格式描述这两个阶段,以便事务和客户级别的聚合将它们的参数指定为键值对:
我们将把这个文件保存为 config.json。
请注意,函数参数值与我们在配置中输入的值非常接近。除了 None 值变为 null 之外,其余参数保持其类型。
随着我们继续这个例子,我们将使用 JSON 格式的配置,但是您可以探索其他适合您需要的格式(例如 YAML 或 XML )。
如何将配置文件加载到 Python 脚本中?
现在我们已经在配置中为我们的步骤指定了参数,我们将使用两个模块将数据从 JSON 文件中取出并放入程序中。为此,我们将导入两个 Python 库:
当我们通过命令行界面运行 Python 脚本时,argparse 库允许我们指定配置文件的名称。
json 库用于将我们的配置内容从 JSON 格式转换成 Python 对象。
在 main 中,我们使用 argparse 来使用配置文件的绝对路径,并使用 json 来解码文件内容:
从配置中传递程序设置现在就像运行python pipeline . py-c config . py一样简单
我如何利用配置的内容?
此时,我们已经将 JSON 文件的内容作为 Python 字典加载了(在我们的代码中方便地称为“config”)。
回想一下,我们拆分了配置,这样管道的每一步都由一个字典来表示,该字典映射处理阶段的名称以及该阶段的相关参数。在管道的 Python 代码中,我们用一个函数来表示处理的每个阶段。请注意,配置中的阶段参数与函数参数名称相匹配。这很方便,因为对于配置中的每个阶段,我们可以使用**运算符提取每个处理阶段的参数:
这是一种过于简单的方法,但是您可以将这种设计扩展到更复杂的管道,这些管道可能跨越许多源文件。如果您的管道步骤需要运行多个 Python 脚本,您仍然可以使用统一的配置文件,但是只选择您在每个步骤中需要的配置部分。
在本文中,我们研究了一种利用配置文件设计 Python 管道的方法。尽管它很简单,但这一功能帮助我们更清楚地了解管道的处理过程,并让我们对训练模型所用的数据充满信心。查看本文的代码这里开始在您自己的数据科学管道中实施配置!
使用面向对象编程改善您的数据争论
按目标
munging 和 OOP 令人惊讶的成功结合
“你永远不会孤独”(摄影:Pexels)
数据科学的一个肮脏秘密如下:
“你不会花时间去发现辉煌的新算法和开发尖端的神经网络,事实上,你会把大部分时间花在清理、管理和操纵数据上”
这是一个简单而不可回避的事实的结果——现实世界中的数据通常不会被很好地包装在顶部有一个漂亮的蝴蝶结的熊猫数据帧中。由于更高质量的数据产生更高质量的模型,这是我们不能忽视的过程的一部分。
不久前,我写了一篇关于争论数据的技术的博客,尽管其中包含错误,但至少是在一个数据框架中构建的。但是如果你连那个都没有呢?回想一下我在 Target 上一篇来自 的博客——我们使用 Splinter 从英超网站上的比赛页面中收集数据。
通过自动化与网页的交互,从您的抓取中获得更好的数据
towardsdatascience.com](/elevate-your-webscraping-with-splinter-a926eee7f7d9)
这为我们提供了我们想要进行的分析/建模所需的所有数据。但这不是最友好的格式——我编写的抓取代码将所有内容放入嵌套字典中,其模式看起来有点像这样:
**MATCH_DICTIONARY
-> MatchID** (str)
**-> GameWeek** (str)
**-> Events** (long list of strings)
**-> Stats**
-> HomeStats (dictionary of team stats)
-> AwayStats (dictionary of team stats)
**-> Players**
-> HomeTeam
-> TeamName (str)
-> StartingPlayers (list of strings)
-> Subs (list of strings)
-> AwayTeam
-> TeamName (str)
-> StartingPlayers (list of strings)
-> Subs (list of strings)
值得注意的是,首先收集这些数据作为字典并不是不合理的——每场比赛中的“事件”数量因游戏而异,所以我们不能很好地将它们存储在关系表中。此外,dictionary 方法意味着比赛数据与 MatchID 相关联,match id 是每个游戏的唯一标识符,在建模过程的后期非常重要。
无论如何,我们需要以某种方式获取这些数据,并将其转换成模型友好的格式。然而,操作嵌套字典的代码很快就会变得混乱,也许更危险的是,字典是可变的数据结构——也就是说,它们的内容可以被编辑。因此,以目前的字典形式保存数据可能会有风险。
幸运的是,还有另一种方法——面向对象编程(OOP)。
对象的简要介绍
在深入我们的具体示例之前,让我们后退一步,在更概念性的层面上考虑“对象”——尤其是在 Python 的上下文中。你可能熟悉 Python 是一种“面向对象的语言”,但这实际上意味着什么呢?
非常粗略地说,一个“对象”是 Python 中的一个东西,我们可以将它存储到我们的计算机内存中。所以这可能是一个字符串,或者一个列表,或者一个字典。它也可以是更抽象的东西,比如熊猫数据帧、matplotlib 图形或 scikit 学习模型。
大多数对象的一个关键特征是我们可以对它们执行“方法”。方法是内置的函数,对于特定类型的对象来说是特有的。例如,我们可以“调用”。lower()方法使字符串中的所有字符都变成小写:
**IN:** 'MyString'.lower()
**OUT:** 'mystring'
但是,如果我们尝试调用。lower()方法,那么我们会得到一个错误——这个方法在不是字符串的对象的上下文中没有意义。
这就把我们带到了“类”的概念上。在 Python 中,类是两件事的组合:
- 一种类型的对象。
- 我们可以在该类型的对象上调用的一系列“内置”方法。
“字符串”是 Python 中一个类的例子。我们可以创建“String”类型的单个对象(例如,“Hello World”、“MyString”或“我喜欢数据科学”),并且我们有一系列可以调用它们的方法。重要的是,这些方法是“内置”在对象中的。
我们可以看到,这些方法是“内置”在对象中的——在 Jupyter notebook 的点击标签后,它会自动获取我们可以调用的可用方法。
同样值得注意的是,对象通常有属性(我们可以查找)。例如,熊猫数据帧有一个“形状”属性,它告诉我们它有多少行和列。类似地,它的列名和索引也是属性。当然,同一类的两个对象的属性可以不同(不同的数据帧可能有更多或更少的列和行),但它仍然是一个我们可以对其调用相同方法的对象。
查找数据帧的一些属性
如果类比有帮助的话,你可以把我的宠物寿司想象成一个物体。特别是,她是‘猫’类的‘对象’。因此,她与同类的其他对象(例如,其他猫)共享一些内在特征,但是她具有与其他猫不同的属性,例如“毛发长度”或“眼睛颜色”。
重要的是,她还有一些我们可以在她身上执行的“内置”方法,例如。笔画()。feed()或。playfight(),我们也可以在其他猫身上进行。当然,如果我们试图在其他类的对象上调用这些方法,比如灯、自行车或饼干,那么我们会得到一个错误。
或者至少咨询心理医生。
寿司,一个“猫”类的对象,为分散数据科学工作的注意力而高度优化…
创建自定义类
这一切都很好,尽管您可能想知道这与数据争论到底有什么关系。
好了,事情变得有趣起来——Python 允许您创建自己的类。这意味着,如果您有本质上相似但略有不同的事物的数据表示(比如我们的嵌套字典包含所有匹配数据),那么最好将它们转换成定制的 Python 对象。
假设我想创建一个新的类‘Match’。这个类的对象看起来像什么?如上所述,每个匹配对象都有一组属性:
- 每个队参加比赛的队员
- 该场比赛的统计数据(例如控球率、铲球次数等。)
- 这场比赛的解说
我们也可以定义方法(记住,这些只是特定于类的“函数”)。例如,我们可以有一个方法来输出一个数据帧,显示每个玩家的上场时间。
首先,我们必须以 Python 能够理解的方式定义所有这些。让我们一点一点地建立这个定义。
类定义总是以或多或少相同的方式开始:
**class Match(object):**
第一行告诉 Python 你正在定义一个类,就像‘def’告诉 Python 你正在定义一个独立的函数一样。给定所需的类名(标准格式指南建议您使用 Pascal Case 来实现),然后将短语“object”放在括号中。
添加到这个类中的第一个东西就是所谓的“构造函数”。请记住,这样做的目的是将一个嵌套的字典(类型为“dictionary”)转换成一个新的对象(类型为“Match”)。构造函数是 Match 类中内置的函数,它将创建新的对象。
class Match(object):
**def __init__(self, match):**
不管我们定义的是什么类,构造函数的语法都是一样的。它是用短语 init(两边有两个下划线)定义的,有两个变量:
- self’(你可以把它想成一个虚拟变量,指的是我们将要创建的对象)。
- match '(这只是一个占位符,用于创建新对象,在我们的例子中是匹配信息的嵌套字典)。
类别的建构函式会指派属性给新建立的物件。让我们开始为我们的“匹配”对象构建这些。每场比赛的两个基本属性是唯一的比赛 ID 和比赛发生在赛季的哪一周。这两个数据点都可以在我们的嵌套字典的第一层中找到:
class Match(object):
def __init__(self, match):
**self.match_id = match['MatchID']
self.game_week = match['GameWeek']**
所以上面的代码只是告诉我们的构造函数,我们的新对象的 match_id 属性应该等于在输入字典的’ MatchID '键下找到的值。构造函数可以非常灵活地分配属性:
class Match(object):
def __init__(self, match):
self.match_id = match['MatchID']
self.game_week = match['GameWeek'] **self.home_team = match['Players']['HomeTeam']
self.away_team = match['Players']['AwayTeam']** **self.teams = [self.home_team, self.away_team]**
注意我们是如何通过组合上面几行中定义的两个属性来创建“teams”属性的?通过这些属性定义,我们可以得到更多的信息——定义了“events”属性(从嵌套字典中提取注释字符串的列表)之后,我们可以通过过滤包含短语“Goal!”的注释字符串的列表来定义“goals”属性
class Match(object):
def __init__(self, match):
self.match_id = match['MatchID']
self.game_week = match['GameWeek'] self.home_team = match['Players']['HomeTeam']
self.away_team = match['Players']['AwayTeam'] self.teams = [self.home_team, self.away_team] **self.events = match['Events']
self.goals = list(filter(lambda x: 'Goal!' in x, self.events))**
我们可以继续以同样的方式添加我们需要的所有不同的属性。
一旦我们完成了,我们就可以开始创建(或者用技术术语来说是“实例化”)我们实际的匹配对象。这非常简单—我们只需使用匹配字典并执行以下操作:
my_match_object = Match(my_match_dict)
完成这些后,我们可以看到我们的对象拥有我们所期望的所有内置属性和方法。
当我们第一次单独运行变量名时,我们可以看到它是一个匹配对象。然后,我们在句点后点击 Tab,以查看所有内置的属性和方法。
所以现在,我们不必通过一堆烦人的字典查找(甚至更糟)来从每个匹配字典中获取我们需要的数据,我们只需在 match 对象上调用一个简单的方法/属性查找。
管理对象
在这一点上,值得考虑我们如何存储这些对象。记住一个英超赛季有 760 场比赛,用它自己的变量存储每个比赛对象(正如我在上面的例子中所做的)是非常不切实际的。
具体采用什么样的对象管理策略取决于个人。就个人而言,将匹配对象存储在一个列表中就足够了——我能够使用简单的列表理解来创建它,因为匹配字典本身已经在一个列表中了。
match_object_list = [Match(i) for i in match_dict_list]
您还可以将它们存储在字典中,使用匹配 ID 作为关键字(使用字典理解)。
{Match(i).match_id : Match(i) for i in match_dict_list}
如果你觉得特别有趣,还可以将对象作为元素存储在熊猫数据帧中。
让事情更上一层楼
创建了我们的 Match 类之后,我们可以考虑创建另一个类——Events。请记住,我们比赛中的字符串是从英超联赛网站上的比赛评论中刮出来的。
这些事件字符串是相似的,因为它们描述了比赛中某个特定时刻发生的事情,并且描述了哪些球员/球队做了那些事情。但是同样,作为字符串,它们对数据分析不是特别有帮助。
如果有某种方法可以获取一个事件字符串,并自动查看它涉及哪个玩家,或者事件发生的时间,或者事件的结果是什么,这不是很好吗?好吧,OOP 可以做到这一点!在这种情况下,我们可以使用另一个技巧—子类。
子类是一种承认不同的类可以共享一些特征,但不能共享其他特征的方式。举个例子,让我们想想足球比赛中的不同事件——我们可能会有射门、犯规、角球、换人等等。这些事件都有一些共同的特征,例如,它们都有一个时间戳,告诉我们它们在比赛中发生的时间。因此,我们可以有一个以“时间”为属性的“事件”类。
然而,可能有一些特定于射门的特征(例如,球员从球场的哪个位置射门,或者射门是踢球还是头球)。显然,这些属性对“替代”事件没有意义。因此,我们可能需要一个“Shot”类来捕获这些附加信息。
类似地,有不同类型的射门——进球、扑救、失误等等,它们可能有特定的属性。因此,我们可能需要“目标”、“保存”和“错过”类。您会注意到,我们最终得到了一种相互关联的类树:
**-> Events**
**-> Shots**
-> Goals
-> Misses
-> Saves
**-> Corners
-> Fouls**
注意——在这个树中,虽然每个额外的“分支”增加了新的属性,但是它保持了上一层的属性。因此,“镜头”将保留我们在“事件”类中定义的属性,“目标”将保留我们在“镜头”类中定义的额外属性。
这有道理;每一个进球都是一种射门类型,每一次射门都是一种事件类型,所以合乎逻辑的是,进球至少与事件具有相同的属性。这种逻辑是“类继承”的本质,也是我们使用子类的关键动力之一。此外,我们可以很容易地实现这一点。考虑以下描述目标的事件字符串:
"64.进球!进球!利物浦 4,诺维奇城 1。提姆·普基(诺维奇)从禁区中央右脚射门,球射向左下角。埃米利亚诺·布恩迪亚传球助攻。”
我们可以像以前一样声明我们的事件类,创建属性来返回原始事件文本(即上面的字符串),以及它在匹配期间发生的时间:
class Event(object):
def __init__(self, event_string):
self.event_text = str(event_string)
self.time = event_string.split('.')[0]
然后,我们可以创建一个子类“Shot”,这是一种特定类型的事件。注意,这一次,我们没有使用短语“object”,而是传递了希望新子类继承的“超类”的名称(即“Event”)。
class Shot(Event):
def __init__(self, event_string):
super().__init__(event_string)
超级()。init 短语只是告诉 Python,每当我们实例化一个新的“Shot”对象时,查看超类的所有属性,以及我们特别在新子类中定义的属性(我们将在同一个 init 构造函数中定义这些属性)。
当然,如果我们想创建一个’ Goal ‘子类,我们可以再次这样做,用’ Event ‘代替’ Shot ‘(因为’ Goal ‘将是’ Shot '的子类)。
class Goal(***Shot***): def __init__(self, event_string):
super().__init__(event_string)
注意:我在这里没有包括完整的 Event 类定义——这个单元运行了将近 200 行代码,其中的正则表达式比单个博客中通常认为的健康的要多…
将这一切结合在一起
回想一下前面的 gif,Match 类有一个属性‘goals’。这将返回一个字符串列表,每个字符串都是这场比赛中一个进球的注释。
假设我们使用 Match 对象中的一个目标来实例化一个 Goal 对象,使用我们新编码的“Goal”类—记住我们是这样做的:
my_goal = Goal(my_event_string)
然后我们看到,我们可以开始直接从目标对象中提取关于该目标的信息:
同样,这是一种比试图用特殊函数攻击数据更为简洁的处理数据的方式,特别是考虑到我们将不得不在这个项目中分析的目标数量!
这只是用 OOP 实现的数据管理的冰山一角。为了让你有个感觉,这是我为 Match 类做的一个方法。简单打电话。shots_table()在一个匹配对象上,它产生了一个详细的 pandas 数据帧,其中包含了匹配过程中拍摄的所有照片。
假设我已经有了一个匹配对象的列表,您可以看到,在用 OOP 进行了初步的调查后,我可以用几行代码创建一个记录本赛季英超联赛中每一次射门的数据帧:
df = pd.DataFrame()**for** match **in** match_object_list:
df = pd.concat([df, match.shots_table()])
我们已经从嵌套字典列表中走了很长一段路!
这是我的博客专栏“关于目标”的最新文章,在这篇文章中,我将试图建立一个“赚钱”的梦幻英超联赛的模型。我很乐意听到关于这个博客的任何评论,或者这篇文章涉及的任何概念。欢迎在下面留言,或者通过 LinkedIn 联系我。
使用 argparse 改进您的 EC2 SSH 工作流
端口转发、远程 jupyter 笔记本访问和 tmux 自动启动
动机
数据科学工作通常需要针对巨大数据集使用高级大数据分析技术,以及并行或分布式计算来进行快速模型训练和调整。虽然大多数数据科学工作流(尤其是在探索和开发阶段)都可以在个人的笔记本电脑或台式机上执行,但由于本地开发环境在处理能力、内存和存储方面的限制,完全依赖本地开发环境通常是不切实际或不可能的。
在这一点上,云计算技术的作用,如亚马逊网络服务的弹性计算云(EC2)所提供的技术,从未像现在这样重要,特别是在将大数据应用大规模部署到生产环境中的情况下。随着此类服务的可用性改变了我们设计和实施 DS 架构的方式,它也促使数据科学家在其早期开发工作流程中更加依赖云计算的处理和存储能力。
如果你还没有使用过 EC2(或任何其他类似的云服务),这个 DataCamp 教程提供了一个很好的、激励人心的介绍。我每天都使用 EC2 实例,因为我相信你们中的许多人也一样,所以我想分享一个简单的 Python 脚本,它包含了一些使用argparse
模块来改进 EC2 SSH 工作流和创建最佳 EC2 实例环境的巧妙技巧。当您通过 SSH 建立连接时,该脚本将允许您:
- 每次自动启动
tmux
。 - 在浏览器中本地访问远程运行的
jupyter
服务器。
剧本
如果你对argparse
不熟悉,可以看看这篇温和的介绍或杰夫·黑尔的学习足够有用的 Python:arg parse。
将这个脚本放在您的主目录bin
中,例如/Users/jlee/bin
。我将我的文件命名为ec2
(为了方便起见,没有扩展名.py
)。然后,通过运行以下命令将该文件标记为脚本:
chmod +x ~/bin/ec2
破解密码
1.位置参数:会话
# line 8 aboveparser.add_argument("session", nargs="?", help="tmux session name")
这一行添加了一个位置参数session
,它将允许您在每次 SSH 到远程实例时启动一个新的 tmux 会话(或者附加到一个现有的会话,如果名称存在的话)。
换句话说,每次你想用一个特定的名字启动一个 tmux 会话时,不再需要键入/查找 tmux cheatsheet 来记住tmux new -s new_session
。
如果您将nargs
(参数数量)设置为"?"
,参数定义将在被解析的命令行上消耗零个或一个参数。
可变参数列表
换句话说,不为session
参数传入一个字符串名称将会给你一个选项,连接到你的 EC2 实例而不用一个 tmux 会话。用以下内容扩展原始argv
列表(第 16-21 行):
# lines 24-25if args.session is not None:
argv.extend(["-t", "tmux", "-2", "new-session", "-A", "-s", args.session])
2.可选参数:jupyter
# lines 9parser.add_argument('-j', '--jupyter', action='store_true', help='forward port to use remote jupyter')
jupyter
是一个可选(相对于位置)参数,由前面的--
(或其简称-j
)表示。对于可选参数,我们指定了一个新的关键字action
。如果我们给它赋值'store_true'
,如果指定了选项,它将把值True
设置为args.jupyter
。不指定它意味着False
。
因此,在下面的代码中,传递jupyter
参数将使您的localhost:8888
指向远程服务器的8888
端口。因此,当您在 EC2 实例上运行jupyter notebook
时,它会将的端口8888
转发到本地机器的8888
端口*,从而允许您从本地浏览器访问远程 jupyter 笔记本。*
# lines 22-23if args.jupyter:
argv.extend(["-L", "8888:localhost:8888"])
现在,如果您从命令行运行jupyter notebook
,它会显示如下内容:
Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
[http://localhost:8888/?token=a2bc3def5g41234ed56s78zxfxc3vdfb&token=a2bc3def5g41234ed56s78zxfxc3vdfb](http://localhost:8888/?token=a7213f1213c009f4c7a6c2eddd2fded65d538e5b9ae37876&token=a7213f1213c009f4c7a6c2eddd2fded65d538e5b9ae37876)
只需将令牌 URL 复制并粘贴到您的本地浏览器中,您就可以立即访问您的远程 Jupyter 笔记本。
收尾
如果您想要定制您的 tmux,在您的主目录中创建一个名为.tmux.conf
的配置文件。这是一个隐藏文件,所以你不会看到它,如果你ls ~
),但tmux
将选择这个文件为当前用户。
将这几行添加到文件中,看看您对 tmux 的新外观有多喜欢:
# ~/.tmux.confset-window-option -g window-status-format " [#I] #W "
set-window-option -g window-status-current-format " [#I] #W "
set-window-option -g window-status-current-style fg=colour240,bold,bg=colour247
set -g default-terminal "screen-256color"
结果是 tmux 状态栏在终端底部以灰色显示当前窗口,与会话中的其他窗口(绿色)相区别:
查看这个网站获得更多 tmux 定制选项。
最后,这里有一些有用的键盘快捷键:
- 在当前会话中创建一个新窗口:
Ctrl+b
,然后是c
- 转到会话中的下一个窗口:
Ctrl+b
,然后是n
- 转到会话中的上一个窗口:
Ctrl+b
,然后是p
- 从当前会话中分离:
Ctrl+b
,后跟d
命令示例
综上所述,您现在可以尝试使用这些示例命令之一运行ec2
脚本,每个命令都将创建一个 SSH 连接,但是结果略有不同:
(1) ec2 new_sesh -j
(2) ec2 -j
(3) ec2 new_sesh2
(1)将在 SSH 上创建(或附加到)一个名为new_sesh
的 tmux 会话,结果是当您调用 jupyter notebook 时,它会将您的远程服务器的端口 8888 转发到您的本地机器的端口 8888。
(2)将使用端口转发将您 SSH 到您的 EC2 实例中,但不会启动 tmux(因为您没有为session
提供名称)。
(3)将创建(或附加到)一个名为new_sesh2
的 tmux 会话,但没有端口转发(即,您将无法从本地浏览器访问远程 jupyter 笔记本)。
最后,当然可以使用 bash 别名来达到(1)-(3)的效果。例如,将下面一行添加到您的.bashrc
或.bash_profile
脚本并运行ssh-jlee
将启动一个名为default
的 tmux 会话,并开始通过 SSH 连接进行8888
→ 8888
端口转发:
alias ssh-jlee='ssh -i ~/.ssh/mykey.pem ubuntu@10.999.9.999 -L 8888:localhost:8888 tmux new-session -A -s default'
然而,我认为使用argparse.ArgumentParse
创建一个解析器对象并传递一系列字符串作为命令行参数,比使用多个硬编码(1)-(3)中所有变量行为的 bash 别名更灵活、透明、可控。
使用缺失数据改进您的模型|使用 NumPyro 进行插补
用贝叶斯方法充分利用缺失数据。使用蒙特卡罗方法提高模型性能并制定比较基准。
一个丢失的框架随时准备把你从你的模型上扔下去。维尔莫斯·海姆在 Unsplash 上拍摄的照片。
丑陋的数据
你如何处理丢失的数据、数据框中的间隙或有噪声的参数?
你花了几个小时在工作中,在实验室里或者在野外为一个有趣的研究问题或假设生成或管理一个数据集。可怕的是,您发现参数的一些测量值丢失了!
另一个可能让你困惑的情况是意外噪声 e,它是在实验中的某个时刻引入的,它注定了你的一些测量结果是极端异常值**。
有时你不能仅仅因为有 25%的条目丢失就从你的分析中排除一大堆参数,你应该这样做吗?**
不用担心,有一种方法可以让你充分利用你的数据集。
在本帖中,我们将使用概率编程【5】来填补你的模型可能会遇到的空白。
模拟缺失数据
让我们从头开始构建一个问题。勤奋的读者很可能熟悉鸢尾花的数据集。它在某种程度上是 ML 社区中“hello-world”的一部分;紧挨着著名的 MNIST。我们使用 sklearn.datasets 获取数据,并将其放入熊猫数据框中。:
iris_data = datasets.load_iris()
iris_df = pd.DataFrame(data=np.c_[iris_data['data'], iris_data['target']],
columns=iris_data['feature_names'] + ['target'])
为了让这个例子更加清晰易懂,我们只从给定的 3 个物种中选择 2 个。:
iris_df["target"] = iris_df.target.astype("int")
iris_df = iris_df[iris_df.target.isin([0, 1])]
现在你应该看到鸢尾花数据的 100 个测量值(行是我们的 n )。
我们现在随机取出 50%的花瓣长度条目**,并赋予 NaN 值,带有一点数字和熊猫的魔力。**
random_vec = np_random(iris_df["petal length (cm)"].shape)<0.5 iris_df["petal length (cm)"] = iris_df["petal length (cm)"].where(random_vec, other=np.nan)
为什么要关心所有的尺寸?
当我们绘制数据时,我们可以清楚地看到花瓣长度和花瓣宽度可以帮助我们识别我们正在观察的物种(目标列)(见图 1)。
图 1:花瓣长度和宽度与花种 aka 的关系。目标列。我们可以清楚地识别出两个集群。
我们可以通过仅使用花瓣长度参数对组进行回归来进行更清晰的区分,我们之前已经残酷地对该参数进行了删减。
图 2:已识别物种(目标)上花瓣长度参数的回归。
糟糕的是大约一半的数据丢失了。那我们现在怎么办?
不同深浅的思念
首先,至少有两种程度的缺失数据:
完全随机失踪(MCAR)和随机失踪(马尔)。
关于这方面的正式介绍,参见贝叶斯数据分析[1]第 18 章。
一个重要的区别是,MCAR 将您的缺失参数视为独立于您观察到的测量值(姑且称之为 y )。
在我们的例子中,我们相信我们的参数是随机丢失的或被标记的**。这意味着我们不认为值丢失的可能性取决于参数属性*,但是*我们认为在我们的长度测量和目标物种之间存在潜在的某种关系。**
回归模型来统治它们
首先,让我们从目前掌握的数据中提取一些基本信息:
>>> print("Petal Length μ = {}".format(iris_df["petal length (cm)"].mean()))
>>> Petal Length μ = 2.8072727272727267
>>> print("Petal Length σ^2 = {}".format(iris_df["petal length (cm)"].std()))
>>> Petal Length σ^2 = 1.4923079993698234
现在我们建立模型来做插补。为此,我们使用工具 NumPyro ,这是一个轻量级的概率库。NumPyro 为重型概率语言 Pyro 提供了一个类似 numpy 的后端。
这对于后期的模型拟合来说就派上用场了。我们将使用 MCMC 方法来执行贝叶斯推理,并在此过程中模拟缺失条目。
为了使其更适用于用例,我们将它与贝叶斯回归模型结合起来。这意味着我们想在给定所有已知参数的情况下模拟物种间的差异。因此,我们的变量是:
sepal length := s_len, sepal width := s_width, petal width := p_width and, lastly petal length := **p_len**, which includes our **missing data**.
为了简单起见,对于这些参数中的每一个,我们引入一个可学习的偏差,该偏差来自于一个规则的正态分布,即高斯分布。所以让我们开始吧。:
def model(s_len, s_width, p_len, p_width, target=None):
b_s_len = numpyro.sample("b_s_len", dist.Normal(0,1))
b_s_width = numpyro.sample("b_s_width", dist.Normal(0,1))
b_p_width = numpyro.sample("b_p_width", dist.Normal(0,1))
...
人们可以对上面的偏差参数做一点调整,但是对于我们的初始运行,正态高斯工作得相当好。现在是微妙的部分。我们引入分布**,我们假设它位于我们的半破坏参数下**。与许多事情一样,我们假设这是高斯分布,在我们的情况下,这是一个合理的假设。因此,我们使用之前计算的值 loc=μ (2.8 +/- 0.2)和 scale=σ (1.5+/-0.2)。****
...
len_mu = numpyro.sample("p_length_mu", dist.Normal(2.8, 0.2))
len_sigma = numpyro.sample("p_length_sigma", dist.Normal(1.5, 0.2))
...
有见识的读者清楚地看到μ和σ本身是我们在此过程中建模的概率参数。对于这两种情况,我们都假设为高斯分布,但也可以选择更严格的分布。现在让我们找到我们要建模的目标。我们用 numpy 函数定位向量中缺失的位置。
...
len_is_nan = np.isnan(p_len)
len_nan_idx = np.array(np.isnan(p_len).astype(int)).nonzero()[0]
len_impute = numpyro.sample("len_impute", dist.Normal(len_mu[len_nan_idx], len_sigma[len_nan_idx]).mask(False))
...
此外,我们告诉 NumPyro 使插补值(len _ imput)成为一个可学习的参数**,并用我们丢失的值填充尽可能多的零。
简而言之——注意给面具带来一些光亮:我们的模型有一个预测模式,当没有提供目标(又名物种)时发生。当我们调用预测功能时,我们使用采样μ和采样σ从引入的分布中进行采样。
确保只在矢量中的 NaN 位置应用。**
numpyro.sample("len_impute", dist.Normal(len_mu[len_nan_idx], len_sigma[len_nan_idx])
然后,我们将估算值放入正确的位置和样本,实际长度值作为我们的观察值。为此,我们使用 JAX 库和一些有效的索引更新——线性代数风格。:
...
p_len = ops.index_update(p_len, len_nan_idx, len_impute)
numpyro.sample("p_length", dist.Normal(len_mu, len_sigma), obs=p_len)
...
在我们的长度采样中,我们将实际长度测量值作为过程中的观察值包括在内。
把东西放在一起
我们继续将估算值放入回归模型。为了让贝叶斯模型起作用,我们必须符合我们的目标 y** (物种)。
因为我们有两个类,所以我们通过调用伯努利分布来实现。因此,我们在伯努利过程中问,我们看到的是 y=0 还是 y = 1——两个类别,两种可能的结果。**
贝叶斯回归代码
我们引入两个参数,其中之一是长度参数的偏差项。
...
l = numpyro.sample("l", dist.Normal(0, 1))
b_len = numpyro.sample("b_length", dist.Normal(0, 1))
logits = l + b_len * p_len
logits = logits + b_s_len*s_len + b_s_width*s_width + b_p_width*p_width
...
现在到了神奇的部分。我们将 logit 用于伯努利,它由我们放入模型的所有参数组成,与它们的偏差相互作用。这个过程中的观测值现在就是目标参数——我们的 y。
numpyro.sample("target", dist.Bernoulli(logits=logits), obs=target)
完整的 NumPyro 模型
def model(s_len, s_width, p_len, p_width, target=None):
b_s_len = numpyro.sample("b_s_len", dist.Normal(0,1))
b_s_width = numpyro.sample("b_s_width", dist.Normal(0,1))
b_p_width = numpyro.sample("b_p_width", dist.Normal(0,1))
# impute length
len_mu = numpyro.sample("p_length_mu", dist.Normal(2.8, 0.2))
len_sigma = numpyro.sample("p_length_sigma", dist.Normal(1.5, 0.2))
len_is_nan = np.isnan(p_len)
len_nan_idx = np.array(np.isnan(p_len).astype(int)).nonzero()[0]
len_impute = numpyro.sample("len_impute", dist.Normal(len_mu[len_nan_idx], len_sigma[len_nan_idx]).mask(False)) p_len = ops.index_update(p_len, len_nan_idx, len_impute)
numpyro.sample("p_length", dist.Normal(len_mu, len_sigma), obs=p_len)
l = numpyro.sample("l", dist.Normal(0, 1))
b_len = numpyro.sample("b_length", dist.Normal(0, 1))
logits = l + b_len * p_len
logits = logits + b_s_len*s_len + b_s_width*s_width + b_p_width*p_width
if target is None:
# prediction case
probs = expit(logits)
numpyro.sample("probs", dist.Delta(probs)) numpyro.sample("target", dist.Bernoulli(logits=logits), obs=target)
运行它
为了拟合该模型,我们利用 MCMC 方法使用贝叶斯推理;具体的坚果取样器。坚果采样器的美妙之处在于它是一种无需干预的方法,因为它通过问题空间步进,自己调整步长**。我们让它有 5000 次预热或老化运行,在这个过程中被丢弃,让它运行 10000 次迭代,只是为了确保安全。我们的推断以 0.96 或 96%的接受概率结束——太棒了!。接下来,我们来看看发生了什么事的摘要:**
mcmc = MCMC(NUTS(model=model), 5000, 10000, num_chains=1)
mcmc.run(random.PRNGKey(42), **iris_data)mcmc.print_summary()
图 3:在 MCMC 之后的模型总结表明收敛和固体 R-hat 值为度量。
输出清楚地显示了我们在推理中采样的参数的收敛性。具体来说,R-hat 值都> = 1.0。
然后,我们抽取样本并可视化后验概率——我们再次依赖 JAX 的格式化魔法:
samples = mcmc.get_samples()
posterior_mu = jnp.expand_dims(samples['l'], -1) + \
jnp.expand_dims(samples['b_length'], -1)*iris_df["petal length (cm)"].values + \
jnp.expand_dims(samples['b_s_len'], -1)*iris_df["sepal length (cm)"].values + \
jnp.expand_dims(samples['b_s_width'], -1)*iris_df["sepal width (cm)"].values + \
jnp.expand_dims(samples['b_p_width'], -1)*iris_df["petal width (cm)"].values
我们可以看到后面的图。我们找到了。我们两个阶级之间的明显区别。在界限上下有相当多的抖动,但是回归线看起来更好。
公平地说,这也是因为与我们早期的单参数模型相比,我们在回归中使用了所有可用的参数。
上面的樱桃是回归线周围的 90%置信区间,取自我们的后验线。这让事情看得更清楚。
图 4:对具有 90%置信区间的 MCMC 样本的最终贝叶斯回归。
我们已经证明,我们可以通过 MCMC 采样来填补模型中的空白。
对于精通代码、好奇的读者来说,最终的笔记本可以在我的 github 和 Google-Colab 上找到。
两个以上的类和分类数据?
这些问题也可以用 NumPyro 的工具包来回答:
- 除了在模型中进行伯努利试验,您还可以使用多项式进行贝叶斯推断。
- 我们也可以使用分类数据进行模型拟合。那看起来很不一样。我们不使用一维数组,而是依赖尽可能多的维度。
适合分类数据而不是连续数据的一个很好的例子**是关于年龄插补的优秀 Kaggle 笔记本,给出了 Titanic 数据集的其他参数:
使用 Kaggle 笔记本探索和运行机器学习代码|使用《泰坦尼克号:灾难中的机器学习》中的数据
www.kaggle.com](https://www.kaggle.com/fehiepsi/bayesian-imputation-for-age)
该做的和不该做的
请坚持你所拥有的!
不要捏造数据。我怎么强调都不为过。我们在这里走的是一条很细的线。
如果你的实验产生的数据不足,或者你不得不丢弃一些数据,那么输入数值可能不是最好的办法。即使您的数据可能以这种方式呈现。当与客户或学术期刊交谈时,你不会因为以下几点而得分:我们将参数 X 和 Y 模拟为 85.3%——无论模型看起来有多好。
请声明您使用了什么方法、分布和假设来制作模型和估算,以获得最大的透明度。
一个建议是,你可以随时取出数据,在没有遗漏测量值的情况下拟合一个模型,并将其与最终有估算值的模型进行比较。这可以成为一个引人注目的故事。
结论
我们已经走过了数据插补的过程,从最初杂乱的数据框架到最终的贝叶斯推理过程和拟合后验分布。
我们已经展示了贝叶斯回归如何受益于估算向量,以及包括置信区间在内的输出结果。
概率编程可以成为你日常工作中的一个强有力的工具。
祝您的模型和分析好运!
参考
[1] A .盖尔曼、J.B .卡林等人。艾尔。、 贝叶斯数据分析 。第三版。
[2] R .麦克勒瑞斯。统计学反思。2016.CRC 出版社。
[3]优步技术公司。使用 NumPyro 进行回归。 2019。 NumPyro 文档。
【4】m .贝当古。哈密尔顿蒙特卡罗概念介绍。 2020 年上ArXiv。
【5】CS 4110—编程语言与逻辑。概率编程。 2016 年。康奈尔大学。**
通过添加无监督的辅助损失来提高神经网络的泛化性能
作为一种正则化形式的无监督重建损失。
Sebastien Gabriel 在 Unsplash 上的照片
深度神经网络有一个很大的过拟合问题,特别是在应用于少量标记数据时。研究人员设计了多种方法来解决这个问题,如 L1/L2 权重正则化、辍学、迁移学习和多任务学习。
在这个项目中,我们将重点关注使用多任务学习作为一种提高神经网络泛化性能的方法。这里实现的想法受到了三篇非常有趣的论文的启发:
前两篇论文试图解释为什么多任务学习可以提高单个任务的表现,它们提供的一些可能的解释是:
表征偏差:
—作者图片
如果我们同时在任务 T 和 T '上训练一个网络,这个网络就会偏向服务于这两个任务的表征。这使得网络最有可能推广到新的任务。
正规化:
使用多任务学习使得网络不太可能过度适应来自训练数据的噪声,因为它减少了可能的解决方案的数量,因为 MTL 的解决方案需要同时对所有任务起作用。
注意力聚焦:
对多个相关任务的训练可以在什么是相关特征以及什么只是噪声上给模型更强的信号。
第三篇论文考虑了只有一个监督任务 T 的情况,因此作者增加了一个新的人工和非监督的重建输入的任务。他们在一个简化的设置中证明,增加重建损失提高了监督任务的泛化性能,并显示了一些支持他们假设的经验结果。
数据:
在接下来的实验中,我们使用免费音乐档案(FMA)小版本。这是一个包含 8000 首歌曲片段的数据集,分为 8 种类型:
{
"International": 0,
"Pop": 1,
"Instrumental": 2,
"Hip-Hop": 3,
"Electronic": 4,
"Experimental": 5,
"Folk": 6,
"Rock": 7
}
我们以 70%-10%-20%的比例将数据集分为 Train-Val-Test,并将原始音频波形转换为 Mel 频谱图,然后将它们馈送到网络。关于预处理的更多细节,你可以看看我以前的一个项目:
Mel 光谱图示例—作者提供的图像
型号:
我们沿着时间轴应用基于 LSTM 的神经网络来分类音乐流派。
音乐流派模型-作者图像
添加脱落图层作为额外的正则化,并使重建任务对模型来说更具挑战性。
我们使用 Pytorch Lightning 来实现这个模型,转发函数看起来是这样的:
**def** forward(self, x):
x = self.do(x)
x, _ = self.lstm1(x)
x_seq, _ = self.lstm2(x)
x, _ = torch.max(self.do(x_seq), dim=1)
x = F.relu(self.do(self.fc1(x)))
y_hat = self.fy(x)
x_reconstruction = torch.clamp(self.fc2(self.do(x_seq)), -1.0, 1.0)
**return** y_hat, x_reconstruction
现在我们将损失定义为分类损失和重建损失之间的加权和,如下所示:
损失=损失 _ 分类+ λ *损失 _ 重建
其中,λ是一个超参数,它有助于缓解两个损失的比例不同这一事实,同时还能更好地控制我们希望赋予辅助任务的重要性。损失定义如下:
**def** training_step(self, batch, batch_idx):
x, y = batch
y_hat, x_reconstruction = self(x)
loss_y = F.cross_entropy(y_hat, y)
loss_x = F.l1_loss(x, x_reconstruction)
**return** loss_y + self.reconstruction_weight * loss_x
结果:
在实验中,我们尝试了多个λ值,以查看哪一个效果更好,基线为λ= 0,这意味着辅助损耗被忽略。
我们可以看到,与基线相比,增加重建损失(λ = 10 和λ = 2)会产生更好的性能。
就分类准确度而言,我们有:
- 随机猜测: 12.5%
- **基线:**准确率= 47.5%
- λ =10 :精度= 51%
当使用相同的分类体系结构时,增加重建损失给出了超过基线约 3%的精度改进。
结论:
在这个项目中,我们证明了向神经网络添加辅助的无监督任务可以通过作为正则化的附加形式来提高其泛化性能。添加重建损失的方法在 Pytorch Lightning 中很容易实现,但代价是我们需要优化新的超参数λ。
复制结果的代码在这里分享:https://github.com/CVxTz/ReconstructionAuxLoss
使用这些格式和文档模板改进您的 SQL
使用更好的 SQL 格式和文档,减少代码审查和知识转移的痛苦。
艾萨克·史密斯在 Unsplash 上拍摄的照片
介绍
您是否遇到过用于分析的复杂 SQL 查询?
你是否很难理解代码本身和其下的业务逻辑?
我做到了。而且有时候,是跟我过去的疑问!
为了节省阅读我的代码的每个人(包括我自己)的时间,我尝试将两个模板应用到我的查询中,并发现它们非常有帮助:
- 提高代码本身的质量
- 减少代码审查时间
- 并改善知识转移
你能期待什么
在本文中,我将与您分享我使用的这两个模板。
模板#1: 记录 SQL 查询中的上下文和假设
模板#2 :格式化 SQL 查询
为了演示它们的用法,我将通过一个使用 MySQL 的例子来总结新冠肺炎前后的销售情况。
希望这些模板能派上用场!尤其是在新冠肺炎启动后,远程工作成为我们的新常态,这增加了过度沟通的重要性,以确保每个人都在同一页面上。
模板# 1:SQL 查询中的文档上下文和假设
1.模板
在编写查询之前,请列出这些信息:
- 此查询的重要业务上下文
- 对查询结果的期望
- 对业务逻辑和数据的任何假设
/*
CONTEXT:
- add a brief description of why we need this queryRESULT EXPECTATION
- add a brief description of your expectations for the query resultASSUMPTION:
- add assumption about business logic
- add assumption about data
*/
2.行动中的模板
将此模板应用于我们的销售总结示例:
/*
CONTEXT:
- Our company wants to understand if COVID has any impact on sales in stores around Chicago.RESULT EXPECTATION:
- This query returns total sales (in USD) for each of our stores in Chicago every month before and after COVID, starting from 2019-03-01.ASSUMPTION:
- Dates before 2020-03-01 are considered "Before COVID"
- Each transaction has a unique id, so we do not expect duplications in our transaction table
- There are some spam transactions we have identified after COVID, so we will filter these out
*/
3.利益
在我们进行查询之前,对业务环境、结果预期和假设进行简要描述有很多好处:
- 它让我们在编写查询时专注于查询的主要目标
- 它帮助读者快速建立对我们的查询的价值和期望的高层次理解
- 它帮助代码评审员根据期望为查询创建初始测试
记住,这一步是一个迭代过程。在编写查询时,我们可能不得不反复修改文档。
模板#2:格式化 SQL 查询
1.模板规则
格式化 SQL 查询有许多规则。简单来说,这些是我遵循的主要规则:
- 使用大写字母突出显示保留的关键字(例如 SELECT,WHERE)
- 使用缩进清楚地显示查询或子查询的开始和结束位置
- 对于长而复杂的查询,在任何主要的子查询或上下文连接之前包含一个注释
- 请给出列的来源及其源表或描述性表别名
2.行动中的模板
此查询仅用于演示
3.利益
这种查询格式对可读性有很多好处。以下是一些例子:
- 理解查询的整体结构(即选择了哪些列,有多少个连接,应用了哪些过滤器)
- 节省时间来确定测试子查询的开始和结束,因为我们可以在同一条垂直线中看到子查询的开始和结束括号
- 避免迷失在复杂的查询中,在整个查询中都有注释
组合模板#1 和模板#2
在我们的例子中,结合这两个模板,我们将得到:
/*
CONTEXT:
- Our company wants to understand if COVID has any impact on sales in stores
around Chicago.RESULT EXPECTATION
- This query returns total sales (in USD) for each of our stores in Chicago
every month before and after COVID, starting from 2019-03-01.ASSUMPTION:
- Dates before 2020-03-01 are considered "Before COVID"
- Each transaction has a unique id, so we do not expect duplications
in our transaction table
- There are some spam transactions we have identified after COVID,
so we will filter these out
*/SELECT
store_info.id,
store_info.name AS store_name,
DATE_FORMAT(transactions.date, "%Y-%m") AS transaction_month,
SUM(transactions.total_amount) AS total_amount
FROM
transactions
LEFT JOIN
-- get all stores in Chicago
(
SELECT
id,
name
FROM
stores
WHERE
city = 'Chicago'
) AS store_info
ON
transactions.branch_id = store_info.id
WHERE
transactions.date >= '2019-03-01'
-- filter spam transactions
AND transactions.id NOT IN
(
SELECT
id
FROM
spam_transactions
)
GROUP BY
store_info.id,
store_info.name,
DATE_FORMAT(transactions.date, "%Y-%m")
额外小费
这些模板并不是唯一的模板。找出最适合你和你的团队的方法是一个反复试验的过程。
最终确定格式样式后,手动设置每个查询的样式可能会很累。许多 SQL IDE 都有一个选项可以自动完成这个过程。然而,如果它仍然不能满足您的需要,还有其他的工具。尝试使用这些关键字进行搜索:“SQL 格式化程序”
对于 Python 用户,我一直在摆弄 sqlparse 。你可以在这里获得更多的信息。
感谢您通读整篇文章!希望这些模板对你有帮助。我很想知道您的想法和任何其他技巧,以使 SQL 查询更具可读性。欢迎在下面留言,或者在 LinkedIn 上联系我。
改进的朴素贝叶斯分类器解决文本分类问题。
入门
斯蒂芬·菲利普斯-Hostreviews.co.uk 在 Unsplash 上的照片
本文致力于解释最经典的机器学习分类器之一——朴素贝叶斯。在这个故事中,我将解释朴素贝叶斯和背后的理论,最重要的是将呈现这个模型的一个简单的从零开始的 实现 。对于进入机器学习领域的人来说,这篇文章将会非常有用和有趣。我也相信有 ML 经验的读者也会学到新的东西。所以,事不宜迟,让我们开始吧。
正如艾萨克·纽敦曾经说过的那样:“如果我比别人看得更远,那是因为我站在巨人的肩膀上”,这是我自己相信的事情,就好像你想创造新的东西,你必须了解以前已经做过的事情。这可以让你不要在发明的东西上浪费时间,或者寻找机会来改进以前的方法和挑战现状。
因此,我想谈谈如何使用朴素贝叶斯来解决文本分类问题,但不会触及最新的深度学习方法。这将使大多数读者能够跟踪和理解这篇文章,无论其教育背景如何。
如果你是自然语言处理(NLP)领域的新手,我建议你在阅读这篇文章之前,先看看我的上一篇文章,在那里我讨论了我们如何使用简单的概率规则来执行文本生成。
在本文中,我们将从头开始使用朴素贝叶斯分类器创建一个垃圾邮件过滤器,而不使用任何外部库。首先,我想介绍一些理论,然后我们将讨论实现部分。
理论。
贝叶斯公式。
有很多文章讨论了朴素贝叶斯以及它在概念上是如何工作的,所以我不打算深入进行理论解释,而是讨论最基本和最重要的部分。
我们都知道贝叶斯公式是这样的:
**在我们的 NLP 设置中,我们想要计算什么是 P(垃圾邮件|文本)和什么是 P(非垃圾邮件|文本)。
对于常用的术语,我将把“非垃圾邮件”称为“火腿”。
众所周知,任何电子邮件的文本通常由单词组成。因此,我们有兴趣计算 P(spam | word1,word2,…,wordN) 。以贝叶斯公式的形式来说:
当我们计算类似的概率时,但是对于火腿的情况,我们将得到以下公式:
如您所见,分母 P(w1,w2,…,wN) 【也称为归一化**】在两种情况下是相同的,因为我们的任务是确定哪个概率更高(而不是确切的值),所以我们可以去掉归一化部分。推理如下所示:**
奇怪的符号表示不确定左侧(LHS)是大于还是小于右侧(RHS)。
取消分母,因为我们知道概率是非负的。
所以最终,在贝叶斯分类中,我们得到了以下简化:
旁注:标准化部分在大多数情况下被忽略,不仅因为它是多余的,而且因为从数据中计算通常是非常复杂的,所以不必处理它简化了我们的生活!
我们为什么幼稚?
概括地说,为了判断电子邮件是否是垃圾邮件,我们需要查看哪个值更高 P(spam|w1,…,wN) 或 P(ham|w1,…,wN) 。
为了计算 P(spam|w1,…,wN) ,我们需要:
- P(垃圾邮件)–*很容易找到,因为它只是我们数据集中垃圾邮件* 电子邮件与所有电子邮件的比率。**
- P(w1,…,wN | spam)–直接计算有点困难,所以让我们使用“天真”的假设,即文本中的单词是独立的,这样我们可以将这个术语简化为:
所以最后为了计算 P(spam|w1,…,wN) 我们会做:
看起来很简单,但是,让我们不要犯一个会破坏我们结果的错误。考虑一下,如果我们需要检查一个单词 w_i 但它从未出现在垃圾邮件中,会发生什么?
我们如何计算一般情况下的概率?
换句话说: P(w_i|spam) = 0。
显然,在这种情况下,由于乘法运算,我们最终会得到 P(spam|w1,…,wN) = 0 ,这可能会导致错误的结论,因为我们永远无法假设拥有包含所有单词的完美训练数据。也有可能一个单词包含了错别字,因此系统显然不知道它。(还要考虑这个词从未在垃圾邮件和火腿邮件中出现的情况,那么我们该如何对包含这个词的邮件进行分类呢?)
为了解决这个问题,使用了一种平滑方法,确保我们将非零概率分配给任何项:
这里α ( alpha )是一个平滑参数,应该是一个非负数。
提高效率——使用对数。
正如我们之前所看到的,为了计算概率,我们将不得不进行大量的乘法运算(与我们要评估的电子邮件中的单词一样多的次数)😗***
问题是所有的概率都小于 1,一般来说都很小。众所周知,2 个小值 0 < a,b < 1 的乘积会产生一个更小的数。因此,在朴素贝叶斯方法的情况下,我们将受到我们计算机的浮点精度的限制(因为在某些情况下,spam 和 ham 方程将会收敛到零,只是因为计算机在处理非常小的数字时的限制)。
我们该怎么办?我们需要把我们的计算转移到能够记录非常小的数字的超级计算机上吗?幸运的是,这是不需要的,对数来帮助!而且我们会用对数的乘积法则。
我们使用对数的另一个原因是:
这意味着如果 A > B,则 log(A) > log(B),反之亦然
**Hence, **if** *log(P(spam|text)) > log(P(ham|text))* **then** *P(spam|text) > P(ham|text)***
所以对于我们来说,用对数来计算概率会容易得多,对数的形式如下:
实施。
照片由 Alexander Sinn 在 Unsplash 拍摄
理论说够了,让我们把理论带入生活。正如我之前提到的,我将从头开始实现朴素贝叶斯分类器,并且不会使用任何 python 的外部库,因为它允许实现多种功能:
- 它帮助我们更好地理解朴素贝叶斯背后的理论。对于刚接触机器学习领域的读者来说是非常有益的。
- 它允许我们有一些灵活性并改进标准方法。在本文中,我们将同时使用单字和双字(如果你不知道什么是双字,请阅读本文)。
完整的代码和数据集可以在我的 github 库上找到。
数据集包含原始格式的电子邮件,因此为了处理电子邮件数据,我们必须使用 python 的email
库。
现在,让我们看看支持功能:
第一个是记号化器,在你的修改中可以随意使用任何其他方法,但是这里这个方法记号化方法工作得足够好(你会看到我们的精度会有多好)。
下面你可以看到我们将用来计算垃圾邮件的对数概率的函数。
如前所述,我将在这里使用 unigrams 和 bigrams 来识别电子邮件是否是垃圾邮件,因此我们正在计算
P(spam|token_1,token_2,… token_n) ,其中 token_i 可以由一元或二元表示。这比只在单词(单字)层面上工作要好,因为我们变得不那么幼稚(因为我们接受文本中单词的某种依赖性)。
除了我们在理论部分讨论的内容,我还介绍了一些特殊的标记,如<UNK>
,如果我们偶然发现在训练集中没有见过的未知单词,我们将在推理部分使用这些标记。**
除此之外,我创建了一个记号<LONG_W>
,它对应于大于 12 个字符的长单词和,其概率的计算与所有其他记号类似。**
垃圾邮件过滤器类别。
现在让我们转到垃圾邮件过滤器类的实现:
该类的初始化需要大约 10 秒钟,因为我们需要遍历所有垃圾邮件和业余邮件,并计算所有词类相关概率。
is_spam
函数完成所有的推理工作,并负责根据文本中出现的标记判断电子邮件是否是垃圾邮件。因为这个函数确实做了我们在理论部分讨论过的事情,所以我不打算提供实现的详细解释。
现在,让我们创建主函数,并测试我们的解决方案有多好。
结果我们得到:
**done with initialization! 9.24 s
spam errors: 1
ham errors: 1
correct identified: 0.995**
99.5%的准确率,只有 1 个假阴性和 1 个假阳性,这是一个非常 骄人的成绩!
随意改变一些参数,比如平滑,或者不使用二元模型,而只使用一元模型,看看模型的准确性降低了多少。在这里,我已经提出了达到完美效果的最佳模型。
正如您从本文中看到的,朴素贝叶斯是一个非常简单且易于实现的机器学习模型,它能够在语言分类任务中实现一些令人难以置信的结果。
最不可思议的是,我们只用了 10 秒钟就训练好了模型,并且达到了非常好的精度。将其与深度学习方法进行比较,深度学习方法需要几个小时的训练,并且很可能只能达到与我们的朴素贝叶斯相似的性能。
如果你想让我在下一篇文章中涉及一些 ML 话题,请在评论中告诉我。
敬请关注更多关于人工智能和自然语言处理的文章。
洪水预报的改进
照片由 @chriswebdog
随着气候恶化,我们预测灾难性事件的责任
谷歌人工智能的研究人员写了一篇名为的文章,发表于 2020 年 9 月 3 日星期四,这是我们最近改进洪水预报背后的技术。
关于洪水,你可能不知道的几件事:
- 洪水是地球上最常见的自然灾害。
- 它影响着全球数亿人的生活。
- 每年造成约 100 亿美元的损失。
谷歌在前几年一直致力于洪水预报。
他们最近努力提高印度和孟加拉国的洪水预报。
通过这样做,他们将覆盖范围扩大到 2 . 5 亿多人,并提供了前所未有的前置时间、准确性和清晰度。
谷歌人工智能可视化
他们想出了解决这个问题的新方法。
即 形态学淹没模型 : 将基于物理的建模与机器学习(ML) 相结合,以在现实世界设置中创建更精确和可扩展的淹没模型。
他们将此与 警报定位模型相结合:“…允许使用端到端的机器学习模型和全球公开可用的数据来识别面临前所未有规模洪水风险的地区。”
他们有洪水预报系统,叫做(在今年的 ICLR 人工智能地球科学和 EGU 上展示)。
它们描述了几个重要的方面:
- 预测水位:预计河流是否会发生洪水。
“一旦预测河流将达到洪水位,生成可操作警报的下一步是将河流水位预测转换为洪泛区将如何受到影响的预测。”
- 形态淹没建模:在之前的工作中,我们开发了基于卫星图像的高质量高程图,并运行基于物理的模型来模拟水流穿过这些数字地形。”
根据谷歌人工智能的说法,这样做可以在数据匮乏的地区以前所未有的分辨率和准确性发出警告。
他们提到洪水建模面临三个重大挑战。
- “由于此类模型所涉及的面积和所需的分辨率较大,因此它们必然具有较高的计算复杂性。
- 此外,大多数全球高程图不包括河床水深测量,而河床水深测量对于精确建模至关重要。
- 最后,需要了解并纠正现有数据中的错误,可能包括仪表测量错误、立面图中缺失的特征等。”
他们对高程图的形态进行计算修改,允许人们使用简单的物理原理来模拟洪水,例如那些描述流体静力系统的原理。
他们训练模型使用它正在接收的数据来实时直接推断洪水地图。
谷歌人工智能可视化
“下面的动画展示了 HydroNets 中信息的结构和流动……网络迭代的输出将传递给下游模型,以此类推。”
谷歌人工智能可视化
看到谷歌人工智能取得的进步令人着迷。
这项工作肯定有助于拯救数百万人的生命。
这里是#500daysofAI,您正在阅读的是第 469 条。500 天来,我每天都在写一篇关于或与人工智能相关的新文章。
使用变换改进 CNN 深度学习模型时做什么和不做什么
迪伦·麦克劳德的图片
PyTorch 和 Torchvision 中的一个实验,用于在计算机视觉中诊断您的神经元网络性能
转换技术概述📋
根据维基百科[1]
数据转换是将数据从一种格式或结构转换成另一种格式或结构的过程。
在计算机视觉中,数据增强对于规范你的网络和增加你的训练集的规模非常重要。有许多数据转换技术(旋转、翻转、裁剪等……)可以改变图像的像素值,但仍然保留图像的几乎全部信息,因此人们很难分辨它是否被增强。这迫使模型在图像内的对象变化较大时更加灵活,较少考虑位置、方向、大小、颜色……通过数据扩充训练的模型通常概括得更好,但剩下的问题是数据转换可以在多大程度上提高 CNN 模型在图像数据集上的性能。
让我们来看看 Pytorch 在 Torchvision 中支持的一些数据转换技术,可以帮助你对ơn 图像进行增强。我还提供了一个真实图像数据集的实验,以显示应用不同方法时的性能。
- torch vision . transforms . center crop(size):类似于放大图像中心。它将给定的图像裁剪到所需的输出大小和位置(大小可以是正方形或长方形)。
- torch vision . transforms . pad(padding):相当于缩小图像。它将在给定图像的所有边上用某个值创建一个填充。
- torch vision . transforms . random crop(size,padding):这个函数将在随机位置裁剪给定的图像,以创建一组用于训练的图像。
- torch vision . transforms . randomhorizontal flip§:该函数将按照给定的概率随机水平翻转给定的图像。
- torch vision . transforms . randomverticalflip§:这个函数会以给定的概率随机垂直翻转给定的图像。
- torch vision . transforms . random perspective(distortion _ scale,p) :该函数将对随机给定概率的给定图像进行透视变换。它通过扭曲整个图像来降低模型学习的透视效果。
- torch vision . transforms . gray(num _ output _ channels):将图像转换为灰度。这有时有助于 CNN 模型使用单通道进行更快的训练,并且更容易地学习图像的模式。
- torch vision . transforms . color jitter(亮度、对比度、饱和度、色调):I 可以随机改变图像的亮度、对比度和饱和度
- torch vision . transforms . Normalize(mean,std): 用均值和标准差归一化张量图像。它将有助于 CNN 模型轻松转换到全局最小值或快速减少损失。如果你想知道为什么数据规范化是必不可少的,以及如何进行规范化以提高机器学习模型的性能,你可以参考下面的博客。
[## 用 Pytorch 中的数值变量转换提高机器学习模型的精度
这个故事提供了在 Pytorch 中实现转换技术和提高代码准确性的完整指南
towardsdatascience.com](/push-the-accuracy-of-machine-learning-model-with-numerical-variable-transformation-in-pytorch-9f56c56203fd)
- torch vision . transforms . random erasing(p,scale,ratio,value)在图像中随机选择一个矩形区域并擦除其像素。该方法对 CNN 模型进行惩罚,有助于防止训练时的过拟合现象。
增强也可以应用于 NLP 以提高性能。例如:在句子中随意插入、调换、替换单词;在翻译中使用中间语言(反向翻译),打乱句子。
有几个库提供了优秀的增强模块,如白蛋白,NLPAug…下面是一篇来自 neptune.ai 的很棒的进一步探索的文章。
[## NLP 中的数据扩充:来自 Kaggle Master 的最佳实践
在自然语言处理中有许多任务,从文本分类到问题回答,但是无论你做什么,数据的数量…
海王星. ai](https://neptune.ai/blog/data-augmentation-nlp)
在 CNN 模型上评估转换技术📄
让我们讨论变换技术对 CNN 模型的影响。本实验中使用的数据集是来自 Kaggle 竞赛的犬种识别,该竞赛提供了 ImageNet 的严格犬子集,以便练习细粒度的图像分类。该数据集包括 120 个品种的狗,总共有 10,222 张图像。我们以 0.8/0.2 的比率将数据分割为训练/有效。
在本文中,预训练的 ResNet 2模型将被用作分类 CNN 模型的主干。ResNet 是 SOTA 预训练模型之一,在广泛的计算机视觉任务中显示出良好的结果。ResNet 的主要思想不是用函数 H(x)(多层感知器网络的堆叠)直接学习图像的输入特征,而是提供一个残差函数,它可以重构 H(x) = F(x)+x,其中 F(x)和 x 分别表示堆叠的非线性层和恒等函数(输入=输出)。这种思想可以解决深层神经元网络(消失梯度)的退化问题,因为它比原始映射函数更容易优化残差映射函数 F(x)。
首先,我们将加载原始数据。
# Create a custom Dataset class to read in data from dataframe
class BreedDogDataset(Dataset):
def __init__(self, dataframe, root_dir, transform=None):
self.dataframe = dataframe
self.root_dir = root_dir
self.transform = transform
def __len__(self):
return (len(self.dataframe))
def __getitem__(self, idx):
img_name = os.path.join(self.root_dir,
self.dataframe.iloc[idx, 0])
image = Image.open(img_name)
target = self.dataframe.iloc[idx, 1]
target_processed = breeds_processed_dict[target]
if self.transform:
image = self.transform(image)
sample = (image, target_processed)
return sampletransform = transforms.Compose([transforms.Resize((255, 255)),
transforms.ToTensor()])train_ds = BreedDogDataset(df_train,TRAIN_DIR, transform=transform)
val_ds = BreedDogDataset(df_val,TRAIN_DIR, transform=transform)
然后我们创建一个这样的结构模型:
# load pre-trained model
conv_base = models.resnet50(pretrained=True)# create a new model class
class Model(nn.Module):
def __init__(self, base_model, output):
super(Model, self).__init__()
self.base_model = base_model
self.output = output
self.fc1 = nn.Linear(n_outs, 512)
self.fc2 = nn.Linear(512, output)
self.dropout = nn.Dropout(0.5)
def forward(self, image):
x = self.base_model(image)
x = self.dropout(x)
x = F.relu(self.fc1(x))
x = self.dropout(x)
outs = self.fc2(x)
return outs
在深度学习模型中要调整的最重要的超参数之一是学习速率。选择正确的学习率非常重要,如果学习率太高,模型将面临发散问题,但如果学习率太低,模型将需要很长时间才能收敛。这个想法是,我们可以通过数百次迭代训练模型来找到一个好的学习率,然后基于学习曲线,我们获得一个好的学习率。学习率调度技术有很多,例如,功率调度、指数调度、1 周期调度、常数调度……在这个实验中,我们将应用1 周期调度。在 Leslie Smith 的论文中,这项技术被证明可以显著提高训练时间。你可以看看这个博客了解更多的解释。
介绍
towardsdatascience.com](/finding-good-learning-rate-and-the-one-cycle-policy-7159fe1db5d6)
模型的学习曲线
根据上面的学习曲线结果,它显示下限是 0.9E-04,I 取整值为 1.0E-03。现在,我可以通过使用经验法则定义学习速率的范围来微调新图像数据集上的预训练 ResNet50:从比损失最小的学习速率低一个数量级的速率开始,到“安全”速率 1E-03 结束。
经过 5 个历元的训练,有效集的正确率为 83.284 %
列车损失和有效损失如下所示。可以看到两个值都降低了。从第 4 代到第 5 代,损失和准确性几乎是恒定的。这意味着模型已经从数据中学习了相对的一切,所有帮助模型区分品种狗的好信息都被捕捉到了。
然后,我们检查前 6 个损失最高的图像。我们可以看到,模型对一些图像进行错误分类的原因之一是,狗的品种预测与其他不同品种的样本相似,这甚至是人类无法区分它们。为了解决这个问题,转换技术非常有希望帮助模型深入学习每个品种的模式。另一个问题是,一些图片有 2 只不同的狗,但标签上只显示一只。错误的标签也是另一个问题。这些问题无法解决,这似乎是一个异常情况。
让我们在这个数据集中应用一些转换技术。
mean = [0.4766, 0.4527, 0.3926]
std = [0.2275, 0.2224, 0.2210]# define transform function
transforms_train = transforms.Compose([
transforms.Pad(25, padding_mode='symmetric'),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(10),
transforms.ToTensor(),
transforms.Normalize(mean, std),
transforms.RandomErasing(p=0.75,scale=(0.02, 0.1),value=1.0, inplace=False)
])transforms_val = transforms.Compose([
transforms.Resize((255, 255)),
transforms.ToTensor(),
transforms.Normalize(mean, std)
])
# load in the temporary dataset from the original dataframe with transform
train_ds = BreedDogDataset(df_train,TRAIN_DIR, transform=transforms_train)val_ds = BreedDogDataset(df_val,TRAIN_DIR, transform=transforms_val)
转换后的训练数据集
转换后的有效数据集
在变换后的数据集上用 5 个历元进行训练后,我们得到有效集的准确率达到 0.817,与原始图像相比略有下降。
新的列车损失和有效损失如下所示。与上述模型相比,列车损失和有效损失之间的距离增加,而第 4 和第 5 个时期的精度略有下降。这表明新模型面临着不匹配的问题。通常,转换确实有助于模型防止过度拟合问题。然而,在这种情况下,因为先前的模型没有过拟合问题,并且训练损失甚至高于有效损失,所以当应用变换时,没有改善。
让我们看看损失最大的前 6 张图片。我们可以看到一些混乱的图像现在可以被分类,但有些还没有。然而,这个问题无法解决,一些异常值仍然会影响模型的性能。
最后的想法📕
这个博客的主要目的是介绍一些转换图像的技术。从根本上说,这将有助于避免过拟合问题,以及增加 CNN 模型的训练数据的数量。但这并不意味着变换总是会提高模型的精度,它必须依赖于输入图像(卫星、细菌、动物、物体、…)和模型结构。许多 Deep-ML 实践者遇到了这个问题,他们通常在检查他们的模型可以从他们的原始图像学习到什么程度之前进行转换。如果模型不符合原始图像,转换数据无助于解决问题,换句话说,它会降低模型的准确性。因此,我们必须一步一步地诊断我们的模型,并通过最坏的预测来分析可视化的结果。
如果你想进一步讨论,可以联系我。这是我的 LinkedIn
尽情享受吧!!!👦🏻
参考
[1]https://en.wikipedia.org/wiki/Data_transformation
[2] 何、、、【邵青任、、,深度残差学习用于图像识别,2015。
改进 538 著名的 NFL 预测模型
深入研究 NFL 的统计数据,以改进一个有潜力与拉斯维加斯竞争的著名模型
照片由于是吊杆从的 Unsplash
NFL 赛季即将来临。球员们正在接近训练营的尾声,球迷们都渴望在周日再次观看他们最喜爱的球队比赛。退一步说,我们是如何走上这条不归路的。休赛期是我们从未见过的,今年也是如此。鉴于今年的表现,我们都应该感谢甚至会有 2020 赛季。但是足球似乎又回来了,每个人都在做准备。
有钱可赚
当我说每个人都准备好了,我指的是:教练,球员,球迷,体育出路,体育作家,体育更好,和拉斯维加斯。拉斯维加斯可能看起来很奇怪,但我向你保证,他们比任何人都更为 2020 赛季做好了准备。你可能会问为什么?因为他们从事信息产业。他们掌握的信息越多,你损失的钱就越多。
随着 2020 赛季在拉斯维加斯周围滚动,将在当前赛季发生的数百个事件中设定赔率,每年都有数百万投注者在这些赔率上下注,希望它们发生。剧透警报!许多更优秀的人将会失败。为什么?因为这些几率不会凭空出现。不,它们是由精心设计的模型生成的,当输入信息时,会输出一个事件的概率,而大多数优秀者通常会高估或低估这个概率。这就是为什么大多数优秀者会失败。他们只是没有维加斯有的信息。
有些人会战胜维加斯,并不断战胜他们。这些更好的人创造了他们自己的模型,与维加斯使用的模型竞争,这就是我们著名的模型出现的地方。
著名的模特
你可能想知道“著名模特”上的这句话是什么,它是由 Nate Silver 创作的,他是 FiveThirtyEight 的创始人兼主编和他的团队。
FiveThirtyEight 的标志
FiveThirtyEight 是一个专注于政治、经济和体育博客的网站。他们以使用统计分析和硬数字来讲述令人信服的故事和创建显示或预测结果的模型而闻名。毫无疑问,他们最著名的是他们的 NFL 预测模型。这种模式被认为可以与拉斯维加斯的模式相提并论,这也是许多职业运动员使用这种模式的原因。他们依赖预测,并根据模型的预测下注。
该模型创建于 2014 年,多年来一直在不断更新,以进一步提高预测的准确性。但在 2020 年,我找到了改进模型的方法,进一步提高了预测的准确性。稍后我会更详细地介绍,但我认为是时候让你最终了解这个著名的模型是什么以及它是如何工作的了。
电子学
如果你熟悉国际象棋中玩家的排名,那么你就熟悉 Elo 评级。Elo rating 是一个简单的系统,根据头对头的结果来判断球队或球员,是 FiveThirtyEight 创建的模型的核心。这些 Elo 评级能够预测每场比赛的结果,并为每支球队生成获胜概率。该系统的螺母和螺栓如下所述。
游戏预测
首先,Elo 模型给每个团队分配一个功率等级。这些评级用于根据两个团队的 Elo 评级之间的差异来生成游戏的获胜概率。球队通过赢得比赛来增加他们的 Elo 等级,通过输掉比赛来减少,但是增加多少取决于比赛的预期结果。
例如,让我们取两个团队,给他们每个人一个 Elo 评级。让我们说达拉斯牛仔队对纽约巨人队。达拉斯牛仔队的 Elo 评分为 1650,纽约巨人队的评分为 1400。由于达拉斯的收视率更高,我们知道他们比纽约赢得了更多的比赛,应该在这场比赛中受到青睐。事实上,我可以告诉你,Elo 模型给了他们 80%的胜算。
达拉斯牛仔队主场迎战纽约巨人队,2019 赛季第一周。照片由 Depositphotos
Elo 评分被称为一对一系统的原因是,如果达拉斯赢了这场比赛,他们将从纽约拿分,这意味着他们的 Elo 评分将增加纽约的减少量。这个数字取决于预期的结果。由于达拉斯有望以较大优势获胜,如果他们真的获胜,他们的 Elo 评分只会增加约 5 分。然而,如果纽约赢了,他们的 Elo 评分将增加约 17 个点。Elo 考虑到了对手的水平,对失败者奖励更多,对受欢迎者奖励更少。这是为了确保 Elo 评级高的团队能够战胜实力相当的团队,而不仅仅是低于平均水平的团队。
调整
从表面上看,Elo 系统非常简单。如果一个球队赢了一场比赛,他们的 Elo 评分会根据预期结果增加,反之亦然,如果他们输了这场比赛。但是足球比这更复杂。Elo 评级显示了足球队作为一个整体的整体实力,但它没有考虑影响足球比赛的外部环境。环境包括:球队是在主场还是客场比赛,他们是否会在一周内结束比赛,或者首发四分卫是否有变化。
必须针对这些情况进行调整,以确保模型尽可能准确。因此,FiveThirtyEight 的 Elo 模型还包括在每场比赛开始前进行的几项调整:
- 主场适应——NFL 的球队主场比客场打得更好。事实上,在过去的 10 年里,主场作战的球队比客场作战的球队平均多赢 2.2 分。因此,该模型通过在比赛前将主队的 Elo 评级增加 55 分来调整主场优势。
- 休息调整—NFL 中的每支球队都有 1 个轮空周(除了在季后赛开幕周不比赛的头号种子球队),研究表明,他们在那个轮空周的表现比预期的要好。为了适应这种更好的打法,一个结束轮空周的队伍会在那场比赛中获得 25 点 Elo 积分。
- 季后赛调整 —季后赛和常规赛很不一样。在季后赛中,人们发现,热门球队往往会以更大的优势击败弱队。为了调整这一点,该模型在季后赛中略微增加了热门球队的获胜概率。
- 旅行调整— 旅行会对玩家产生巨大的影响,从时差到身处不同的时区。它也显示出会影响团队作为一个整体的表现。那些不得不在全国各地旅行的球队比那些只坐一辆公交车的球队打得差一点。为了调整这一点,对手每行进 1000 英里,主队获得 4 个 Elo 点的增加。这意味着巨人队在“主办”喷气机队时获得 55 分的 Elo 奖金(没有旅行调整,因为他们在同一场地比赛,55 Elo 奖金来自主场调整),而如果西雅图来比赛,巨人队将因西雅图必须旅行的长距离获得 62 分的 Elo 奖金。
- 胜率乘数— 如果我们只根据输赢来评判球队,以上所有的调整就足够了。然而,我们希望该模型考虑到一支球队是如何获胜的——他们是统治了对手还是仅仅险胜对手。因此,有一个乘数,增加了一个团队获得的 Elo 点的数量基于他们如何获胜。这个乘数的公式如下。
如果你想知道像主场优势增加 55 个 Elo 点这样的具体数字是如何产生的,那么这是因为 NFL 比赛中的每一点都等于模型中的 25 个 Elo 点。我之前说过,主场作战的球队平均多赢 2.2 分。2.2 x 25 =55,这就是模型找出每次调整应该增加多少的方法。
季前赛收视率
关于 Elo 模型应该知道的一点是,它总是连续的。每支球队的 Elo 不会在每个赛季后重置,相反,Elo 评级会延续到下一个赛季。考虑到休赛期的变化,可能包括选秀权,自由球员,交易和教练的变化,有一个对 Elo 评级的季前赛调整。
这种调整使用两个因素。第一个是将一个球队的赛季末 Elo 恢复到平均值 1505 的三分之一。第二个是使用下个赛季拉斯维加斯的总胜率。
下赛季开始时,一个球队的 Elo 评级是三分之一的权重给回归的 Elo,三分之二的权重给拉斯维加斯的胜利。
准确(性)
回头看看达拉斯对纽约的例子,达拉斯的 Elo 评分是 1650,纽约是 1400。两者相差 250。如果我们除以 250/25,我们得到 10。这在模型中的含义是,它预测达拉斯将赢纽约 10 分。这就是如何预测每场比赛的点差。该模型将 Elo 差值除以 25,因为像我之前提到的那样,NFL 比赛中的每一分都等于模型中的 25 个 Elo 分。
通过比较每场比赛的实际得分差异,预测的点差是评估预测准确性的一个很好的方法。这正是 FiveThirtyEight 判断其模型准确性的方式。使用均方误差,该模型找出预测的点差和实际点差之间的差异,并对误差进行平方,以对不良失误给予更大的惩罚。误差越小越好。
均方差公式:
(Predicted Point Spread - Actual Score Diff)² = MSEExample: (10 - 7)² = 9
FiveThirtyEight 还使用 Brier 分数评估他们的模型。Brier 分数使用获胜概率,而不是点差。在我们达拉斯的例子中,模型给了他们 80%的获胜概率。在 80%的情况下,模型非常有信心达拉斯会赢,如果他们赢了,Brier 分数会很低(Brier 分数越低越好,因为它们意味着预测更接近正确)。然而,如果纽约仅以 20%的获胜概率获胜,这意味着我们的模型在预测这场比赛的结果方面是非常错误的,并且 Brier 得分将会相当高。
Brier 评分公式:
(Win Probability — Outcome of Game)² = Brier ScoreExample:
(0.80 — 1)² = 0.04 #If Dallas wins the game
(0.80 — 0)² = 0.64 #If Dallas loses the game
最新版本
多年来,FiveThirtyEight 的 Elo 预测模型不断更新,进行了新的调整,以进一步提高这两个指标的预测准确性。2019 年,他们的最新版本问世,表现比以前的版本好得多。下图显示了最新版本和旧版本在准确性指标上的差异。精确度的提高可以归功于他们最新模型的又一次调整,这是他们最大的调整,也可能是他们最重要的调整,我们还没有讨论过。
四分卫调整
2019 年 FiveThirtyEight 增加了游戏最重要位置的调整。它是这样工作的:
- NFL 中的每个球队和四分卫都会根据他们最近的表现获得一个价值评级。该价值等级根据四分卫在比赛中的表现而增加或减少。
- 模型如何通过这个公式评估四分卫在一场比赛中的表现: *-2.2 *传球次数+ 3.7 *完成次数+(传球码数/ 5) + 11.3 *传球次数-14.1 *拦截次数-8 次抢断次数-1.1 *抢断次数+ 0.6 *抢断码数+ 15.9 抢断次数
- 上面的公式是一个四分卫的 Game_VALUE。每场比赛后,四分卫的价值根据他们的 Game_VALUE 增加或减少。这是通过将更新后的四分卫值贡献为其旧值的 90%和其游戏值的 10%来完成的(即,值评级新值= 0.9 值评级旧值+ 0.1游戏值)
- 这个值也通过计算球队允许的 QB 值来调整对手的防守质量。这是通过从对手通常每场比赛放弃的值中减去联盟平均值来完成的,并用它来调整 QB 在所讨论的比赛中的表现。例如,如果对方球队通常放弃比平均水平高 5 分的 QB 值,我们会将其他球队个人 QB 的表现向上调整 5 分,以说明对方防守更容易。
- QB 值代表了我们期望四分卫在下一场比赛中面对一般质量的传球防守时的表现。要将这一价值评级转换为 Elo,该值可以乘以 3.3,以获得 QB 与未选秀新秀替代者相比的预期 Elo 点数。
- 为了了解四分卫可能拥有的不同价值评级,下面是 2019 赛季前 10 名四分卫的表格。
四分卫 Elo 调整在每场比赛前应用,方法是将起始 QB 的价值评级与球队的价值评级进行比较,并乘以 3.3。
德鲁·布里斯,新奥尔良圣徒队的四分卫。照片由 Depositphotos
例子:当德鲁·布里斯在 2019 赛季中途受伤时,他的价值评级为 75。新奥尔良圣徒队的球队价值得分是 78,替补球员泰迪·布里奇沃特的个人得分是 45。因此,当调整圣徒队的下一场比赛的 Elo,让布里奇沃特而不是布雷斯首发时,我们会将*3.3 (45–78)=-109的调整应用到新奥尔良在第四周对阵牛仔队的比赛中 1639 的基本 Elo 评分。这实际上会使圣徒队成为一支 1530 年的 Elo 球队,布里奇沃特在中心位置(在对主场,旅行和休息进行调整之前),尽管在主场比赛,新奥尔良队的获胜概率从 60%下降到 44%。在这种情况下,QB 调整会产生巨大的影响!
就像球队的季前赛评分是如何调整的一样,个人四分卫也是如此。当休赛期到来时,四分卫在赛季结束时的最终价值评级会在下一个赛季开始前恢复到平均 QB 值的四分之一。
对于初次登场的新秀,我们根据选秀位置给他们分配初始评分。一个未选秀的新秀在他的第一次首发总是被评为零分。相比之下,第一个整体选秀权在他第一次开始之前得到 35 的价值评级。
现在,您应该对 FiveThirtyEight 模型的工作原理有了更高质量的理解。这是一个简单而有效的模型,可以预测游戏的结果、赢的概率和他们的点数差距。
然而,这个模型还可以进一步改进,我已经改进了!我发现了 FiveThirtyEight 的人没有做的其他调整,并最终极大地改进了模型。
改进
当试图找出如何改进 FiveThirtyEight 的模型时,我不得不考虑他们没有针对游戏的哪些方面进行调整。正如我们之前讨论的,他们已经调整了:主场,再见周,季后赛的最爱,旅行距离,和首发四分卫的变化。但是深入到足球比赛中,我发现了 3 个新的调整,极大地改进了模型。
1.一个队在哪一天比赛
明尼苏达维京人队的四分卫柯克·考辛斯是这次调整的灵感来源。考辛斯因在黄金时段游戏(周一晚上)玩得很烂而出名。周四晚上和周日晚上的比赛)。事实上,他在这些比赛中的战绩是 6 胜 13 负,尽管他在一个高于平均水平的球队打球。
这让我想到,在全世界都在看的国家电视台黄金时段的比赛中,球队比在正常的周日下午打得更好还是更差。当深入研究过去 10 年的统计数据时,我发现…
从上表可以看出,在周四和周日晚上的比赛中,主队获胜的几率要高出近 5%。这些球队还在周四晚上将他们在这些比赛中的平均胜率提高了约 1 分,在周日晚上提高了约 2.5 分,从而证明了在主场,球队通常在黄金时间的比赛中打得更好。所以我们改进模型中加入的调整是周四晚比赛主队加 25 Elo 分,周日晚比赛主队加 50 Elo 分。
2.部门游戏
在 NFL 的每个部门有 4 个队。这些队在常规赛中互相打了两次。由于同一个赛区的球队比其他球队更频繁地碰面,所以他们的优势是打一次,适应一下,然后再打一次。这将导致分区赛比非分区赛更接近,因为这些球队已经看到了其他球队如何比赛,他们可以为此做准备。
下表显示了过去 10 年间分区赛和非分区赛的区别。
从上表可以看出,在分区赛中,主队的胜算比非分区赛低近 3%。他们的平均胜率也减少了一分,证明了分区比赛比非分区比赛更接近和更难预测。为了适应更接近的赛区比赛,我们的改进模型将 Elo 分数较高的球队减少 25 分,以最小化两个赛区球队之间 Elo 的总体差异。
3.维加斯点差
正如我之前所讨论的,FiveThirtyEight 的模式很有名,因为它与拉斯维加斯的模式竞争。事实上,当比较他们的模型的预测点差和拉斯维加斯的预测点差时,在大约 80%的游戏中,两者之间的差异仅仅是一个投篮得分(3 分或更少)。但是 Elo 模型的缺点是它不像 Vegas 模型那样是最新的。
当维加斯推出他们的点差时,他们在计算他们能找到的每一个因素;伤病,天气,最后一秒的首发变化等等。Elo 模型不会这样做。它的大部分调整你可以在一周前找到信息。唯一最新的调整是四分卫调整,当四分卫发生变化时应用,但对于其他每个位置,不存在调整。
如果在大约 80%的游戏中,两个点差之差大约是 3 分,那么剩下的 20%会发生什么呢?在我的结论中,我认为除了四分卫位置之外,可能还有一个伤害,或者一个 Elo 模型无法调整的未知情况,这可能会使两个点之间的差异大于 3。这是我们进行第三次调整的地方。
由于我们的模型不能适应这些情况,我决定从维加斯模型中得到一点帮助。因此,如果我们的模型的预测点差和 Vegas 的预测点差之间的差异大于 3,那么我们调整我们的预测点差以匹配 Vegas 的点差。例如,如果我们的模型预测牛仔队赢 10 分,但维加斯预测他们赢 6 分,那么我们调整两个队的 Elo,这样我们预测的点差就是 6 分。
就像上面的例子,达拉斯的 Elo 是 1650,纽约是 1400。Elo 的差别是 250 和 250/25 =10。我们的模型预测点差为 10,但我们希望它与拉斯维加斯的 6 相匹配。因此,我们将达拉斯的 Elo 从 1650 降低到 1550。
1550–1400 = 150,150/25 = 6,我们现在当前的预测点差(这也将达拉斯的预测获胜概率从 80%降至 70%)。
我们改进后的车型在这一季表现如何
2019 年是一个有点不可预测的 NFL 赛季。这是因为在 FiveThirtyEight 的最新和旧的 Elo 模型版本中,两个准确性指标(Brier 分数、均方误差)都比前几年有所下降。但是,在这个不可预测的 NFL 赛季中,我们改进的模型表现如何呢?通过在两个准确性指标上比较所有 3 个模型,我们可以确定哪个模型在 2019 年赛季中表现最好。
欧石南得分:
如果你记得 Brier 分数越低越好,我们改进的模型能够显著降低 Brier 分数。2019 年,新 Elo 的平均 Brier 分数为 0.219 ,我们改进的模型的平均分数为 0.196 ,这表明在 2019 年,我们的模型能够比两个 Elo 版本更好地预测团队的获胜概率。但不仅仅是 2019 年。如果你看上面的图表,在过去的 5 年中,我们的改进模型每年都比两个 Elo 版本表现得更好。
使用 Brier 得分,让我们看看每个模型的准确性如何在常规赛中随着时间的推移而演变。从下面的图表中可以看出,在第 3、4 和 9 周,我们的模型比 Elo 版本表现更差。但在其他方面,我们改进的模型表现更好。这意味着在 14/17 周,我们改进的模型表现最好。这导致了 2019 赛季 Brier 的整体平均得分下降。
均方差:
类似于 Brier 分数,我们改进的模型在均方误差方面每年都优于两个 Elo 版本。2019 年,新 Elo 的平均 MSE 为 183 ,而我们改进的模型的平均 MSE 为 174 ,表明 2019 年我们的模型能够比两个 Elo 版本更好地预测游戏的点差。
最大命中和未命中
你已经看到了我们的改进型号在季度和周周基础上比 Elo 版本做得更好。理论上,接下来的部分是展示我们改进的模型在哪些特定的游戏中提高了最多的准确性。
下面是两张图表。第一个显示我们的改进型号相对于最新的 Elo 版本的最佳选择,第二个显示最差的。
(老栏目指的是 FiveThirtyEight 最新的 Elo 模型预测获胜队伍的胜率。新列是我们的改进模型预测同一团队的胜率)。
2019 赛季,我们的改进模型和 FiveThirtyEight 的最新 Elo 模型之间的最高 Brier 分数差异。
2019 赛季,我们的改进模型和 FiveThirtyEight 的最新 Elo 模型之间的最低 Brier 得分差异。
FiveThirtyEight 的 NFL 预测游戏
每年 FiveThirtyEight 都会举办一场 NFL 预测比赛,看看是否有人能在预测 NFL 比赛中击败他们的 Elo 模型。每年,成千上万的人报名证明他们比 FiveThirtyEight 和其他任何人都更了解足球。
游戏通过使用 Brier 分数来工作。NFL 赛季(包括季后赛)的每一周,球员通过给球队一个胜率来决定哪支球队会赢。在每场 NFL 比赛结束后,玩家将根据他们是否选择了获胜的球队以及他们对获胜的信心程度来得分或失分。你分配给一个团队的获胜概率越高,你能赢得的分数就越多——但如果你过于自信,你失去的也就越多。
通过给匹兹堡 91%的获胜概率,如果他们赢了,这个玩家将获得 24.2 分。然而,过度自信可能是灾难性的,因为如果他们输了,这个球员将失去 57.8 分。
为了进一步证明我们改进的模型可以比 FiveThirtyEight 更好地预测 NFL 比赛,我决定参加他们的比赛,看看如果我们确实在 2019 赛季比赛,我的改进模型会积累多少分。
2019 年,超过 15,000 名球员报名参加了 FiveThirtyEight 的 NFL 预测游戏。在赛季结束时,FiveThirtyEight 的模型已经积累了 773.7 分,足以进入第 97 百分位和第 514 位。这意味着他们的模型能够比 97%的比赛更好地预测 NFL 比赛。相比之下,排名第一的格里芬·科莱齐以 1,126.2 分的成绩排名第 99 位。
问题是,我们的改进模型能够击败 FiveThirtyEight 的分数吗,它甚至接近格里芬的分数吗?
下面是我们的改进模型在 NFL 预测游戏中每周获得的净点数的每周图表。
除了 2 周之外,我们改进后的车型在净积分方面一直保持正值,并以 1,414.8 分的总成绩结束了本赛季。我们不仅将 538 分提高了近一倍,还比格里芬多得了近 300 分,彻底击败了他。这意味着,如果我们的改进模型在赛季中参加预测比赛,我们不仅会在近 15,000 名竞争对手中获得第一名,而且甚至不会接近。
季节模拟
Elo 模型如此伟大的原因是因为预测比赛所需的大部分信息可以在下赛季赛程公布前几个月找到。由于我们距离赛季开始还有几天,我们现在了解了 Elo 评级如何随着赛程的进行而更新,我们拥有了所需的信息,并可以预测整个 2020 赛季将会发生什么。
NFL 赛季并不总是确定的,通常应该击败其他球队的球队有时会输。这意味着 NFL 赛季存在随机性。为了将这种随机性整合到我们的模型中,我们使用蒙特卡罗方法模拟 2020 年 NFL 赛季 10 万次,跟踪每个模拟宇宙为每个球队产生给定结果的频率。
最后,模拟将显示一支球队的预期 Elo 积分、积分差异、整个赛季的记录以及赢得分区冠军、进入季后赛、获得第一轮比赛甚至赢得超级碗的几率。
在下面找到你最喜欢的球队,看看我们的模型预测你的球队下赛季会发生什么。
结论
FiveThirtyEight 的 Elo 模型简单但有效。通过 Elo 评级,很容易判断任何给定团队的实力,并预测赢家。随着模型而来的调整,通过处理直接游戏内外的情况,帮助这些预测变得更加准确。
我们能够通过重新创建并添加一些我们自己的调整来改进他们的 Elo 模型。但是我们能进一步改进它吗?绝对的!足球不是停滞不前的,它是不断变化的,新的模式和比赛风格将会出现,这将改变比赛的方式。这就是为什么拉斯维加斯的模式仍然是最好的,他们总是根据游戏的变化而变化。通过做同样的事情,Elo 模型可以与拉斯维加斯的一些最好的竞争,甚至有能力超越。
谢谢你
我想写一小段话来感谢五点三十八分的朋友们。他们在提供他们模型的细节方面做得很好,这让我可以重现他们。我在本文中讨论了该模型的大部分内容,但如果你想自己阅读细节,这里有一个他们网页的链接:我们的 NFL 预测如何工作。如果你也想参加他们的 NFL 预测游戏,我也会留下一个链接:如何参加我们的 NFL 预测游戏。
参考
[1] FiveThirtyEight,我们的 NFL 预测如何工作 (2019),https://FiveThirtyEight . com/methodology/How-Our-NFL-Predictions-Work/
[2] N. Paine,我们的新 NFL 模特这一季表现如何 (2020),https://fivethirtyeight . com/features/How-Our-New-NFL-Model-DOD-This-Season/
[3] J. Boice,如何玩我们的 NFL 预测游戏 (2018),https://fivethirtyeight . com/features/How-To-Play-Our-NFL-Predictions-Game/
[4]职业足球参考,https://www.pro-football-reference.com/(2020)