TowardsDataScience-博客中文翻译-2021-九十九-
TowardsDataScience 博客中文翻译 2021(九十九)
人工智能会很快取代我们的艺术家吗?
超越创造力前沿的人工智能之旅

尼古拉·约翰尼·米尔科维奇在 Unsplash 上的照片
人类的创造力似乎是人工智能(AI)的最后一个前沿。许多人声称人工智能擅长各种事情,它的表现超出了人类的预期,但展示创造力将是一个挑战。创作过程仍然神秘莫测。我们可以使用技术来培养创新思维,但最终,我们并不完全确定它是如何发生的。由于缺乏对我们自身生物过程的了解,科学家很难在机器上复制它们。尽管有这些限制,我们在使用人工智能创造创新杰作方面还没走多远。

当涉及到文本内容时,AI 非常擅长处理文字。我们可以列出过去的各种例子,人工智能诗歌与人类创作的诗歌难以区分。很好奇的是,2016 年,一篇名为《电脑写小说的那一天》的文字曾被提交给日本的一个短篇小说比赛。这部小说遵循一个计算机程序,因为它实现了作为作家的能力,同时放弃了预编程的职责。该文本完全是使用人工智能编写的,并成功通过了第一轮比赛。

由瓦茨拉夫·pluhař在 Unsplash 上拍摄的照片
绘画也是由人工智能产生的。2018 年,佳士得拍卖行收到了一幅名为《爱德蒙·德·贝拉米的肖像》的画作。令人难以置信的是,它是由一个人工智能系统使用模仿人类认知的过程创建的。人工智能最初接触了数百幅风格相似的绘画,然后被要求生成数百幅潜在的图片。最终选择了符合这种风格但又与其他作品有很大不同的图片。尽管它的创作者最初认为他们可以从中赚几千美元,但实际上,这幅画以近 50 万美元的价格售出,今天被认为是第一件由机器创作的艺术品。

Aditya Chinchure 在 Unsplash 上拍摄的照片
使用人工智能创作数字音乐甚至歌曲已经被研究了很长时间。音乐相对简单,因为系统可以从各种可用的曲调中获得灵感。事实上,近年来已经有很多创造 AI 曲调的尝试。英国国王学院(King's College)一个名为“反馈循环”(Feedback Loops)的令人兴奋的项目使用人工智能来看一名舞蹈演员,并以旋律的形式解释她的动作。但是用歌词创作一首真正的歌完全是另一回事。几个月前,多伦多大学创建了一个人工智能系统,其任务是听 100 个小时的在线圣诞音乐,并创作第一首人工智能圣诞颂歌。结果被描述为有点乏味,不合节日气氛,令人不安。因此,人工智能凭借其首支圣诞单曲将玛丽亚·凯瑞挤出前 10 名还需要一段时间。

照片由 Ahmad Odeh 在 Unsplash 上拍摄
舞蹈和戏剧也是艾的拿手好戏。我们看过各种艾积极参与表演的节目。马耳他大学创造了虚拟化身与人类舞者共舞。在 2020 年的新年前夕,波士顿动力公司——生产世界上最先进的机器人的公司——发布了一套独特的舞蹈设计。它的四个机器人正在表演 Contours 的热门歌曲“你爱我吗?”结果是相当了不起的,超出了大多数人的能力。更令人印象深刻的可能是无人机展示,它使用大约 500 架无人机在空中创建移动的 3D 形状。它们被用来庆祝 2020 年的苏格兰新年前夜和其他重大的世界性事件。
如今,剧院背景也是由人工智能生成的。被操纵的图像已经存在很多年了。Photoshop(用来编辑图片的最有效的程序之一)可以修改现有的场景,甚至通过一些努力创建假的场景。然而,就在去年,Nividia 宣布了 GauGAN,这是一个令人印象深刻的人工智能系统,能够将孩子们喜欢的涂鸦转换成高分辨率的照片。一条简单的带圆圈的线变成了湖边瀑布的逼真图片。从而创建高分辨率,逼真的动态背景。

照片由 Marija Zaric 在 Unsplash 上拍摄
计算机生成图像(或 CGI)已被广泛用于电影中,以创建从灾难性灾难到动画角色的各种重建。最近的时尚似乎是创造虚拟演员。这些演员是为了取代真正的演员,他们可能因为太贵而无法合作,并且不严格按照导演的指示行事。在某些情况下,他们被用来代替死去的或年纪太大的演员。据说,60 多年前去世的神话演员詹姆斯·迪恩将出演一部名为《寻找杰克》的电影。在《星球大战》电影《星球大战外传:侠盗一号》中,由于凯丽·费雪(莱娅公主的原始女演员)已经衰老,年轻公主的数字身体被创造出来,她的面部表情保持不变。然后人工智能被用来简单地填充这些点。

所以在合成中,人工智能既可以是艺术的天才创造者,也可以仅仅是艺术家使用的工具。当然,这引发了很多问题,比如人工智能艺术的功劳归谁?是程序的问题吗?但是它没有法人实体!程序员呢?程序员创造了算法,它可以指导人工智能获取新知识。但是就像师傅带徒弟一样,它并不能决定学习什么!当然,这场辩论没有结论,并将在未来几年继续激烈进行。但我们应该以开放的观点来看待它,拥抱技术,并接受我们可以通过将人工智能与艺术相结合来达到新的不可想象的高度。
如果您喜欢这篇文章,并想联系我,请联系我🐦https://twitter.com/alexieidingli*,🔗LinkedIn,📷insta gram或者😊* 脸书 。
*
阿列克谢·丁力教授 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他已经出版了几本同行评审的出版物,并成为马耳他的一员。由马耳他政府成立的人工智能特别工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。*
数据会成为云原生的吗?
数据专业人员使用的工具和平台越来越多地运行在云原生技术上。

照片由Jonny Gios在 Unsplash 上
数据工具仍然是一个极其活跃的空间,这对于作为一个懒惰用户的我来说是非常令人兴奋的。别信我的话,你可以看看马特图尔克的精彩帖子: 韧性与活力:2020 年数据& AI 景观 。
如果你深入挖掘 Matt 文章中的大量信息图表,你会发现许多列出的技术、工具和公司都是云原生的。这一趋势的惊人之处在于,它发生在现代数据基础架构堆栈的上上下下 —从接收到存储,再到处理和预测。
不过,我想知道的一件事是,云原生数据工具是否会成为主导。
什么是云原生?
出于各种各样的原因,人们开始使用术语“原生云”来表示两种完全不同的东西。
- “原生云”意味着使用“容器、服务网格、微服务、不可变基础设施和声明性 API”,正如 CNCF 所描述的那样。
- “云原生”意味着使用云服务提供商的“原生”工具。在这种用法中,我们谈论的是紧密集成的特定于提供商的工具,例如与 AWS 控制塔、配置或并行集群。
不幸的是,这个词有两种几乎相反的用法,但我对此无能为力。(实际上,我认为在某些情况下,可能在无服务器领域的某个地方,您最终会实现这个术语的两种含义,所以它们并不完全相反。)无论如何,对于本文,我将使用第一种用法。
云原生数据工具
让我们面对现实:数据工具运行在原生云上。
Spark 现在的目标是在 Kubernetes 上部署。这本身就是巨大的。此外,AWS 最近为他们的托管 Spark 集群推出了功能,让你可以在他们的托管 Kubernetes 集群上运行它。(我会注册在 Kubernetes 上运行 production Spark 工作负载吗?我不确定。)
Jupyterhub 、 RStudio 和 Kubeflow 都是使用 Kubernetes 的数据基础设施即软件的例子。这些是值得尊敬的增值工具,使用他们已经熟悉的工具为数据科学家提供一致的用户体验(Kubeflow 添加了一些新东西,但也嵌入了 Jupyter)。这个空间还远未尘埃落定: AWS Sagemaker 、 Azure ML Studio 、 Google AI 平台都是强势的祭品,有时候会和云原生轻度重叠。“开放核心”的后起之秀 Databricks 正在努力推动 IPO。现在 Spark 在 Kubernetes 上运行,Databricks 会开发一个掌舵图吗?
最近发布了 2.0 的流行数据管道工具 Airflow 运行在 Kubernetes 上。Kafka,大型流媒体数据平台,正在简化其架构,而 Strimzi 项目正在简化 Kafka 在 Kubernetes 上的部署。(Strimzi 目前是一个 CNCF 沙盒项目,这意味着你可能要过一段时间才能告别你的专用生产 Kafka 集群。)人们试图将生产数据库放在 Kubernetes 上(我有一些问题)。地理空间数据市场的赢家ESRI将在其旗舰产品 ArcGIS 中添加对 Kubernetes 的支持。特征库,一种用于 MLOps 的多工作流数据库运行在 Kubernetes 上。
这些技术不会在一夜之间全部融入 Kubernetes,cloud native 也没有取得任何胜利,但显然有一些势头。
不要惊慌
尽管很复杂,但我倾向于认为 Kubernetes 扩展到数据基础设施只是 Kubernetes 扩展到一切的一个功能。它正向边缘移动。这是进入 IaaS 。显然,你可以在上面运行区块链。银行都在用。
我一般不知道这将如何结束,但我完全期待它保持令人兴奋和充满活力。这些问题处在极其活跃的社区(数据、ML、云原生、无服务器、开源)、各种形状和大小的供应商以及大量投资的交叉点上。在我看来,这就像是有趣时光的公式。
除非你讨厌钱,否则你绝对不应该跑出去给你团队中的每个数据科学家买一本云原生基础设施书籍和一个 Kubernetes 训练营。如果你是一名数据专业人士,阅读这篇文章并考虑“学习 Kubernetes”,这是一件非常好的事情。多样化可能是件好事。只要意识到你正在让自己陷入什么样的境地。
原载于https://theslipbox.substack.com。
2022 年还会需要数据科学家吗?
在决定职业道路之前,你应该知道以下几点

在 Unsplash 上由 Boitumelo Phetla 拍摄的照片
几年前,数据科学变得非常流行。围绕这个领域有很多炒作,来自不同背景的人都在争先恐后地向数据科学过渡。
数据科学硕士学位开始变得越来越受欢迎,互联网上也不乏在线课程。学生们涌向 Coursera、Datacamp 和 Udemy 等网站,以获得数据科学认证并进入就业市场。
然而,现在人们开始越来越怀疑这个领域。
我读过描述“数据科学之死”的文章有些人认为数据科学只是一种短暂的趋势,在另一个领域取代它之前,这种炒作很快就会消失。
我还阅读了鼓励学习者从事数据工程或 MLOps 职业的文章,因为这些领域是“下一个大事件”,并且将比数据科学职业更长久。
我花了很多时间纠结于是否应该继续学习数据科学,或者花时间学习一种不同的技能。
现在,在数据行业工作了一年多之后,我想分享一下我对这个主题的见解,特别是针对该领域的初学者,他们经常得到相互矛盾的建议,并且对从事数据科学职业犹豫不决。
以下是我将在本文中尝试回答的一些问题:
- 数据工程是下一件大事吗?我应该努力成为一名数据工程师而不是数据科学家吗?
- 随着自动化 ML 工具开始变得越来越先进,它们会取代数据科学家吗?
- 数据科学市场是否已经饱和,是否还有可能获得该领域的入门级工作?
数据工程与数据科学
我在网上读到过许多不同的观点,认为数据工程将取代数据科学成为 21 世纪最热门的工作。
在与数据工程和数据科学团队密切合作后,我得出结论,这两个领域同等重要。
公司需要数据工程师。他们需要能够获取大量数据并使之可用的人。
然后,数据分析师和数据科学家需要使用这些数据来得出商业价值。他们创造出对组织有利的最终产品。
数据工程现在被大肆宣传的原因是因为公司没有足够的数据工程。
过去,这些组织过于看重数据科学家。当他们没有合适的数据管道时,他们雇佣数据科学家来建立盈利模型。
数据科学家擅长模型构建,对于大量实时、非结构化的数据流入,他们无能为力。这意味着他们无法为组织增加多少价值,因为数据没有按照他们要求的方式准备好。
现在公司开始意识到这一点,他们更加重视雇佣数据工程师。
这并不意味着数据科学家是多余的。他们的建模技能对组织来说仍然是有利可图的——这只是以后的事情,一旦数据可以使用。
这两种职业都很受欢迎,并且确实为公司增加了价值。你可以选择你最喜欢做的事情。
自动化会取代数据科学家吗?
像 DataRobot 和 AutoML 这样的工具已经变得越来越流行。越来越多的公司开始投资这些工具来建立预测模型。
这引发了一个新的问题——组织内还有数据科学家的空间吗?还是这些工具会取代他们所做的工作?
我以前用过 DataRobot。该工具旨在帮助业务分析师在没有机器学习或数据科学知识的情况下构建预测模型。
以下是我与 DataRobot 合作后的一些发现:
- 这是一个非常简单的工具。只需点击几下鼠标,即可轻松导入不同格式的数据并构建 ML 模型。
- 参数调整已经完成。DataRobot 搜索每个超参数的一系列值,并选择最符合您的数据的最佳值。这减少了您需要做的手工工作量。
然而,尽管自动化机器学习有这些伟大的功能,但在很长一段时间内,它们都无法取代数据科学家。
我研究了 DataRobot 的各种用例,但仍然需要自己执行一些任务,比如添加特性权重。此外,在准备好构建模型之前,所有的预处理都需要手动完成。
数据需要根据业务用例进行定制和分解。数据科学家通常会花更多的时间根据需求做数据准备,而花更少的时间建立模型。
这种繁重的工作仍然需要由数据科学家来完成,而数据科学家通常完成的 80%的任务是无法自动化的。
当使用 DataRobot 时,我仍然必须根据我需要的结果手动更改参数(如模型的阈值和特征权重)。
使用这些工具时,仍然需要人的判断力。
只要你拥有你所从事领域的领域知识,以及分析和编程技能,你就不会被自动化工具所取代。
这些工具可能有助于加快您的工作流程并减少您使用的计算资源量,但它们旨在补充您所做的工作,而不是取代它。
数据科学家还有需求吗?
2020 年,每个人在一秒钟内产生 1.7 兆字节的数据。互联网用户每天产生大约2.5 万亿字节的数据。
数据正被用于在许多行业(医疗保健、金融、营销、商业等)创造巨大的变化。
我在市场营销领域工作,每天都要处理数百万个数据点。这些数据点用于观察客户行为,并提出不同的目标策略。
公司需要雇佣能够从这些数据点中获取价值的人。
仅仅一个业务分析师无法做到这一点,因为他们不具备技术知识。
只要你能处理海量的数据,并借助这些数据解决问题,行业里就有你的空间。
然而,你需要超越模型构建。你需要有领域专业知识。你需要知道如何利用你的技能获取外部数据。你需要有很强的沟通和解决问题的能力。
这些技能将使你与一般的数据科学有志之士区分开来。
实践中的数据科学与理论上的数据科学非常不同。
在这个行业工作时,如果你工作一整天来使你的模型比以前精确 1%,这并不重要。这些事情在 Kaggle 竞争中可能很重要,但对利益相关者来说并不重要。
像每个特性与你的模型的相关性,模型的可解释性,以及你的模型对现实世界场景的适用性这些事情要重要得多。
拥有这些技能的数据科学家是不可替代的。
结论
数据科学不会消亡。然而,这个领域正在经历转变,公司开始寻找能够用数据解决问题的人。
这不仅仅包括建模。当面临商业问题时,答案并不总是在于创建机器学习模型。
有时,一个简单的 SQL 查询或可视化可能会告诉您需要知道的一切。采用 ML 技术来解决每一个数据问题既昂贵又耗时,这就是为什么你需要掌握更多的知识。
只要您不断学习和创建数据驱动的解决方案,没有任何自动化工具可以取代数据科学家的技能。
谷歌妈妈会扼杀 SEO 吗?
人工智能
妈妈将彻底改革搜索引擎。

一周前,谷歌在 T4 举办了一年一度的谷歌 I/O 大会。他们展示了现有技术的更新,如谷歌地图或谷歌照片,以及一些令人惊叹的技术,如 LaMDA ,一个熟练的健谈人工智能,可以彻底改变聊天机器人技术,或 MUM。
今天我来说说 MUM ,代表多任务统一模式。简而言之,这项技术旨在让搜索引擎更加强大,就像几年前 BERT 所做的一样。
我将回顾什么是 MUM,以及它将如何影响未来的 SEO,直到让它过时。尽情享受吧!
妈妈——搜索引擎的大脑
MUM 是谷歌搜索引擎的改进。像其他流行的最先进的语言模型,如 GPT 3 或 LaMDA,MUM 是基于 transformer 架构的。BERT (MUM 的前身)在这方面是类似的,主要区别是 MUM 强大 1000 倍。
正如 Pandu Nayak 在谷歌博客上写的那样,MUM 旨在解决的主要问题是,“必须输入许多查询,执行许多搜索才能得到你需要的答案。”对于没有简单答案的查询,MUM 最大限度地发挥了它的力量,帮助谷歌的搜索引擎处理复杂的任务。
在演示中,Prabhakar Raghavan 用一个查询例子说明了妈妈的力量:“你已经徒步走过了亚当斯山。现在你想明年秋天去富士山远足,你想知道要做哪些不同的准备。”这是一个我们可以想象向徒步旅行专家,而不是向搜索引擎提出的问题。
利用今天的技术,我们必须搜索我们能想到的每一个变量(例如,天气、一年中的时间、地形的高度、齿轮的类型、岩石的类型……),然后比较我们的结果以提取有效的答案。
然而,妈妈可以使用其全能的工具包直接解决这个问题:“富士山与亚当斯山的海拔大致相同,但秋天是富士山的雨季,所以你可能需要一件防水夹克,”妈妈回答道。
妈妈的技能——多语言和多任务
妈妈已经接受了 75 种语言的训练,这使得系统能够打破语言障碍。有了今天的技术,我们必须用日语搜索信息,才能为我们的富士山徒步旅行找到满意的答案。妈妈可以直接查找信息,然后为你翻译。
妈妈已经接受了 多任务的训练,实现了“比以前的模型更全面地理解信息和世界知识”这里的可能性很大。妈妈能理解亚当斯山和富士山是山。它可以理解你需要知道地质学上的差异,但你不会关心动物和植物。它可以理解,准备可能不是指身体上的训练(就像你已经在亚当斯山徒步旅行过的那样),而是指“找到合适的装备”之类的事情。
妈妈不仅能够“理解”语言,还能“生成”语言(在这个意义上,它可以与 GPT-3 相提并论)。然而,我想在这里修饰一下“理解”这个词。我不认为妈妈(也不认为 GPT-3)能理解语言。我要说这些系统是塞尔的中国房间论点的真实例子。为了理解,我们需要意义。为了获得意义,我们需要将语言符号的形式与它们在世界中的外部经验表征联系起来。计算机内部的语言模型——妈妈、GPT-3 或 LaMDA——可以访问符号的形式,但它们无法体验世界,这使得意义遥不可及。
最后,值得一提的是,谷歌将责任作为其人工智能系统的主要优先事项。对于他们创造的每一个人工智能,他们都试图减少偏见,减少碳足迹。妈妈也不例外。
妈妈的边缘——多模态
但到目前为止,MUM 带给我们的最重要突破是它管理多模态信息的能力(这超出了 GPT-3 和 LaMDA 的能力)。MUM 可以结合图像和文本信息(未来谷歌将包括音频和视频)。
最终,你可能会拍下你的登山靴,然后问,“我能穿这双靴子去富士山吗?”“妈妈会理解这个图像,并将其与你的问题联系起来,让你知道你的靴子会很好。”
这在这个级别的人工智能世界中是新的,这是迈向人工智能的关键一步。当将人工智能系统与人脑进行比较时,可以更好地说明这一里程碑的重要性。让我们的大脑在世界导航中表现出色的特征之一是它的多感官特性。
- 世界是多模态的。这意味着我们周围的大多数事件/物体都会产生不同种类的信息(电磁、机械、压力、化学等。).想想任何物体,比如苹果。它有颜色、形状、质地、味道、气味…
- 我们的大脑是多感官的。我们被赋予了一套感官系统,使我们能够感知世界的多模态本质。大脑解释并整合所有这些信息,形成一个单一的现实表象。
保持距离,妈妈可能是第一代能够像我们一样结合多模态信息的人工智能。
妈妈可以让搜索引擎优化过时
谷歌从它的概念开始就一直试图让搜索引擎感觉更自然。当他们在 2019 年推出 BERT 时,纳亚克在他的博客文章中写道搜索引擎没有正确理解查询的事实是“人们经常使用”关键字-ese 的原因之一”。“谷歌搜索是它自己的交流方式,与我们向另一个人提问的方式相去甚远。
BERT 是将搜索引擎从匹配关键词转变为解释句子中单词的上下文的第一块基石。伯特,在妈妈之前,已经改变了搜索引擎优化的游戏。Google 承认除了为用户优化之外,没有其他方法可以为 BERT 优化。BERT 降低了在网页排名中使用关键字的影响。系统现在最关心的是网页是否回答了用户的查询,即使没有准确的关键词。
妈妈可以彻底改变搜索引擎,以至于搜索引擎优化的概念变得过时。人们不会直接寻找网页,他们只会简单地问妈妈一个问题,妈妈会像一个人的私人助理一样完成这项工作。关键词无关紧要。当妈妈能理解自然语言时,我们为什么要强迫自己用“关键字-ese”语言写查询呢?
当然,关键字仍然很重要,因为查询仍然会包含它们,但它们不会再帮助网页定位。为 MUM 优化内容的想法不会存在。不会有一个直接的方法来对付搜索引擎。人们将永远不再为算法写文章,而是为人民写文章。
最后的想法
妈妈的力量依赖于它的多语言、多任务,尤其是它的多模态特性。这将彻底改变搜索引擎,可能会使 BERT 在 2019 年的影响增加数倍。正如 Edwin Toonen 所说,谷歌搜索引擎将不再是一个搜索引擎,而是演变成一个“知识展示机器”
每次谷歌对算法进行更新时,搜索引擎优化专家都必须适应。当伯特出场时,他们没有看到对他们的表现有多大影响。然而,如果有一天我们不需要搜索任何东西,而是我们可以简单地用自然语言编写我们的查询,让一个超级强大的人工智能为我们找到答案,我们只能等着看 SEO 会发生什么。
订阅 获取更多关于人工智能、哲学和认知科学的内容!
我的数据科学项目会成功吗?

评估商业成功的可能性
简介。
在之前的博客中,我们讨论了评估技术成功概率的方法——P(T)。目的是使用客观标准和数学公式来推导数据科学或人工智能原型成功的概率。得出的数字指导决定是激活还是放弃项目的原型阶段。
在这篇博文中,我们继续可行性评估的话题。我们专注于推导商业成功的概率——P(C)。我们将 P(C)定义为
- 我们的原型可以扩展并交付给最终用户
- 最终用户将按照设计使用这些功能。
最终用户可能是购买嵌入了原型技术的产品的客户,或者是在内部运营中使用商业原型的公司用户。在后一种情况下,说“可操作的原型”可能更准确,但我们将继续使用“商业化”作为我们选择的术语。
评估商业成功的概率 P(C)原则上类似于评估 P(T ),但在一个重要的细节上有所不同。虽然 P(T)函数主要包括评估专业知识的技术值和参数,但 P(C)在很大程度上取决于用户群和企业文化的心理评估。这是一个挑战。我们的目标是客观,但我们将讨论内在难以量化的措施。我们是否危险地沉溺于主观领域?我必须承认,比我喜欢的还要多,但这个过程仍然保持着严谨。
我将用一个例子来说明。让我们从注入概率函数的参数列表中选择一个度量。我们将在本文中进一步讨论完整的列表,但现在让我们讨论“接受技术变革的意愿”。这似乎是一个模糊的衡量标准,但只要稍加努力,我们就可以将它与可测量的历史事件联系起来。下面是一个可能的过程:
- 创建一个实例列表,列出您的组织在不久前推出替换软件、经历重大版本升级或实施全新技术的实例。几个实例总比一个好。太多的实例可能会使分析变得混乱,所以要坚持突出的几个。
- 对于每一个实例,采访以前的项目负责人,并征求对用户抵制的看法。询问他们对该指标的评分(10 分制)。取平均值。
- 收集关于计划采用时间表和实际采用时间表的信息。
- 计算时间超出计划的百分比
- 将原因百分比分配给用户推回
- 在可用的公司或用户论坛上查看关于该项目的评论。对负面反应的代表性进行简单的统计分析。
- 使用这些附加信息,根据您的发现向上或向下微调项目冠军评估数字。
从访谈中确定一个初始的度量数字,然后根据新的信息进行小的调整,这种方法具有令人惊讶的功效。加德纳和泰特洛克在 2013 年出版的《超级预测:预测的艺术和科学》一书中描述并记录了这种方法的一种变体。我会推荐它作为灵感。
商业成功评价。
现在让我们来评估一下我们的数据科学或人工智能原型商业化需要什么。我们将追求两条不同的轨道:产品和运营改进。产品跟踪代表将技术商业化,将产品交付给市场中的最终用户。亚马逊产品推荐或谷歌地图“下一个目的地建议”都是商业化数据科学或人工智能项目的例子。相反,运营改进路线侧重于为公司内部用户带来好处。产品需求预测或用于生产质量保证的机器视觉就是两个例子。
我们在公式中使用的参数与 P(T)评估中的参数相呼应。然而,每个类别都必须通过生产镜头进行审查。当评估原型的数据时,我们询问数据是否干净和准确。如果数据在源位置生成时不干净,那么我们是否可以适当地清理并准备好供使用。我们也许能够在原型阶段完成这一点,但是在生产阶段会出现一系列新的复杂性。例如,我们可能以极高的音量和极快的速度接收数据;这两种情况都使生产部署经常需要的近实时数据清理和验证变得复杂。有时,这些增加的复杂性会将原型阶段的易处理任务变成生产阶段的不可能任务。因此,对 P(T)和 P(C)提出的类似问题可能会产生截然相反的答案。在进行 P(C)分析时,请记住这种观点的转变。
以下列表适用于公司内部推广。产品的商业化需要一个不同的焦点,这一点我可能会在以后介绍。我邀请您将这个列表视为一个可变的起点,您可以调整它以适应特定的垂直市场和场景。一旦你建立了自己的清单,你就用 1-10 分的标准评估每一项,然后把这些值代入顺序风险评估(ORA)评分公式。所得的 ORA 分数是以百分比表示的商业成功的概率。如果是在舒适门槛以上,你做项目;如果它立即徘徊在阈值附近,那么你可以进一步考虑它。
企业支持和业务:
- 在管理层定义项目冠军
- 企业利益相关者的认同
- 原型后范围的利益相关者协调
- 清晰阐述的商业价值
- 为项目分配足够的预算(过去和现在)
数据:
- 我们拥有所有必需的数据特征
- 每个特征的数据都是完整的
- 每个特征的数据都清晰而准确
- 我们可以根据生产速度验证和调整数据
- 我们可以在没有官僚障碍的情况下访问数据
基础设施和团队:
- 我们拥有处理高吞吐量和高容量的工具
- 我们有数据科学模型测试、再培训和部署的流程
- 我们拥有持续集成的经验和工具
- 充分分配发展资源(当前和过去)
- 熟悉数据科学库
- 根据需要扩充团队的章程
部署和推广
- 最终用户参与原型开发
- 培训用户的现有流程
- 向用户展示技术的现有流程
- 在过去的开发过程中遵守时间表
- 在过去的推广过程中遵守时间表
- 用户接受技术变革的意愿
- 发布前的 Beta 测试和验证流程
- 发布后维护工程的流程和预算
现在我们有了 ORA 分数所需的一组值。我们使用与评估技术成功概率相同的公式来计算概率。

在哪里,

是单个参数分数的数组
N 是一组参数,在我们的例子中 N = 20
C 是压缩系数, C = aN ,其中 a = 3(凭经验选择)

是一组参数权重,用于确定每个参数的关键程度。 w = 6 对于单个参数当 s = 1 时会起到将 P(T) 压制在 0.5 以下的作用。

是最安全的,除非很好地理解了特定重量的原因。
定义 P(C)的目的是决定是否继续商业化阶段,就像原型已经完成一样。在实践中,我们在开始原型工作之前进行可行性评估,并计算技术和商业成功概率。这两者的产物给了你最初的想法在生产中看到用途的成功的总可能性。
P(T)和 P(C)是独立的,但合在一起描绘了一幅更全面的风险图。
量子计算机会完全取代经典计算机吗?
你会把冰箱换成冰柜吗?
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
你会把冰箱换成冰柜吗?大概不会。尽管你的冰箱可以做冰箱能做的一切。只要让门开一点点,让外面的热量补偿冰箱更高的冷却能力。

作者图片
你觉得这样效率低吗?然后,将这种低效率指数化,你最终会明白用量子计算机取代经典计算机意味着什么。
如果你觉得这个比喻不恰当,我补充一个简短的免责声明: 这根本不是一个比喻。
一台经典的电脑只不过是一台冰箱。内部的风扇将 CPU(中央处理器)冷却到不超过 100 摄氏度。从物理角度来看,CPU 的主要输出不是计算。是热。计算只是一个副产品。
在这种观念下,量子计算机只不过是一个冰箱。QPU(量子处理器)周围的设备将它冷却到零下 273 摄氏度,非常冷。它不会这样做,因为 QPU 产生这么多热量。相反,QPU 根本不会产生太多热量。但是量子计算对任何热量都极其敏感——如果当我们在接近绝对最小值的时候谈论热量是恰当的话。

作者图片
所以,只要经典计算机完成工作,用量子计算机取代经典计算机似乎并不高效。
但是,我们必须记住,量子硬件的发展仍处于起步阶段。如果我们回顾经典计算机的早期,没有人预测到有一天我们能达到的能力和小型化。早期的计算机用机电继电器代替晶体管。它们效率低下且容易出错。但是他们达到了目的。他们证明制造计算机是可能的。
底层技术变化非常快。晶体管取代了继电器。1965 年,摩尔假设,在一个给定的空间单位中,我们可以容纳的晶体管数量大约每两年翻一番。这一轨迹在过去 50 年里一直如此。然而,我们目前见证了摩尔定律的大幅放缓。
但是对计算性能的需求持续增长。新技术,比如深度神经网络、区块链,以及我们每天收集的海量数据,都依赖于底层硬件来完善。
当然,量子计算机不会取代经典计算机,只要它们像早期的经典计算机一样低效和容易出错。但是一旦我们找到了高效建造量子计算机的方法,会发生什么呢?如果我们进入一个摩尔定律适用于量子器件的时代,会发生什么?
一般来说,我们可以在 QPU 上运行任何可以经典运行的计算。量子比特(qubit)是经典比特的推广。经典位不是 0 就是 1,量子位是 0 和 1 的复杂线性组合。我们称这种状态为叠加态。但是这种叠加状态包括 0 和 1 的特殊情况。所以,我们可以像使用普通比特一样使用量子比特。当然,这将是对能力的巨大浪费。然而,这是可能的。

作者图片
但是,量子比特很有趣,因为它们可以做经典比特做不到的事情。我们可以纠缠量子位,让它们形成共享的叠加态。然后,我们可以用量子位表示的并发态的数量呈指数增长。这种能力让我们解决经典解决不了的问题(在任何合理的时间内)。
然而,即使我们进入了“量子摩尔”时代,QPUs 也需要相当长的时间才能变得像经典计算机一样大(就量子比特的数量而言)。这是因为我们不需要那么多量子比特来做经典计算机做不到的事情。
每当 qpu 成为负担得起的硬件时,我们很可能会看到它们补充我们的计算机,而不是完全取代底层技术。尽管 QPU 可以完成任何任务,但一直使用它是不合理的。如果一个问题可以经典地解决,我们将继续经典地解决它。我们只会用 QPU 来解决传统上难以解决的问题。
我们已经目睹了类似的发展。不久前,当我们考察计算机的性能时,CPU 是最重要的处理器。首先,我们看看时钟速度。后来,我们看了核心的数量。
但是上面提到的现代技术的要求已经超过了今天最好的 CPU 所能做到的。因此,我们为计算机配备了 GPU(图形处理单元),甚至 TPU(张量处理单元),来完成经典 CPU 难以完成的工作。然而,我们并没有取代 CPU,因为它仍然是大多数任务的最佳处理器。
GPU、TPU 和未来的 qpu 是解决特定类型问题的专用处理器。它们补充了 CPU,但并没有取代它。
但是,从技术上讲,有 QPU 的计算机不就是量子计算机吗?因此,在这种观念下,即使量子处理器没有取代中央处理器,我们也会看到量子计算机取代经典计算机。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。

在这里免费获得前三章。
量子计算机会取代经典计算机吗?
经典计算不会消失,但量子技术有可能颠覆许多行业。利用两者的优势来释放 quantum 的全部潜力至关重要。

量子计算机已经在许多应用中击败了经典计算机。作者图片
量子计算的承诺是多方面的:它可以帮助以前所未有的速度开发救命药物,为金融建立更好的投资组合,并开创密码学的新时代。这是否意味着量子计算将成为标准,经典计算将被淘汰?
简而言之,答案是否定的。经典计算机拥有量子计算机难以企及的独特品质。例如,存储数据的能力是经典计算机独有的,因为量子计算机的内存最多只能持续几百微秒。
此外,量子计算机需要保持在接近绝对零度的温度下,大约为-270 摄氏度(-450 华氏度)。普通消费者家里没有如此强大的冰箱,考虑到相应的能源消耗及其对环境的影响,他们也不建议这样做。所有这些挑战表明,量子计算机不太可能成为大多数家庭或企业的固定设备。
更有可能的情况是,学术界和工业界的研究人员将通过云服务访问量子计算机。尽管量子技术仍处于早期阶段,但像亚马逊网络服务和微软 Azure 这样的供应商已经提供了云接入。
毫无疑问,量子计算将在未来十年改变许多行业。然而,经典计算机将永远发挥作用。然而,一如既往,细节是魔鬼:哪些问题更适合量子计算机,哪些问题更适合经典计算机?哪些行业将从采用量子和经典混合计算策略中获益最大?
量子计算机不会取代经典计算机
量子计算从 20 世纪 80 年代早期就已经存在。然而,四十年后,我们甚至还没有在世界范围内拥有三十几个量子设备。量子优势的第一手证明,即量子计算机比经典计算机快得多,只是在 2019 年由谷歌展示的。
根据麦肯锡最近的一份报告,到 2030 年,我们甚至可能不会有超过 5000 台量子机器。这不仅仅是因为量子计算机很难长期存储数据,也很难在室温下运行。事实证明,量子计算与经典计算有着本质的不同,开发、部署和获得这项技术的好处需要时间。
这种根本差异的一个例子是,量子计算机不能像经典计算机那样给出直截了当的答案。经典计算非常简单:你提供一个输入,一个算法处理它,最后你得到一个输出。另一方面,量子计算接受一系列不同的输入,并返回一系列可能性。你得到的不是一个直截了当的答案,而是对不同答案的可能性的估计。
这种计算方式在处理复杂问题时非常有用,在这些复杂问题中,您有许多不同的输入变量和复杂的算法。在经典计算机上,这样的过程通常需要很长时间。量子计算机可以缩小可能的输入变量和问题解决方案的范围。之后,人们可以通过测试量子计算机与经典计算机提供的输入范围来获得直接的答案。
因此,在未来的几十年里,经典计算机仍将是有用的。它们的持续相关性不仅仅是量子计算机需要多长时间才能发展到被主流采用的问题。这也是关于量子计算返回的解的模糊本质。作为人类,我们更喜欢直截了当的答案,这些答案只有经典计算机才能获得。

经典计算机将在几十年内仍然有用。作者图片
量子计算的技术限制
然而,我们应该记住,这种工作方式还没有经过广泛的测试。经过四十年的努力,量子技术仍处于起步阶段,有一些非常棘手的限制需要解决。这些限制将确保经典计算机仍然适用。
量子计算机上的信息以称为量子位的单位存储和处理。类似于经典计算机中的位,它们可以有不同的值,比如 0 或 1。然而,量子位也可以是 0 和 1 的混合,比如 30%的 0 和 70%的 1。这种能力使它们变得非常强大:一台具有 N 位的经典计算机一次最多可以执行 N 次计算,而量子计算机最多可以处理次 2^N 次计算。因此,如果一个经典处理器管理 10 次计算,一个量子处理器将管理 2 个⁰,即 1024 次计算。
问题是,建造具有许多量子位的量子计算机极其困难——目前的记录是一台拥有 76 的中国机器。公平地说,羽翼未丰的初创公司和科技巨头都承诺将很快制造出拥有数千量子位的机器。然而,更大的量子机器往往具有更低的连通性,这意味着量子位不能很好地相互通信。这种连接的缺乏降低了系统的整体计算能力。
最后,量子机器非常容易出错。这些计算误差是量子系统固有的,本质上无法避免。这就是为什么大量的资金和人才被投入到量子错误检测中,开发出能够发现自身错误并加以纠正的机器。虽然在这个领域已经取得了巨大的进步,但是量子误差不可能完全消失。即使使用高度精确的量子计算机,用经典计算机验证最终结果仍然是必要的。
等待中断
将量子计算的技术限制添加到存储硬件所需的过冷温度中,你就开始理解为什么截至目前大多数公司都在犹豫投资量子计算。然而,在某些行业,即使量子计算机解决问题的速度比经典计算机“仅”快 1000 倍,它也可能变得经济可行。这包括金融、制药和加密等行业。
因此,随着量子系统带来越来越多的经济效益,公司将一点一点地采用量子系统似乎是合理的。在此期间,经典计算机将保持相关性,甚至至关重要,以维持现状。很少有公司会在早期进行大规模投资,这意味着经典计算仍将在工业中承担大部分工作。
另一方面,无论现在看起来风险有多大,大型投资都有望成为量子计算应用取得真正突破的驱动力。这种破坏在两个领域尤其明显:药物开发和密码学。这两个领域依靠巨大的计算能力蓬勃发展,量子机器可以以前所未有的方式提供这种能力。

量子将在许多领域蓬勃发展,但经典计算机仍将是重要的。作者图片
量子将会蓬勃发展——而经典将会有所帮助
并不是所有的行业都会以同样的方式从量子计算中获利。据麦肯锡称,量子计算可以在四个领域产生巨大的长期收益。尽管如此,经典计算将在这些领域保持相关性,并补充量子技术的优势。
- 药物开发:药物分子的计算机模拟是必不可少的,因为它们可以削减成本和时间,有时甚至是大幅削减。今天,这种类型的模拟只能在相对较小的分子上进行。然而,如果公司对蛋白质感兴趣,因为蛋白质通常有成千上万种成分,他们需要制造它们并在现实生活中测试它们的特性,因为今天的计算资源不足以进行精确的模拟。量子模拟可以大幅降低开发成本,并帮助药物更快上市。然而,由于量子计算总是返回一系列的可能性,药物的最佳分子结构仍然需要用经典计算机来确认。
- 优化问题:工厂里最有效的设备布局是什么?部署车辆以确保高效运输网络的最佳方式是什么?5 年、10 年或 30 年后获得最优回报的最佳投资策略是什么?这些都是复杂的问题,最佳答案并不总是显而易见的。有了量子计算机,人们可以极大地缩小可能性,然后使用经典计算机得到直接的答案。从制造业到运输业再到金融业,这些问题在各个领域都比比皆是。
- 量子人工智能:数十亿美元正投资在自动驾驶汽车上。目标是让车辆变得如此智能,以至于它们能适应地球上任何地方的繁忙道路。尽管有很多人才致力于训练 AI 算法来学习如何驾驶,但事故仍然是一个问题。量子人工智能可能比目前的方法更快、更强大,可能有助于解决这个问题。然而,这些好处可能只有在十年后才能收获,因为量子人工智能今天远不如量子模拟或密码学发达。因此,大多数人工智能算法将继续部署在经典计算机上。虽然现在自信地预测还为时过早,但在几十年内大多数人工智能将是量子的并非不可想象。
- 密码术:今天的安全协议依赖于随机数和数字因式分解的高度,经典计算机可以计算生成密码,但很少能够破解密码。不过,几年后,量子计算机可能会强大到可以用“T2”破解任何密码。这就是为什么研究人员必须开始投资新的量子安全加密技术。然而,量子技术是一把双刃剑,它不仅能破解每个密码,还能生成新的、无法破解的密钥。这个空间正在迅速融入这个新的现实。由于经典计算机将保持相关性,量子安全加密技术也必须为这些计算机而存在。这是可能的,公司已经开始用这种方式保护他们在传统电脑上的数据。
为量子未来做准备
谷歌、IBM 和许多不同的初创公司都希望每年将量子计算能力翻一番。这意味着一些公司需要跟随巴克莱、巴斯夫、宝马、陶氏和埃克森美孚的脚步,这些公司已经在量子技术领域表现活跃。
显然,并非每个行业都可能以同样的方式受益。制药、金融、旅游、物流、全球能源和材料等行业可能会比其他行业更早获利。这也意味着,如果不想被竞争对手甩在后面,这些领域的玩家需要迅速做好准备。
如果有战略意义,这些行业的公司应该效仿巴克莱(Barclays)或埃克森美孚(ExxonMobil)等领先者,在内部建立一支量子人才团队。这样的人才已经很少了,大学也不太可能跟得上不断增长的需求。作为替代,他们可以考虑直接与正在开发量子技术的公司合作,这可能会给他们带来竞争优势。
当然,这并不意味着这些公司将停止使用经典计算机。相反,量子计算将为他们带来特定任务的巨大好处,如药物开发、金融工程等。
与将从量子技术的出现中受益的公司相比,其他公司将不得不投资于保持安全的方法。具体而言,拥有长期数据资产的公司应该开始投资量子安全加密技术,以保护自己免受未来的攻击。这并不意味着他们需要很快使用量子计算机,但他们应该确保他们在未来的量子攻击中是安全的。关注的领域从航空航天工程到药物开发,再到市场研究的社会经济数据。
简而言之,许多不同行业的公司都需要加快步伐。一些人是因为如果他们采用量子技术,他们将从中受益,另一些人是因为他们需要在威胁面前采取行动。无论如何,不接受这种新的量子经典混合动力的公司都有被甩在后面的风险。
量子计算不会接管世界。但是它将在未来的十年或二十年里通过与经典计算机的全面合作产生重大影响。
本文原载于 建于 。
GPU 会开创计算机架构的新黄金时代吗?

促进人工智能软件进一步发展的架构选择
2018 年 6 月 4 日,作为 2017 年图灵奖的获奖者,约翰·汉尼斯和大卫·帕特森在他们的图灵讲座上讲述了计算机架构的新黄金时代。讲座的三个关键观点是:
- 软件进步可以激发架构创新。
- 提升硬件/软件接口为架构创新创造了机会。
- 市场最终解决了架构争论。
我想用第四个观点来修正这三个关键观点,从而完成这个循环:
获胜的架构促进了随后的软件进步。
自轩尼诗/帕特森讲座以来,市场可以说已经实现了人工智能的洞察#3,并决定将图形处理单元(GPU)作为促进人工智能革命的获胜架构。在这篇文章中,我探索了人工智能革命如何激发架构创新和重新发明 GPU。我希望回答我自己的一个重要问题:
GPU 会开创计算机架构的新黄金时代吗?
特定领域架构
Henessy 和 Patterson 提出了特定领域体系结构(DSA ),以创新计算机体系结构,并努力迈向新的黄金时代。顾名思义,GPU 是 3D 图形的 DSA。它旨在渲染 3D 虚拟世界的照片级逼真图像;然而,几乎所有的人工智能研究人员都使用 GPU 来探索 3D 图形以外的想法,在人工智能“软件”,也就是神经网络架构方面取得突破。虽然在 3D 中仍然不可或缺,但 GPU 已经成为人工智能的“CPU”,因为它促进了人工智能的软件创新。除了 3D 用途之外,GPU 架构师一直在为非 3D 用途提供 GPU 的计算资源。我们称这种设计理念为通用 GPU (GPGPU)。
如今,我们看到 AI DSAs 取代 GPGPU 的激增,试图用更好的性能取代 GPU。就连 GPU 本身也在双重人格之间挣扎:AI DSA 和 3D DSA。原因是 AI DSA 需要加速张量运算,这在 AI 中很丰富,在 3D 中却没有。同时,3D 固定功能的硬件听起来对 AI 来说是不必要的。
因此,主要架构的争论似乎在问
- GPU 会保住 AI“CPU”的宝座吗?
- GPU 会不会分叉成两个 DSA,一个用于 AI,一个用于 3D?
我的预测如下:
- GPU 硬件/软件接口将使 GPU 成为人工智能的“CPU”。
- 基于人工智能的渲染将使张量加速成为 GPU 的中流砥柱。
- 虚拟世界和现实世界相互映射的数字孪生将主宰市场,最终解决架构之争。
GPU 硬件/软件接口
我们可以将 GPU 在 3D 领域的主导地位和在 AI 领域的巨大成功归因于其硬件/软件接口,这是 GPU 和 3D 图形软件架构师努力拥抱的。这个接口是解决以下矛盾的关键。虽然 GPU 社区继续使 GPU 更加通用,但行业的其他人已经转向更专业的硬件来解决摩尔定律的消亡。

GPU 管道(图片由作者提供)
双层可编程性
从概念上讲,GPU 是处理阶段的长线性流水线。不同类型的工作项在管道中流动时被处理。在早期,每个处理阶段都是一个固定功能块。程序员对 GPU 的唯一控制是调整每个块的参数。如今,GPU 硬件/软件接口让程序员可以随心所欲地处理每个工作项目,无论是顶点还是像素。不需要在每个顶点或像素循环中处理循环头,因为 GPU 架构师在固定的函数中实现它。这种架构选择让程序员有责任关注循环体或“着色器”,它通常以工作项的类型命名,例如用于处理顶点的“顶点着色器”和用于处理像素的“像素着色器”。
现代游戏如何用这样的线性流水线制作出惊艳的画面?除了在一次通过管道时控制不同类型的着色器之外,程序员还可以多次渐进地通过管道来产生中间图像,最终产生在屏幕上看到的最终图像。程序员有效地创建计算图,描述中间图像之间的关系。图中的每个节点代表一次通过 GPU 管道。
通用计算资源的集中池
通用计算资源的集中池在处理阶段之间共享,并承担繁重的工作。这种方案的最初动机是负载平衡;在不同的使用场景中,处理阶段的工作负载可能会有很大的不同。被称为着色器核心的计算资源已经变得更加通用,以实现灵活性和产品差异化。
GPU 架构师抓住机会将集中式着色器池作为 GPGPU 提供给非 3D 应用程序。这种设计方案使 GPU 能够在运行人工智能任务方面实现突破,即使是作为兼职。
平衡专业化
GPU 架构师通过在不改变硬件/软件接口的情况下添加协处理单元来定期“加速”或“指定域”着色器池。纹理单元就是这样一个协处理单元,纹理贴图中的纹理元素通过它被提取并过滤到着色器池。特殊功能单元(SFU)是另一个协同处理单元,执行超越数学功能,如对数、平方根倒数等。虽然拥有多个功能听起来类似于 CPU 中的超标量设计,但有一个显著的区别:GPU 架构师根据“平均”着色器程序使用它的频率来分配协处理单元的吞吐量。例如,我们可以给纹理单元着色器池吞吐量的八分之一,假设纹理操作平均八分之一的时间出现在基准测试或游戏中。当一个协处理单元繁忙时,GPU 会切换任务以保持自己忙碌。
三维张量加速
在我的介绍中,我指出 GPU 难以在 3D 中采用张量加速。让我们看看,如果我们改变 GPU 渲染典型游戏帧的方式,这种趋势会如何逆转。GPU 首先为每个像素生成并在 G-buffer 中存储着色像素所需的所有信息。从 G 缓冲区,我们计算如何照亮一个像素,然后是几个处理步骤,包括
- 移除锯齿状边缘(抗锯齿(AA))
- 将低分辨率图像放大到更高的分辨率(超分辨率(SR))
- 为整个帧添加特定的视觉效果,如环境遮挡、运动模糊、高光滤镜或景深。
我们称这种渲染方案为延迟着色,因为对一个像素的着色是“延迟”的,直到每个像素都得到它需要的信息。我们将光照后的处理步骤称为后处理。今天,后期处理消耗了大约 90%的渲染时间,这意味着 GPU 的屏幕时间主要花在 2D 而不是 3D 上!
英伟达已经展示了基于人工智能的 DLSS 2.0,据称它可以产生比没有 DLSS 2.0 时更好看的图像。此外,NVIDIA 还为光线跟踪提供了基于人工智能的蒙特卡罗去噪,通过它,我们可以使用少量光线来实现只有使用更多光线才能实现的质量。此外,人工智能激发了许多其他类型的后处理的新解决方案,例如用于环境遮挡的 NNAO 和用于景深的 DeepLens 。
如果基于人工智能的后处理成为主流,张量加速将成为 GPU 个性的 3D 方面的支柱。GPU 分化成 3D DSA 和 AI DSA 的可能性会变小。
3D/AI 融合
为了解决架构的争论,我们想要解决最后一个难题:我们是否应该最终移除 3D 渲染中的固定功能硬件,尤其是对 AI 而言?请注意,通过 GPGPU,GPU 可以作为纯“软件”进行 3D 渲染,而无需使用任何固定功能的硬件。
在严格意义上,给定场景参数,3D 渲染模拟光子如何从光源传输通过空间,以与 3D 虚拟世界中的对象进行交互。由 GPU 进行的传统 3D 渲染是这一过程的非常粗略的近似。因此,微软在公告中称“[传统的基于光栅化的] 3D 图形是一个谎言”,以促进光线跟踪成为“未来的完整 3D 效果”然而,一个 3D 渲染纯粹主义者可能仍然会拒绝光线追踪,在光线追踪中,我们通过追踪光线从像素向后进入 3D 虚拟世界来实现 3D 渲染,这也是不真实的。
这两种方法都近似于基于模拟的 3D 渲染。无论哪种情况,我们都将 3D 虚拟世界的建模或内容创建从渲染中分离出来。在第一种情况下,建模 3D 虚拟世界需要工程师和艺术家进行大量艰苦和创造性的工作来描述每个对象及其与光交互的物理属性。在第二种情况下,关于渲染,完全真实是不可能的,因为我们需要大幅简化 3D 渲染,以满足资源预算内的不同性能目标。
与用最知名的科学知识和数学理论为给定问题找到解决方案相反,人工智能方法是关于从数据中“学习”计算模型或神经网络。我们通过试错法反复调整网络参数。我们通过先前的参数估计向前运行网络,并测量不匹配或“损失”然后,我们根据其梯度调整参数以减少损失,有效地在梯度的相反方向上导航损失景观。这种机制被称为反向传播,它要求沿正向路径的所有计算都是可微分的,以便参与计算梯度。
神经渲染是一个新兴的人工智能研究领域,使用上述方法研究 3D 渲染。下面是我跟踪神经渲染进展的思维导图:

作者图片
这个 3D 虚拟世界的模型被隐含地表示为神经网络参数(参见 NeRF 、格拉夫、长颈鹿),这些参数是我们通过比较真实世界的图像和我们从虚拟世界渲染的图像而推断出来的。然后我们反向传播比较的梯度来调整神经网络参数。可选地,我们可以从数据中学习显式 3D 网格(参见深度行进立方体、 GAN2Shape )。实际上,建模 3D 虚拟世界与学习神经网络参数是一回事。这一过程要求我们在前进路径中包括 3D 渲染管道,并在紧密循环中集成 3D 虚拟世界的建模和渲染。通过反复渲染和测试真实世界的图像,我们获得了所需的模型和场景参数,我们可以用它们来渲染虚拟世界的新视图。
在这个框架内,我们可以选择不调整每个参数的整体,例如,保持一个物体的形状不变,但估计它的位置(见 iNeRF )。通过这种方式,我们可以有效地识别和定位有问题的对象,而不是对其进行建模。建模和识别任务之间不再有区别。相反,这是一个我们想要“学习”或“估计”哪些场景参数的问题
结论
因此,在人工智能问题解决范式下,3D 渲染不仅是关于产生 3D 虚拟世界的照片级逼真图像,而且是为了从真实世界构建虚拟世界。此外,新框架在以下方面重新定义了 3D 和 AI:
- 3D 渲染成为人工智能训练循环中的一个基本操作
- 训练,或“梯度下降”,过去只发生在云中训练神经网络,现在是推理的一部分。
- 照片写实主义和保持真实世界和虚拟世界的一致性一样重要。
数字双胞胎将要求把巨大的和不断变化的真实世界带给它的未开发的双胞胎,并不断保持双胞胎之间的一致性。通过神经渲染获得的虚拟对象需要与经典构建的对象共存。因此,我相信神经渲染和传统渲染将在 GPU 上融合,利用其成熟和高性能的 3D 管道。数字双胞胎的需求将落在未来 GPU 的肩上。为了参与 AI 训练循环的梯度计算,需要在 GPU 端完成工作以变得“可微分”。
假设 GPU 变得天生可微分和张量加速,以响应 3D 中人工智能的进步,我预见 GPU 的双重人格将成为一个。
然后,GPU 保持了其作为首选架构的地位,以促进人工智能中的进一步软件进步,并最终开启了计算机架构的新黄金时代。
你会让自动驾驶汽车做出道德决定吗?
重要的是要思考为什么无人驾驶汽车如此难以实现

来源: pixabay
无论你对自动驾驶汽车(AV)有什么感觉,世界上最大的公司似乎都无法投入足够的资金来实现它们。被认为处于领先地位的 Waymo 公司已经花费了惊人的~ 35 亿美元!这是将极大改变我们日常生活方式的事情之一。
随着 AVs 的出现,我们希望看到交通流量效率的提高和更低的碳排放。想想基本上有很多拼车,它们不会在路上暴怒,以非常可预测的方式行动,并能在突发情况下做出瞬间决定。这对鹿来说可能更好🦌宾夕法尼亚州的人口也是如此。
对于像优步这样的公司来说,这意味着更可预测的司机供应,而对于司机来说,这可能意味着大规模的失业和再培训需求。但是,在我们超越自我,开始担心我们的工作,或者为路上少了一些人而庆祝之前,思考一下为什么无人驾驶汽车如此难以实现是至关重要的。
进退两难
几十年前,即使是今天的智能手机也会被认为是“不可能的”。回顾技术的巨大飞跃,我们有时会忘记引领我们走到今天的渐进式变革。因此,唯一合理的假设是汽车也将演变成 AVs,甚至可能是我们甚至不认为是汽车的东西。
问题并不在于制造无人驾驶汽车的技术方面。那就是我们大多数人现在生活的这个世界,并不是用来对付他们的。我们设计这个世界的思想是让人们来做决定,而不是机器。
快速决策意味着什么
让我们假设我们有一辆汽车,一辆自动驾驶的汽车,或者一辆安全的汽车。现在 Sav 基本上能做你作为司机能做的事,但它能做到精准,不累,用心。基本上 Sav 是最好的驱动。它没有任何瑕疵,它做的每一件事,采取的每一个行动都没有随机性。
一天,你在 Sav 里开车,突然,刹车失灵了。 Sav ,虽然是个完美的驾驶员,但也不是神仙,只能反应。但你应该庆幸不是你在开车,因为作为人类,我们往往会很恐慌。有压力的情况不是快速做出决定的最佳场所,尤其是那些涉及我们生活的情况。
啊哦,你看到前面有一条人行横道,上面有一些人。 Sav 也看到了这一点,并且* 哔哔 *之后,它意识到它只有两种可能的结果。要么转向一边,避免撞到行人,但对你造成致命伤害,要么直行,对随机出现的人造成致命伤害,但救了你的命。
谁应该优先选择生命的道德困境?如果它真的做出了选择,我们作为社会将如何应对?
为什么这么重要?
如果你或我在这种情况下,我们会怎么做?很有可能我们甚至无法想到所有可能的结果或确定幸存的概率。我们很可能会等太久才做出决定,然后最终做出一个不合格的决定,很可能伤害到所有相关人员。
那么,如果 Sav 做了更优化的事情,为什么会有问题呢?首先,最优是非常主观的。在这种情况下,每个人的最佳选择至少是他们自己的生存。问题不在于我们做了一个糟糕的决定,而在于我们决定背后的意图和推理。
不可否认的事实是,作为人类,我们不擅长做决定。幸运的是,Sav 没有得到同样的好处。当它做出决定时,我们确切地知道它为什么做出决定。不存在随机性。如果它选择拯救一个生命而不是另一个,我们知道它做了一个计算,在那里它赋予人的生命价值。
你希望这些车出现在你的街道上吗?你坐在车里舒服吗?如果 Sav 的公司要求你签署一份免责声明,免除他们的任何责任,你会这样做吗?或者你想预先选择 Sav 应该如何做出这些决定?
把道德还给人民?
好的,我们知道 Sav 只能根据它的算法来决定生死。我们可以让 Sav 在只有一两个选择的情况下随机行动。但我真的不认为我们作为社会会真的对一些被建造得更加准确和决定性的东西感到高兴,突然选择让生活在一个它完全控制的地方由随机机会决定。
回车,https://www.moralmachine.net/。

你会选择什么? 来源 刻字机
MoralMachine 是麻省理工学院媒体实验室建立的一个平台,用来收集关于我们如何做出艰难决定的观点。这个想法是在我们处于能够正确思考的精神状态时问我们这些问题,就像计算机不会受到周围发生的事情的情绪影响一样。
该平台为每个人提供了一组场景,一个接一个,询问汽车应该采取什么行动。无论是两组行人,还是乘客对行人,每一个动作都会导致某种形式的死亡。
它变得更加有趣(或令人震惊)的地方是我们被测试我们会赋予什么更多价值的地方。我们是否经常选择去拯救那些更年轻的人?我们区分动物和人类的生命吗?有人乱穿马路有关系吗?
由此产生的问题是,政策制定者和汽车制造商是否应该合作使用这些数据,民主地选择 Sav 的行为方式。在一个民主国家,我们投票给我们的政治领袖,以及他们所代表的价值观。即使这些价值观并不适合所有人,也没关系。如果大多数人希望以某种方式实现,他们就赢了。
“但是,当连伦理学家都不能就基本真理——或其近似值——达成一致时,社会怎么能达成一致呢”
“道德机器”的阴暗面和自动驾驶汽车计算伦理决策的谬误
这就是为什么让道德机器用于任何形式的政策制定都不是一个好主意。当然,我们可以让每个人在人口统计学上做出选择,但这总是会让一些人比其他人更不安全。根据该平台目前的结果,年轻人口可能从中受益。人们基本上不再走在马路旁边。因为总会有比你年轻的人出现在 AV 中,对他们来说你的生命就没那么重要了。

我们看重什么?来源: MoralMachine
同样,人们必须首先权衡进入汽车的风险。这样做,他们冒着让一个更年轻的行人来到车前的风险,触发 Sav 的生命价值分配算法,让你知道你的生命价值降低了。
这只是我在这里关注的一个特征,想象一下其他少数民族会是什么样子?我们将开始需要一个早晨提醒,提醒我们开车或走路的风险,这取决于我们周围当前的社会人口统计。这太荒谬了。
自驾只是白日梦吗?
人工智能中的伦理和道德已成为将越来越多的人工智能融入我们生活的最大挑战之一。公司有盈利的倾向,个人有不同的价值观,他们希望保持真实。任何可用的技术都会有一定的偏见,无论是基于工程师还是组织。
对于自动驾驶汽车,我们需要改变设置和评估其行为的框架。
1.重新设计结构
当我们想到自动驾驶时,我们想象的是世界的样子,区别在于自动驾驶汽车。相反,也许解决方案还在于更新世界,或者至少交通部门,需要看起来有点不同。人行横道、道路、交通信号灯、停车标志,所有这些都是基于这样一种假设,即人类需要相互协作来维护安全。无人驾驶汽车不一定需要同样的基础设施。类似于地铁系统如何与道路并行,也许我们也需要重新设计城市,以不同于我们目前的方式融入行人。
2.重塑我们的思维方式
值得注意的是,尽管道德机器让我们相信 AV 必须基于道德做出决定,但试图将道德和伦理纳入机器并没有真正的好处。因为我们作为人类还不能弄清楚什么是道德上正确的,我们没有办法实际上把它结合到机器中去执行。
问题不在于一辆汽车是否应该为了救五个人而杀死一个人,而是这项技术的引入将如何塑造和改变周围所有人的权利、生活和利益。
这里的论点是,AI 的行为使用概率来确定要做什么。它将基于事件的不同概率,以及采取这些行动的结果的价值,试图最大化总收益。我们不应该也不能将不同人口统计或人口的生活价值观模型化到它的决策中。
如果你看过任何机器人试图保护人类免受其害的科幻电影,如“瓦力”,给 AI 分配道德任务通常是事情开始出错的地方。
因此,与其关注 Sav 在这些一次性情况下会做什么,我们需要了解 Sav 实际上是如何做出决策的,并从中消除尽可能多的偏见。思考《道德机器》中提出的道德问题会分散注意力。
资源
- 人们想要有功利主义道德观的无人驾驶汽车,除非他们是乘客
- 无人驾驶汽车的困境揭示了道德选择并不普遍
- 无轨电车的愚蠢:伦理挑战和自动驾驶汽车
- “道德机器”的阴暗面和自动驾驶汽车计算伦理决策的谬误
原载于 2021 年 9 月 28 日 https://wordsbywaseh.comhttps://wordsbywaseh.com/will-you-let-self-driving-cars-make-moral-decisions/。****
你会从 PyCharm 转向 JetBrains 的最新数据科学 IDE DataSpell 吗?
回顾 DataSpell IDE 的主要功能

在常见的 Python IDEs 中,PyCharm 是我的最爱,原因有几个:1)。PyCharm 给了我更连贯的用户体验,因为我以前经常使用 AndroidStudio2).出色的自动完成智能,可提高生产率;3).版本控制工具的原生集成(例如 GitHub);4).虚拟环境易于管理;5)重构和调试是没有痛苦的。
虽然不如其他大型科技企业知名,但 JetBrains 是一家高度创新的公司,它支持广受好评的 Python IDE——py charm,以及其他专业开发的几个行业领先的 IDE,如 WebStorm for web development。
作为一名使用 Python 进行数据处理和分析的科学家,我是 PyCharm 的忠实用户,py charm 支持数据科学家寻找的所有基本特性。我最近得知 JetBrains 发布了一个专门针对数据科学项目的 IDE—DataSpell。当然,我试了一下。下面是 DataSpell 必须提供的关键特性的快速总结。
在你决定继续阅读之前,有一点很重要,DataSpell 不会像其他 JetBrains IDEs 一样是免费产品。然而,我猜想他们可能会提供一个教育或社区版本,可以对一些人免费。现在,JetBrains 正在为 DataSpell 运行一个早期访问程序,如果你愿意,你可以下载进行评估。
整体更好的笔记本电脑体验
虽然 PyCharm 支持 Jupyter 笔记本,但我很少在 PyCharm 中编辑或运行笔记本,因为它肯定不会像其他笔记本编辑器一样提供最佳的笔记本体验,例如 JupyterLab,甚至是在 Python 扩展中正式集成了笔记本支持的 VS Code。PyCharm 的一个特别的缺点是它的接口将代码单元从输出中分离出来,提供了一种支离破碎的体验。
DataSpell 通过提供一个类似于 JupyterLab 的接口解决了这个问题。本质上,输出就在电池的正下方——非常简单。此外,常见的快捷键与 JupyterLab 或 VS 代码对单元格的操作相同,如 a 在当前单元格之前添加一个单元格,b 在当前单元格之后添加一个单元格,shift + enter 运行当前单元格并添加一个新单元格。
与数据框的良好互动
当您将数据帧显示为输出时,DataSpell 可以显示整个数据帧,而不会在数据帧很大时屏蔽一些数据,这与其他笔记本编辑器不同。当数据帧很大时,DataSpell 会自动添加一个滚动条,允许您滚动查看整个数据集。
同时,您可以通过单击列名轻松地对数据进行排序,这将使用该列按升序或降序对数据帧进行排序(如果您单击它两次)。
如果希望在单个框架中查看更多数据,可以选择在单独的选项卡中打开数据框架。下图向您展示了这些功能。

数据帧交互性
智能编码辅助
正如我前面提到的,我非常欣赏 PyCharm 对编码智能的支持,比如有效的自动完成建议。DataSpell 完全继承了这个特性。当您在单元格中编写代码时,您可以获得与在 PyCharm 中编写 Python 脚本文件相同的待遇。
我知道 JupyterLab 通过第三方插件提供了相同的编码支持,但并不总是有效,而且很慢(当它有效时)。从动画图像中可以看到,DataSpell 几乎是立即快速提示可能的候选对象。您的编码生产率可以显著提高。

编码帮助
版本控制集成
有专业软件开发团队工作经验的人应该熟悉版本控制系统。然而,对于一些独立工作的数据科学家来说,这不一定是真的。如果你还没有开始使用版本控制工具,你必须查找它们。其中,最常见的是 GitHub。要开始创建主分支,您只需点击 GitHub 上的 VCS ->共享项目,系统会提示您输入您的帐户。

GitHub 上的共享项目
虚拟环境管理
当您使用 Python 一段时间后,您应该已经意识到为您的每个项目设置虚拟环境的重要性。如果您选择编写代码来管理这些环境,那就不太容易了。DataSpell 通过提供一个设置页面,让我们可以轻松地为任何数据科学项目配置虚拟环境,如下所示。值得注意的是,它内置了对 Conda 环境的支持,这是许多数据科学家做出的选择,因此大多数人都不会感到陌生。

使用现有环境
数据库连接
没有数据,数据科学家无法开展工作。一个基本的数据源是各种数据库。随着技术的发展,出现了许多不同种类的数据库,如 SQLite、Microsoft SQL 数据库、Oracle 和 PostgreSQL。所有这些主要类型都由 DataSpell 通过点击和连接步骤来支持。

数据库连接
终端、Python 控制台等等
你并不总是在 Jupyter 笔记本或 Python 脚本上工作。有时,您可能会发现打开终端或 Python 控制台来执行一些其他工作非常方便。例如,当我使用 Streamlit 构建一个网站时,我需要使用终端来启动网站进行测试。DataSpell 通过提供终端的内置支持,让我们变得很容易。
同样重要的是可以方便地访问 Python 控制台。这在您编写 Python 脚本时非常有用,您可以在控制台中请求某些代码行,这样您就可以快速评估性能,而无需运行整个脚本。与此功能相关的是,DataSpell 和 PyCharm 一样支持科学模式,在这种模式下,您可以在脚本中启用“类似细胞”的功能,一次运行一个细胞。
在您找到终端和 Python 控制台的地方,您会注意到还有许多其他方便的工具,包括 Python 包和问题,您可以在其中查看当前项目中的潜在问题。

底部工具
结论
我对 DataSpell 的总体印象是积极的,因为它从 PyCharm 中提取了数据科学项目所需的所有关键功能。换句话说,如果您使用 PyCharm professional 版本,您就拥有了 DataSpell 提供的几乎所有东西——只有一个例外。DataSpell 让笔记本体验好了很多。
虽然这里没有涉及,但同样重要的是 DataSpell 也支持 R,JetBrains 团队正在改进他们对 R 语言的支持,对其他数据科学相关语言的支持,如 Julia,也在计划中。
基于以上种种原因,我一定会在 DataSpell 正式发布时尝试一下。我的数据科学家朋友们,你们呢?
威廉姆斯 F1:车队回到正轨了吗?

探索性数据分析
一小群车队对赛车迷的集体想象有着巨大的影响。
当我们想到驱动器、电路,甚至几十年时,某些图像会跃入脑海。90 年代威廉姆斯赛车的霸主地位,加上罗斯曼的车身和雷诺的引擎,就属于这一类。
记忆的直接性不可避免地会消失。如果库存得不到补充,兴奋就会让位于略带褐色的怀旧情绪。
威廉姆斯车队是赢得 1979 年至 2008 年间所有车队冠军和 1984 年至 2008 年所有车手冠军的五支车队之一。它在这项运动遗产中的地位是毋庸置疑的;正如我们所知,车队的辉煌岁月塑造了 f1 的轮廓。
该团队最近的困境与这些令人振奋的成就形成了鲜明的对比。威廉姆斯上一次夺冠是在 2012 年。之前的胜利是在 2004 年。
谷歌在回复关于“威廉姆斯赛车”的查询时给出了这些无情的建议:

这个由弗兰克·威廉姆斯爵士于 1977 年创建的团队现在由多利顿资本管理。最近的收购提供了所需的资金和战略方向的改变,导致许多人问威廉姆斯是否回到正轨。
我看了看他们远近闻名的过去,看看数据是否表明他们早该回到领先的行列。
威廉姆斯赛车:过去
威廉姆斯一共获得了 9 个车队冠军,仅次于法拉利。

图片:克拉克·博伊德;数据:卡格尔
威廉姆斯显然是这里精英俱乐部的一员,与法拉利和迈凯轮一样——但前者保持着健康的领先优势。
自 1950 年 F1 成立以来,法拉利是唯一一支在每届锦标赛中都有赛车参赛的车队,尽管他们因与组织者发生争执而错过了第一场比赛。
自 1977 年以来,威廉姆斯驾驶赛车参加了 754 场比赛,引人注目的是,他们以第一名的成绩结束比赛的次数比其他任何位置都多。威廉姆斯有 114 场比赛的胜利,相当于 15%的比赛胜率。

加上 128 个杆位,车队的血统变得更加清晰。
下面的图表显示了历年来每支车队赢得的累计冠军数,包括车手和车队冠军。

图片:克拉克·博伊德;数据:卡格尔
从这张图表中,我们可以看到 F1 过去的一个简化故事。
法拉利在 20 世纪 50 年代赢得了多个车手冠军,但由于其不存在,在那些年里没有赢得车队冠军。车队冠军是在 1958 年才出现的。这有助于解释它在这里的 22 个标题的扩展计数。
威廉姆斯在 20 世纪 90 年代有一个非常多产的时期,用雷诺提供的引擎在六个赛季的时间里获得了五个车队冠军。
2013 年后,我们可以看到主宰 v6 turbo 时代的梅赛德斯车队令人印象深刻的崛起。自 2010 年以来,红牛和梅赛德斯赢得了所有车队的冠军。
2009 年,布朗赢得了冠军,然后梅赛德斯买下了他们,以启动他们的 F1 入口。
我们也可以注意到所有球队都经历的休整期。法拉利在 1961 年至 1974 年、1979 年至 2000 年和 2007 年至 2020 年期间表现不佳。老实说,他们很快就会把 2021 年加到这个名单上。
毫无疑问,威廉姆斯现在正经历着这样一个时期。下图显示了本世纪车队在车队积分榜上的位置频率。

第三是最常见的位置(4 次),最后一次获得第三名是在 2015 年:

这只是故事的一小部分。两支车队(梅塞德斯和红牛)在这个领域遥遥领先,第三名可能并不意味着第三名的车队就在咫尺之遥。
下面的图表显示了每年领先的车队与威廉姆斯车队之间的差距:

2015 年,威廉姆斯以 257 分获得第三名。奔驰以 703 分夺冠。
威廉姆斯因此获得了梅赛德斯 36.5%的积分,从领先者到威廉姆斯之间产生了 63.5%的差距。
2003 年,威廉姆斯以 142 分获得第二名,紧随法拉利 158 分之后。很重要的一点是,今天一场胜利所获得的分数明显不同(当时一场胜利只有 10 分),但百分比赤字的计算仍然描绘了一幅有代表性的画面。
我们可以看看威廉姆斯每个赛季如何对抗领先的球队,以获得对球队停滞不前的进步的不同看法:

这些图表显示了每个赛季的积分累积情况。梅赛德斯以可靠、快速的速度获得积分,而威廉姆斯则在最近停滞不前。
正如詹姆斯·特洛曼的一些优秀作品中所强调的,这种模式在本季再次上演:

Trotman 也展示了一些来自各个种族的有启发性的图表。例如,我们可以在这里看到领先的车队如何脱离其他车队,并在 2020 年法国大奖赛上扩大其领先优势:

当然,只有戴上金融的帽子,你才能真正理解 F1。
最近几个赛季,威廉姆斯车队的预算是发车区最小的,这与它在方格旗的表现密切相关。

威廉姆斯怎么了?那发生了。
新的 1.45 亿美元的年度预算上限激励了较小的团队留在这项运动中,最终应该会导致更激烈的竞争。
不可否认的是,一些项目不受这一上限的限制,包括营销、司机工资和大约 20 个其他项目。
威廉姆斯:进步的迹象
随着 F1 即将发生翻天覆地的变化(更多内容见下文),威廉姆斯将渴望在本赛季展示进步的迹象。
到目前为止,这种进步在排位赛中已经很明显了——特别是乔治·拉塞尔:

事实上,拉塞尔是今年迄今为止唯一一位在排位赛中击败队友(加拿大人尼古拉斯·拉蒂菲)的车手。
然而,当灯光熄灭时,威廉姆斯面临的挑战也随之增加。拉塞尔仅在第一圈就平均失去了 1.44 个位置,这或许表明他正在为一个出色的排位赛圈竭尽全力。
到目前为止,拉塞尔在今年的第一圈比赛中总共失去了 13 个位置。

尽管如此,威廉姆斯车队确实在英国大奖赛上完成了最快的进站,所以他们在那里补上了一点时间。
下面,我画出了四位车手(汉密尔顿、维斯塔彭、拉塞尔、拉蒂菲)本赛季在每个位置上的总圈数:

做这样的比较似乎很残酷,但我被 Verstappen 对今年比赛的指挥所打动。他本赛季领先了总圈数的 70%,如果不是因为第一圈与汉密尔顿的事故,他肯定会在英国大奖赛上增加总圈数。
再往下,我们看到罗素经常胜过他的队友拉蒂菲。据传闻,26 岁的拉蒂菲每年可获得 2000 万至 4000 万美元的赞助费。他的父亲是加拿大第三大食品公司 Sofina 的创始人。他不是一个糟糕的司机,但罗素显然是卓越的人才。

如果拉塞尔继续他最近的状态,他只会吸引梅赛德斯车队更多的关注。
威廉姆斯车队与其引擎供应商(梅赛德斯)建立了更紧密的联系,关于拉塞尔将成为汉密尔顿车队二号车手的传言也越来越多。
然而拉塞尔的状态对威廉姆斯来说可能是双赢的。
他们的头号车手可能会离开,但他会在这个过程中展示赛车的能力。威廉姆斯对明年有着雄心勃勃的计划,这将由本赛季剩下的比赛中更好的表现来推动。如果拉塞尔获得了梅塞德斯的驾驶权,那么从逻辑上来说,瓦尔特利·博塔斯(前威廉姆斯车手)将会接受报价。
2022 赛季
2022 赛季将会看到 F1 赛车的大量变化,许多人认为这是这项运动历史上最大的改革。正如我们在上面看到的,有一小群资金极其充足的车队主导着今天的 F1。一旦他们到了前面,超车是有限的。
这限制了这项运动对更广泛观众的吸引力,无论是观众还是赞助商。
预算上限将迫使团队创造性地思考,但 2022 年也带来了新的技术规则,试图改善这一奇观。
正如 f1 首席技术官帕特·西蒙兹最近所说:
“‘尾流’是超车问题的关键。当高速行驶时,F1 赛车会产生湍流空气,并向外扩散,以免干扰尾翼。由此产生的尾流会显著降低后车的下压力(将汽车“粘”在路面上),并导致重要系统过热,从而影响后车。因此,很难靠得足够近来尝试超车,甚至在弯道中跟随另一辆车。”
f1 还聘请了一名首席设计师来帮助提升赛车的美学吸引力。F1 网站上有一篇关于新车的精彩报道。
我们从 F1 赛车的历史开始,这表明规则的重大变革会动摇秩序。当新规则开始生效时,各队尽可能地争取优势。布朗在 2009 年做到了这一点,梅赛德斯从 2014 年开始在新时代掌握了主动权。
如果我们看看 2014 年的锦标赛,我们可以看到每支球队的表现与他们对上一年锦标赛的预测表现的对比。
线以上的人表现超出预期:

这并不是说奔驰这次会重复这一壮举;更确切地说,一家制造商可能会比其他制造商更早地破解代码。
威廉姆斯可能不再位于发车区的前面,但它可以在 2022 年利用梅赛德斯赛车后面改进的滑流。
如果它能在 2021 年继续取得积极的成绩——不仅在排位赛中,而且在比赛中——2022 年可能是威廉姆斯复兴的真正开始。
用大台库赢得你的同事
使用 DSS 8.0 Apps-as-recipe 为整个企业创造价值。

由 Unsplash 上的 krakenimages 拍摄的照片
对于希望保持精干并完成大量工作数据科学家和分析师团队来说,Dataiku 是一个非常棒的工具。对于像我这样的数据科学家来说,它允许我花更多的时间做我喜欢的事情,并更快地为企业创造价值。轻松访问数据、轻松实现项目自动化、轻松进行质量保证监控、快速开发……一个梦想。本文并不是对该工具的介绍,但是如果您认为值得一读,我鼓励您留下评论。现在,让我们提醒大家,它是一个数据科学平台,在项目中组织工作,其管道被称为流,用户可以通过 GUI 和通过 DSS api 以编程方式获得。你可以在下面看到一个流程,由称为配方(圆形)的计算片段和称为数据集(方形)的数据片段组成,所有这些都组织在区域中。

项目的典型流程,由作者提供的图像
尽管有上述所有的赞扬和管道的易读性,使用该产品的挫折感会随着时间的推移而增长。您的工作的影响经常受到向其他人提供工作的困难的限制,特别是那些无法访问平台的人,如客户、C-level 等……对于那些可以访问平台的人,即您的数据科学家和分析师同事,有太多的项目内部工作暴露在流程中,有太多的细节需要导航。 决定需要运行什么来得到想要的东西,等等。对他们来说,大部分时间这样的麻烦是不值得的,否则他们将结束一个项目纠缠的节日。 我们离使用数据和模型的简单干净的体验还很远。甚至不要让我开始讨论关注点分离设计原则。如果你的公司希望让这个平台在规模上可行,它将需要放置相当数量的“最佳实践”文档。
我必须赞扬他们站在这些问题的前列,但仍有许多地方需要改进。解决这个问题的能力之一是标签。你可能同意一套标签,如果你的同事和你自己坚持使用它们,你可以很快看到流程中什么是什么,什么是要消费的。我坚持把 if 的成分放在那里。提供的另一个功能是他们所谓的插件。插件可以让你打包一个项目,并让它作为秘方在平台范围内可用。不幸的是,当设计和可用性已经足够让你担心的时候,这仍然需要在插件工程上的扩展投资。这意味着你必须为它写规格,把它交给一个工程师,测试他们的解决方案,然后与他们协调来修复/更新它。就生产率而言不理想。
插件是有用的,但是部署起来昂贵/耗时。进入 Dataiku 8.0 和“应用程序设计器”。问题解决了。
有了这个新功能,您可以构建一个管道,添加一个自动化场景,只需点击几下鼠标,就可以让每个人都可以使用它。真是个梦!如果它有吸引力,你可以把它交给一个工程师,他会有简单的解决方案把它变成一个真正的插件!不幸的是,就像 Dataiku 经常出现的情况一样,文档非常糟糕,无法帮助您超越最基本的用法。在这篇文章的剩余部分,我将尝试提供一些内容来加速你对作为菜谱的应用程序的学习。
允许用户定义的参数
您构建了一个食谱应用程序,该应用程序拍摄上个月穿着 ShoeDirect 的下半部分顾客的照片,并生成一个细分市场、每个细分市场的推荐列表以及他们的潜在 LTV,商店的销售人员将在本月使用该应用程序。这真是太棒了。这里来了一大群快乐的顾客和提高的底线。但是从你的成功来看,经理们现在要求你根据销售人员的销售风格使推荐人的输出有所不同。您需要在应用程序中制作一个下拉菜单,让用户选择销售风格。
你求助于文档/教程或论坛,但是你运气不好:它没有被提及。你只能猜测。
我将向你介绍我对这个问题的了解。
第一步//设计你的流程
这些是基本的。拿一张纸,记下应用程序需要的所有数据输入和它将产生的输出。然后记下用户需要定义的参数。使这些项目变量(不是必需的,但很有用)。

将您的应用程序变量定义为项目变量,图片由作者提供
在运行时,它们将被更改为用户定义的值。
第二步//建立你的流程
为所有输入和输出创建虚拟数据集,并填充它们之间的内容,就像您对任何项目所做的那样。
第三步//制作一个场景
然后,recipe 将在选择的输入上运行给定的场景,因此您需要准确定义您希望您的应用程序做什么,就像您在生产项目中定义场景一样。
步骤 4 //创建您的应用程序
现在是发光的时候了。进入应用程序设计器,选择应用程序作为配方,进入设置菜单。

作者图片

作者图片

作者图片
你的申请正在路上。在运行时,它不会在这个项目中运行,但是项目的流程将被打包,同时打包的还有您需要的元素的副本以及在使用配方时要运行的场景。由于复制元素需要时间和磁盘空间,您需要指定运行应用程序绝对需要的内容,其余的内容会随着应用程序的运行而构建。这是在“包含的内容”一节中完成的,但是文档会让你对它真正的含义/作用感到枯燥。为了安全起见,我包括了“输入数据集”,尽管我认为在大多数情况下这是不必要的。

包含的内容选项,按作者分类的图像
步骤 5 //从 GUI 中检索变量
您的应用程序有一个默认的 GUI 设置,它由以下部分组成:
- 文本框
- 整数选择器
- 下拉菜单

默认设置,图片由作者提供
这是大台库经常失败的地方。它把你带到了伟大的边缘,却把你抛弃在那里,更加困惑,被赋予了更差的文档和更差的 API 选择。
我很高兴地告诉你,解决方案非常简单,不需要任何工作。这些变量被自动添加到应用程序实例流的定制变量中,其名称对应于定义其 UI 元素的 JSON 的“name”字段。如果我们要重命名这些第一变量、第二变量和第三变量,我们在步骤 1 中设置的值将被简单地覆盖。因此,它们可以在任何地方使用,使用通常的 ${} 或data iku . get _ custom _ variables(typed = True)方法,如这里的所示。

让它发挥作用需要知道的事情
是时候缓和你的激动情绪了。作为食谱的应用程序仍然有很多陷阱。我将回顾一下到目前为止我遇到的问题。
失败将没有有用的错误日志。
相反,用户将获得:
java.lang.Exception:场景失败
仅此而已。在 Dataiku UI 中,没有简单的方法来诊断问题出在哪里。既不在使用该应用程序的项目中,也不在其源项目中。在应用程序实例运行并失败后,它会连同相应的日志一起被删除,至少就平台内使用而言是这样(我不知道 DSS 的管理员是否能以某种方式检索它们)。
解决方法是让用户在保持实例复选框打开的情况下重新运行配方。这将防止实例被删除,并且您将能够在那里进行您想要的所有故障排除。

作者图片
但是要小心!不取消选中此框可能会导致大量“单一用途”项目和数据集在您的 Dataiku 框和基础架构中累积。这些实例可以在一个特殊的文件夹中找到,也可以使用 Dataiku 自动赋予它们的特殊标签找到。

按作者查找实例、图像的方法
调试后删除这些项目时必须小心。默认情况下,确保从数据库/存储中删除该项目中使用的所有数据的复选框是未选中的。这意味着,如果您的管理员没有建立定期运行的适当清理流程,您将会在这些数据库/存储中填充 ghost 数据。

检查那个盒子!,作者图片
确保每次都检查它。
不允许版本控制
当您“部署”了一个应用程序时,您对其设置所做的任何更改都会影响到使用它的所有位置。用户没有选择,看不到更新,也无法跟踪您做出更改之前的情况。这当然是应用程序内部工作所需要的,但也适用于菜谱的字段、输入和输出!这可能会导致一些不好的意外。
权限处理不当
对于运行你的应用程序的人来说,他们需要被授予对定义它的项目的写访问权限。这意味着没有办法保护它不被用户篡改。这是非常糟糕的设计,需要行为良好的用户,随着公司的发展,这成了一个负担。这又会导致一些不好的意外。
SQL 依赖项
当使用 SQL 配方而不是可视化配方时,请确保使用链接到输入表的变量,而不是数据库中的表名。出于某种奇怪的原因,食谱不会因为原始数据集没有被设置为实例食谱的输入而失败,而是在应用程序的源项目的表上运行。

变量示例,按作者分类的图像
此外,如果在输入表上使用,替换实例中的输入表将会导致此操作失败。在使用自定义 SQL 之前,您必须将其与副本同步。python 食谱也是如此。

作者图片
等待未来
遵循 Dataiku 的设计理念,数据的来源将尽可能与用户无关,i/o 问题尽可能在技术上由平台静默管理。虽然在大多数情况下,它确实为用户提供了很好的体验,但在这里它也会带来挫败感。让我解释一下。
当在应用程序设计中选择数据集作为输入或返回输出数据集时,将复制数据。根据数据库/存储类型之间的关系,这种复制可能需要一段时间(我们公司的原生 Postgres snowflake 需要几个小时)。在此期间,用户只能看到以下消息:
[INFO] [dku.flow.app] —再等一会儿,以便将来完成实例化
这又是一个无用的日志/消息。
输入/输出混乱
对于每个输入/输出表,用户可以定义无限多个表。我不确定 Dataiku 是如何处理的,但是应该允许每个输入/输出只有一个表,就像可视化菜谱中所做的那样。

应该只有一个数据集替换产品输入,按作者排序的图像
此外,据我所知,如果输入不匹配或者至少包含所需的模式,也没有办法确保用户不能运行菜谱。这将非常有助于防止进一步令人沮丧的失败。
结论
有了上面提供的细节,我希望你能在让数据为你的企业中更多的人服务方面取得长足的进步,并满足你同事的需求。我们都知道,从数据驱动型企业到数据能力型企业还有很长的路要走,而像这样的工具在这条路上至关重要。
如果你相信自助式数据解决方案的力量,并且相信好的设计将对 ML 和数据改善世界大有帮助,我期待你的评论!
风能分析工具箱:迭代功率曲线滤波器
行业笔记
一个开源模块,用于过滤运行风力涡轮机的 SCADA 数据。

介绍
在本文中,我将介绍为分析运行中的风力涡轮机的数据而开发的一系列模块中的第一个——迭代功率曲线滤波器。
理论上,风力涡轮机的功率输出(WT)与风速的立方成正比。这种关系的曲线被称为功率曲线,这可能是风能分析中最重要的曲线。
原始设备制造商(OEM)提供理论功率曲线,该曲线将输入风速映射到给定理想条件下的输出功率。然而,这种关系在运行的涡轮机中很少出现,原因有很多,例如风电场地形、风速计的位置、效率问题以及由于与其他涡轮机的接近而产生的尾流效应。
因此,理解运行涡轮机的风速和功率之间的实际关系是令人感兴趣的,这被正确地命名为运行功率曲线,它可以与理论功率曲线完全不同。
运行功率曲线给出了在正常运行条件下给定涡轮机的风速和功率输出之间的关系。正常运行条件通常被定义为无停机、无故障和无事件数据点。
运行功率曲线是通过清理数百个传感器从风力发电机收集的时间序列数据创建的。记录数据的典型频率是 0.0017s^-1,即每 10 分钟一次。
WT 传感器通过阈值编程,在检测到异常运行条件时发出警报。这就是所谓的监控和数据采集(SCADA)系统。
然而,基于触发的警报简单地过滤 SCADA 数据通常不足以获得对应于正常操作条件的数据点。因此,需要进行统计过滤。
SCADA 数据过滤是许多风能分析项目的基本组成部分,包括涡轮机性能不佳分析、机组并排比较、异常检测以及使用机器学习技术的数据驱动自动化过程。
因此,开发用于预处理 SCADA 数据的现成模块将使工程师和分析师受益,显著减少通常用于数据清理的时间和精力,同时降低行业内利用高级分析的准入门槛。
过滤程序

功率曲线滤波程序
如果每个机组都有一个唯一的标识符,迭代功率曲线滤波器可以处理跨几个风电场的多个涡轮机。该程序包括以下两个主要步骤:
初级过滤
- 停机时间数据点被删除。
- 排除了可能的故障。这一步是经验性的,目的是去除中到高风速下不合理的生产数据。
二次过滤
- 计算来自初级过滤过程的部分过滤的功率曲线的统计参数(平均值和标准偏差)。
- 排除用户指定的+/- x 标准之外的数据点。
- 在用户选择的几个周期内重复上述两个步骤。
Abdul Mouez Khatab 的硕士论文“运行风力涡轮机的性能分析”于 2017 年描述了相关程序。
模块使用
托管在 PyPi 上的 scada 数据分析库包含过滤模块,代码实现的细节可以在 GitHub 上找到。可以通过简单的 pip 命令安装该库,如下所示。
# Pip install library
pip install scada-data-analysis
此外,GitHub repo 项目可以克隆如下:
# Clone github repo
git clone https://github.com/abbey2017/wind-energy-analytics.git
使用该库,您可以通过如下所示的 4 个步骤过滤杂乱的 SCADA 数据:
# Import relevant libraries
import pandas as pdfrom scada_data_analysis.modules.power_curve_preprocessing import PowerCurveFiltering
# Load turbine scada data
df = pd.read_csv('path\to\data')
# Instantiate power curve filtering class
pc_filter = PowerCurveFiltering(turbine_label='Wind_turbine_name', windspeed_label='Ws_avg', power_label='P_avg', data=df, cut_in_speed=3, bin_interval=0.5, z_coeff=2.5, filter_cycle=5, return_fig=True, image_path='..\images')
# Process raw scada data
normal_df, abnormal_df = pc_filter.process()
结果
这个项目的 GitHub repo 有关于功率曲线滤波器的示例数据集和 Jupyter 实验室笔记本用法。此处显示的样本结果基于由 Engie 运营的法国 La Haute Borne 风电场的公开 SCADA 数据。


对未来的展望
来自功率曲线滤波模块的结果可用作训练机器学习模型的基础事实,该模型可用于对 WT 性能或其他先进控制技术的近实时监控。
旨在使工程师和分析师能够利用高级分析的新模块将被添加到风能分析工具箱中。一些当前的想法包括用于生成模型的模块,该模型可以基于历史 SCADA 数据、故障设备检测和性能不佳分类模块来估计/预测来自 WTs 的预期功率。
我想听听你对这篇文章和风能分析工具包的反馈。欢迎模块建议,包括可能的应用。请给 windenergyanalytics@gmail.com 的项目维护者发邮件,或者在项目的 GitHub 页面上提出问题。
什么更有趣?你可以通过我下面的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。
https://aolaoye.medium.com/membership
SQL 中的窗口函数:聚集值
用窗口函数计算累计

来源:照片由 ChristopherPluta 从 Pixabay 拍摄
在 SQL 中使用表时,通常会希望聚合值,或者计算表中值的累计。
在本文中,我们将研究如何使用所谓的窗口函数来实现这一点。
此外,我们还将看到如何将 CASE 语句嵌套在窗口函数中,以进一步定制基于特定条件对数组执行的计算。
使用窗口函数求和平均
考虑以下假设的某一天商店中列出的服装项目表(由作者虚构的值)。
date | item | price | size
----------------+--------------------+---------+--------
2021-01-01 | Sky Blue Jeans | 79.99 | 31
2021-01-02 | Fire Red Jeans | 89.99 | 36
2021-01-03 | Sky Blue Shirt | 59.99 | 38
2021-01-04 | Grass Green Shirt | 69.99 | 34
2021-01-05 | Peach Purple Hat | 79.99 | 40
2021-01-06 | Sun Yellow Jeans | 109.99 | 42
2021-01-07 | Olive Green Hat | 89.99 | 37
现在,假设所有者希望在累积的基础上对每个值求和并求平均值,即创建一个新数组,显示前两个值的总和,然后是前三个值,依此类推。这同样适用于计算平均值。
可以使用窗口函数对这些值求和,如下所示(显示了前五行):
>>> SELECT date,price,
>>> SUM(price) OVER (ORDER BY date)
>>> AS total_price
>>> FROM table;date | price | total_price
---------------------+---------+------------
2021-01-01 | 79.99 | 79.99
2021-01-02 | 89.99 | 169.98
2021-01-03 | 59.99 | 229.97
2021-01-04 | 69.99 | 299.96
2020-01-05 | 79.99 | 379.95
(5 rows)
同理,也可以计算出平均累计价格。
>>> SELECT date,price,
>>> AVG(price) OVER (ORDER BY date)
>>> AS mean_price
>>> FROM table;date | price | mean_price
---------------------+---------+------------
2021-01-01 | 79.99 | 79.99
2021-01-02 | 89.99 | 84.99
2021-01-03 | 59.99 | 76.66
2021-01-04 | 69.99 | 74.99
2020-01-05 | 79.99 | 75.99
(5 rows)
将 CASE 语句与窗口函数相结合
CASE 语句的功能类似于 if-then 语句。如果满足条件,则返回一个特定值,否则,如果不满足条件,则返回另一个值。
让我们考虑这个例子。假设对于这个特定的服装店,商家必须为某些商品提供退款。这如何反映在累计总数中?
考虑这个扩展的表。
date | item | price | size | refund
----------------+--------------------+---------+--------+---------
2021-01-01 | Sky Blue Jeans | 79.99 | 31 | no
2021-01-02 | Fire Red Jeans | 89.99 | 36 | no
2021-01-03 | Sky Blue Shirt | 59.99 | 38 | no
2021-01-04 | Grass Green Shirt | 69.99 | 34 | yes
2021-01-05 | Peach Purple Hat | 79.99 | 40 | yes
2021-01-06 | Sun Yellow Jeans | 109.99 | 42 | no
2021-01-07 | Olive Green Hat | 89.99 | 37 | no
从上面我们可以看到,在这种情况下,商家在 1 月 4 日和 5 日提供退款。为了计算新的累积和,这些值需要从中减去——而不是加到总数中。
在这方面,CASE 语句嵌套在 window 函数中——如果退款变量包含是值,则使用一条指令使价格值为负。
>>> SELECT date,price,refund,SUM(CASE WHEN refund = 'yes' THEN -1*price ELSE price END) OVER (ORDER BY date) AS total_price FROM table;date | price | total_price | refund
---------------------+---------+--------------+------------
2021-01-01 | 79.99 | 79.99 | no
2021-01-02 | 89.99 | 169.98 | no
2021-01-03 | 59.99 | 229.97 | no
2021-01-04 | 69.99 | 159.98 | yes
2020-01-05 | 79.99 | 79.99 | yes
2020-01-06 | 79.99 | 189.98 | no
2020-01-07 | 79.99 | 279.97 | no(5 rows)
正如我们所看到的,一旦在 1 月 4 日和 5 日减去 69.99 和 79.99 的价格,就会计算出 279.97 的总价格,这是由 CASE 语句执行的,因为退款变量的 yes 条目会导致 CASE 语句指定一个负价格。
结论
在本文中,您已经看到:
- 使用窗口功能的目的
- 如何使用窗口函数获得累积值
- 将窗口函数与 CASE 语句结合起来,使窗口函数更加灵活
非常感谢阅读,任何问题或反馈都非常感谢!您还可以在这里找到原始文章以及有用的 SQL 实践的更多示例。
参考
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。
葡萄酒数据集:一个分类问题
介绍
葡萄酒数据集由 13 个不同的葡萄酒参数组成,如酒精和灰分含量,共测量了 178 个葡萄酒样品。这些葡萄酒生长在意大利的同一地区,但来自三个不同的栽培品种;因此有三种不同等级的酒。这里的目标是找到一个模型,该模型可以在给定 13 个测量参数的情况下预测葡萄酒的类别,并找出三个不同类别之间的主要差异。这是一个分类问题,这里我将描述四个模型,并评估每个模型的准确性。此外,我将使用主成分分析来确定和探索这三类之间的差异。
多项逻辑回归
由于葡萄酒有三个类别,我们必须使用多项式逻辑回归,而不是有两个类别时使用的逻辑回归。为了做到这一点,我使用了 nnet 包中的 multinom 函数。
> dim(wine)[1] 178 14> attach(wine)> test=sample(178,45)> library(nnet)> LogReg=multinom(class~.,data=wine[-test,])> summary(LogReg)> Pre=predict(LogReg,wine[test,])> table(Pre,wine[test,]$class)

表 1。多项式逻辑回归模型的混淆矩阵
从表 1 可以看出,在 45 次观察中有 5 次分类错误;因此,多项式逻辑回归模型的准确率为 89%。
通过执行以下命令,我们可以多次重复上述过程,以获得对多项逻辑回归模型性能的更准确估计:
> Accuracy=rep(0,50)> for (i in 1:50) {+ test=sample(178,45)+ LogReg=multinom(class~.,data=wine[-test,])+ Pre=predict(LogReg,wine[test,])+ Accuracy[i]=mean(Pre==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.944
线性判别分析
当我们有两个以上的类,并且当观察数量很小时,LDA 是有用的。当预测值的分布在每一类中都是正态分布时,LDA 也更稳定。
> library(MASS)> lda.fit=lda(class~.,data=wine[-test,])> lda.fit
最后一个命令将生成关于模型的更多细节,如表 2 所示。

表二。各类葡萄酒的 13 个预测指标的平均值
然后,我们根据测试数据评估模型的性能:
> lda.pred=predict(lda.fit,wine[test,])> table(lda.pred$class,wine[test,]$class)

表 3。LDA 模型的混淆矩阵
从表 3 中我们可以看出,LDA 在预测测试数据类别方面具有 100%的准确性。
我们还可以使用下面的命令通过 LDA 可视化训练数据的分类,结果如图 1 所示:
> plot(lda.fit)

图一。通过 LDA 对训练数据进行分类(图片由作者提供)
由于数据集中有三个类,所以只需要两个线性判别式来对每个观察值进行分类。图 1 显示了 LD1 和 LD2 空间上的训练数据的图以及每个数据点的相应类别。基于 LDA 模型的系数计算 LD1 和 LD2 值。
通过执行以下命令,我们可以多次重复上述过程,以获得对 LDA 模型性能的更准确估计:
> for (i in 1:50) {+ test=sample(178,45)+ lda.fit=lda(class~.,data=wine[-test,])+ lda.pred=predict(lda.fit,wine[test,])+ Accuracy[i]=mean(lda.pred$class==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9844444
二次判别分析(QDA)
另一个分类器是 QDA 模型,其语法与 r 中的 LDA 相似。我们可以多次运行该过程,以获得对 QDA 模型性能的更准确估计,如下所示:
> qda.fit=qda(class~.,data=wine[-test,])> qda.pred=predict(qda.fit,wine[test,])> table(qda.pred$class,wine[test,]$class)> for (i in 1:50) {+ test=sample(178,45)+ qda.fit=qda(class~.,data=wine[-test,])+ qda.pred=predict(qda.fit,wine[test,])+ Accuracy[i]=mean(qda.pred$class==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9866667
K-最近邻(KNN)
KNN 是一种非参数方法,其中基于其 K-最近邻的类别对观测值进行分类。当决策边界是非线性时,这是一个有用的模型,但它不会告诉我们哪些预测是重要的。
> library(class)> knn.pred=knn(wine[-test,2:14],wine[test,2:14],wine[-test,]$class,k=1)> table(knn.pred,wine[test,]$class)> mean(knn.pred==wine[test,]$class)[1] 0.7777778
通过执行以下命令,我们可以多次重复上述过程,以获得对 KNN 模型性能的更准确估计:
> for (i in 1:50){+ test=sample(178,45)+ knn.pred=knn(wine[-test,2:14],wine[test,2:14],wine[-test,]$class,k=1)+ Accuracy[i]=mean(knn.pred==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.7435556
我们可以对 k=2 到 5 重复相同的过程,结果显示在表 4 的中间栏中。
从表 4 的中间一栏我们可以看出,KNN 模型的结果并不令人印象深刻。这是因为 KNN 模型使用欧几里德距离来测量两点之间的距离,如果要素具有不同的比例,它会影响模型。由于 13 个要素中的每一个都具有不同的比例,因此对数据进行归一化处理以使所有要素都具有相同的值范围非常重要。我们可以在缩放数据后重新运行 KNN 模型,如下所示:
> for (i in 1:50){+ test=sample(178,45)+ knn.pred=knn(scale(wine[-test,2:14]),scale(wine[test,2:14]),wine[-test,]$class,k=1)+ Accuracy[i]=mean(knn.pred==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9382222
表 4 总结了 KNN 模型的结果,我们可以看到缩放数据极大地提高了模型的性能。

表 4。有无数据缩放的 KNN 模型的准确性
总结
表 5 总结了不同分类模型对葡萄酒数据集的准确性。LDA 和 QDA 的精确度最高,其次是 KNN (k=5)模型。

表 5。不同分类模型的准确性
主成分分析
上面描述的模型可以基于 13 个测量的预测值来预测葡萄酒的类别。然而,我们也有兴趣知道这三个类别之间的主要区别是什么,以及什么预测是重要的。为了做到这一点,我们可以执行主成分分析,这是一个探索性数据分析的有用工具。
> pr.out=prcomp(wine[,-1],scale=TRUE)> pr.out$rotation> biplot(pr.out,scale=0)> plot(pr.out$x[,1:2],col=wine$class)
前两个 PC 分数和相应的加载向量如图 2 所示。

图二。葡萄酒数据集在前两台电脑上的投影。每个箭头表示顶部和右侧轴上的前两个 PCs 的加载向量。图上的每个数字代表该特定数据点的 PC1 和 PC2 得分(图片由作者提供)

图 3。葡萄酒数据集的前两个 PC 分数。黑色代表 1 级,红色代表 2 级,绿色代表 3 级酒。(图片由作者提供)
图 2 和图 3 显示数据点被分成三个不同的组,对应于三种葡萄酒。1 班和 3 班的 PC2 分数相对相同,但 PC1 分数相差很大。另一方面,类 2 具有介于类 1 和类 3 之间的 PC1 分数,并且其 PC2 分数低于其他两个类。我们可以通过查看 PC 负载向量(图 2 中的箭头)来进一步检查每个类之间的差异。例如,“火山灰碱性”的方向是朝向 PC1 和 3 类数据点的高值。因此,我们可以预计,等级 3 具有较高的“灰分碱度”值,其次是等级 2 和等级 1。我们可以对其他 13 个预测因子进行类似的研究。这种调查的结果总结在表 6 和表 7 中,表 6 和表 7 显示了三种葡萄酒之间的主要差异。这些发现与表 2 所示的 LDA 结果一致。

表 6。总结了 1 级和 3 级葡萄酒的主要区别。对于上述参数,等级 2 的值介于等级 1 和等级 3 之间。

表 7。2 级和 1/3 级葡萄酒的主要区别
结论
使用四种分类方法来评估每个模型在预测葡萄酒类别中的准确性。QDA 和 LDA 的精确度最高,其次是 KNN 和多项逻辑回归。在应用 KNN 模型进行准确分类之前,对数据进行归一化是非常重要的。主成分分析用于识别三类葡萄酒之间的主要差异。
消息来源
UCI 机器学习知识库:葡萄酒数据集。知识共享署名 4.0 国际 (CC BY 4.0)许可”。
赢得 Kaggle 谷歌大脑——呼吸机压力预测
实践教程
在 LSTMs、变压器和 PID 控制器上…

一个人工机械肺(来自维基媒体
11 月 4 日,在 Kaggle 上组织的谷歌大脑——呼吸机压力预测中,我们成功地从 2650 个团队中脱颖而出。在这篇博文中,我想带你经历一次让我们赢得胜利的旅程。在 Kaggle 论坛上可以找到这篇文章的更概括的版本,我们已经公布了我们的 LSTM + CNN 变压器模型和 PID 匹配的代码。

赢得永恒的荣耀和 2500 美元!作者截图
来自 Kha 的邮件…
在完成我的博士学位和一段不愉快但短暂的工作经历后,我有一点被烧毁了。结果,我在 2021 年离开了 Kaggle 一段时间,你可以从我的活动日志中看到。我参加了一点“石头、剪子、布大赛”,并尝试了“室内定位和导航挑战”。

作者截图
9 月 23 日, Yves 和我收到了 Kha 发来的邮件。他在 Kaggle 上发现了一个有趣的时间序列比赛,这个比赛只持续了 1 个月。这个比赛吸引了我,因为我非常熟悉时间序列数据,也是短期比赛的忠实粉丝。此外,我知道当 Kha、Yves 和我合作时,好事往往随之而来!我回复 Kha 说我会有兴趣加入,但是在我真正投入之前,先要处理一些现实生活中的事情。其中一件必须做的事情是准备&介绍 Kha、Yves 和我在 2020 年“利物浦离子交换”竞赛中获得的第三名解决方案。Kha 和 Yves 开始着手解决这个问题,并取得了一些进展。过了一段时间,10 月 6 日,我加入了他们。
简要问题陈述
该竞赛的目标是根据吸气电磁阀打开的程度,预测任何给定时间步长的机械肺内的压力(u_in;0 到 100 之间的值)。我们接受了大约 75000 次训练呼吸和 50000 次测试呼吸。每次呼吸由吸气阶段和呼气阶段组成,由提供的u_out指示。我们的提交仅在吸气阶段评分(因此u_out等于 0)。此外,呼吸被记录在具有不同阻力和容量属性的机械肺中,这些属性被编码在提供的R和C变量中。

1 训练呼吸的例子。我们被提供 u_in,u_out,R & C,并被要求预测压力。作者图片
深度学习斗争
从本次比赛中公布的公共笔记本来看,很明显 LSTMs 或任何类型的时间深度学习模型都将占据主导地位。我们三个人都很快增加了功能或对这些管道进行了小的修改,但都无济于事……很明显,深度学习不是我们的强项。然后,我们的思维方式发生了转变:“与其试图创建一个适合所有训练数据的模型,然后用于对所有测试数据进行预测,我们难道不能专注于单独的呼吸,看看我们是否可以对其建模以找到模式?”
拥有物理学背景的 Yves 研究了 PID 控制器,并很快发现了一个很好的简单的后处理技巧,它能够给我们的 LB+0.004 的提升,这是非常显著的。由于这种后处理技巧,我们能够通过稍微修改的公共笔记本爬上排行榜,使我们在黄金区之外获得一席之地。Yves 的后处理技巧解释起来相当简单(但不容易找到),并且是基于 PID 控制器的理论。
我们可以想出一个非常简单的公式:pressure = kt — (u_in/kp)。有了必须调整的算法的kt和kp参数,u_in是输入数据,pressure是我们想要预测的输出数据。在组织者写的一篇论文中提到了搜索这些参数的网格。但是,在没有纸张的情况下,仅通过对训练数据中的每一次呼吸应用线性回归,就可以容易地发现这个网格(其中我们有 u_in 和压力)。
现在的问题是,我们如何知道某一组参数对测试呼吸有多好,因为我们没有计算误差的压力。为此,我们可以利用压力的离散性。只有 950 个唯一的压力值,它们之间的距离相等。因此,如果我们填入两个候选参数kt和kp,并且计算出的压力值与这 950 个独特的压力值完全一致,我们就有了匹配。这种匹配适用于大约 2.5%的数据。
KP_GRID = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
KT_GRID = [10, 15, 20, 25, 30, 35]def match_breath(breath):
u_in = breath['u_in'].values
inspiratory_len = np.sum(1 - breath['u_out'])
u_in = u_in[1:inspiratory_len]
breath_mask = (u_in > 0) & (u_in < 100)
if np.sum(breath_mask) < 2:
return None, None, None, None
for kt in KT_GRID:
for kp in KP_GRID:
preds = (kt - u_in / kp)
preds_round = (preds-MIN_PRESSURE)/DIFF_PRESSURE
tune_error = np.round(preds_round[breath_mask]) - preds_round[breath_mask]
if np.sum(np.abs(tune_error)) < 1e-6:
return preds, kt, kp, breath_mask
return None, None, None, None
派援军来!
在发现后处理技巧后,我们无法做出任何更大的改进。我们慢慢地看到我们在 LB 的排名下降,很明显,为了在这次比赛中取得好成绩,我们需要有更深层次学习经验的人。我们给当时正好在黄金地带下方的舒军发了消息。我知道 Shujun 在深度学习方面非常强,因为他在我也参加的“open vaccine—Covid mRNA”比赛中获得了一个具有独特和强大架构的单人金牌。男孩没有让他或失望。
舒骏的深度学习架构是 LSTM 细胞、1D 卷积和变形金刚的组合。LSTMs 是必要的,因为目标压力严重依赖于以前的时间点。卷积结合转换器是一个很好的组合,可以模拟全局依赖关系,同时弥补转换器无法捕捉局部交互的不足。网络相当深,我们遇到了一些梯度问题。因此,一个名为ResidualLSTM的新模块被创建,它增加了一个前馈网络(FFN),并用一个剩余连接将输入连接到 LSTM,输出连接到 FFN。该模型对原始数据进行处理,此外还有一些滞后和差异特征、u_in 的累积和以及一个热编码的 R & C 特征。

LSTM + CNN 变压器架构。合著者 Shujun 的图片。
我们将我们当前的混合物与舒军的不同运行模型混合,并应用我们的 PP 技巧。轰,我们中了黄金!

作者图片
我们尝试向 Shujun 建议一些想法,以便在他的管道中实施,还尝试添加一些功能,以便管道(因为他的管道利用了功能)。但是,我们还是没有成功。
几天后,我们觉得我们的混合需要另一个独特的深度学习管道。这就是里卡多进入故事的地方!当我们给他发信息的时候,里卡尔多只比我们低几个点。他建立并运行了一个 LSTM 架构,只集中于原始数据。没有特征工程,没有花哨的东西,只有原始数据和许多时代。诀窍是有一个好的学习进度计划。我们选择了 30 个历元的 ReduceLROnPlateau。这与公共笔记本中通常使用的快速退火调度器形成了对比,后者容易过度适应。当我们将我们的验证损失曲线与公共笔记本的验证损失曲线进行比较时,可以看出这一点,如下所示。这里的红色(赌注)曲线对应于公共笔记本,并很快超拟合。

作者图片
这种方法甚至比 LSTM + 1D CNN 变压器编码器得分更高。这些渠道的结合让我们在排行榜上名列第四。我们决定将我们的目标从金牌转变为赢得金钱。

作者图片
同样,我们所有人都专注于改善我们已经启动并运行的两条管道。对两个管道进行轻微的修改,或者向 transformer 管道添加特性。只做了极其微小的改进,与我们前面的竞争对手差距太大。当然,他们发现的数据中隐藏着一些我们还没有发现的东西……
史上最好的生日礼物
10 月 26 日是我的生日,我决定学习更多关于 PID ( P 比例- I 积分- D 导数)控制器的知识,因为这些也是组织者论文中的核心概念。此外,我们的后处理技巧来自 Yves 对这些 PID 控制器的研究。

来戳这个兔子洞吧…图片作者
我在 Python 中摆弄了一下 simple-pid 包,因为这个包已经实现了 pid 公式。我也能够用这个软件包重现我们的后处理技巧。然而,对于大多数呼吸,我无法找到匹配。直到我决定专门研究代码和积分项。有多种方法来计算 PID 控制器的积分项,该软件包只包括普通的实现。我决定插入一些其他的替代品,突然,我有了一个突破……多好的生日礼物啊!下面的代码片段(基于 simple-pid 包)能够为某些呼吸完美地模拟压力- > u_in:

接近了,但还没有…仍然是一个巨大的突破!作者图片

几个小时后,修复一些细节…图片由作者
以下是能够完美模拟某些呼吸的 u_in 压力的代码片段:
def generate_u_in(pressure, time_step, kp, ki, kt, integral=0):
dt = np.diff(time_step, prepend=[0])
preds = []
for j in range(32):
error = kt - pressure[j]
integral += (error - integral) * (dt[j] / (dt[j] + 0.5))
preds.append(kp * error + ki * integral)
return preds
我想我们有了一个新的不错的后处理技巧,并把这个信息发给了 Yves。伊夫做了一些更大的事情。他想出了一些能够完美匹配压力的代码。

你能感受到兴奋吗?作者图片
完美的搭配
我们可以编写 PID 控制器的“普通”方程(对应于我提供的generate_u_in片段):

其中epsilon等于target — pressure,其中目标代表由用户设置的理想压力。现在gamma,也就是衍生项的权重,总是被组织者设置为零。所以我们只需要关注第一项(比例)和第二项(积分)。我们之前讨论的后处理技术实际上对应于这个等式,其中beta设置为 0。为了便于记记,让我们重写第二个积分项。此外,积分项通过如下重量衰减实现:

时间步长 t 和 t-1 之间的时间差τ为常数,对于该数据等于 0.5。我们可以从 PID 方程中分离出积分项,以便计算任意时间点的积分项。这避免了必须在整个信号中传播:

有了这些等式,我们就可以开始匹配算法了。对于给定的参数配置(target、alpha 和 beta 或在本文的剩余部分分别称为 kt、kp 和 ki ),我们的匹配算法在时间步长t填充给定呼吸的压力如下:
- 取两个随机压力值
P0和P1。有 950 种可能的压力值,因此有 950*950 种可能的组合。 - 计算
I0 = (u_in[t - 1] - kp * (kt - P0))/ki - 计算
I1 = I0 + (kt - P1 - I0) * (dt / (dt + 0.5)) - 如果
kp * (kt - P1) + ki * I1 == u_in[t]我们有一个匹配,我们可以填写 P1 压力[t]
在代码中:
P_RANGE = np.arange(
MIN_PRESSURE,
MAX_PRESSURE + DIFF_PRESSURE,
DIFF_PRESSURE
)
for P0 in P_RANGE:
for P1 in P_RANGE:
I0 = (u_in[t - 1] - kp * (kt - P0))/ki
I1 = I0 + (kt - P1 - I0) * (dt / (dt + 0.5))
u_in_hat = kp * (kt - P1) + ki * I1
if abs(u_in_hat - u_in[t]) < 1e-8:
print(P0, P1, pressure[t-1:t+1])
然而,这个代码相当慢。这是因为对于每个可能的参数组合,需要尝试 950950 个组合。kp 和 ki 有 20 个可能的值,kt 有 6 个可能的值,因此总共有 9509502020*6 个可能的组合。然而,对于固定的P0,我们注意到abs(u_in_hat — u_in[t])在 for 循环中线性增长。因此,我们可以通过两次测量来推断。如果这个外推函数与 x 轴的交集出现在一个整数处(或接近于一个整数),那么我们的 If 条件在内部 for 循环的迭代中将被评估为 True。

作者图片
这种观察使我们可以将代码加速 950 倍。
for P0 in P_RANGE:
I0 = (u_in[t - 1] - kp * (kt - P0))/ki
# Calculate 2 points for P1 so we can get the slope
I11 = I0 + (kt - MIN_PRESSURE - I0) * (dt / (dt + 0.5))
u_in_hat1 = kp * (kt - MIN_PRESSURE) + ki * I11
I12 = I0 + (kt - MIN_PRESSURE2 - I0) * (dt / (dt + 0.5))
u_in_hat2 = kp * (kt - MIN_PRESSURE2) + ki * I12
slope = u_in_hat2 - u_in_hat1
x_intersect = (u_in[t] - u_in_hat2) / slope
if abs(np.round(x_intersect) - x_intersect) < 1e-8:
print(P0, MIN_PRESSURE + (x_intersect + 1) * DIFF_PRESSURE)
开大声点!带来噪音!
有了上面的代码,我们能够匹配很多压力。但他们中的许多人仍然无法匹敌。在组织者的论文中,有人解释说 u_in 数据中加入了人为噪声。我们开始努力消除这种噪音。让我们首先看看当前版本的匹配算法对特定呼吸(训练呼吸 id 17)的输出

作者图片
注意,u_in 的第一个值和中间的一些值并不完全匹配。所有呼吸的第一个值都有噪声,不可匹配,但中间的点应该是可匹配的…让我们从索引 5 开始,根据提供的压力调用我们的 generate_u_in 代码,以生成“理想 u_in 值”,并从提供给我们的 u_in 中减去这个值。
kp, ki, kt = 1., 3., 30
I5 = (u_in[4] - kp * (kt - pressure[4]))/ki
u_in_hat = generate_u_in(pressure[5:], timestep[4:], kp, ki, kt, integral=I5)
noise = u_in[5:32] - u_in_hat[:-5]
plt.plot(timestep[5:32], noise, '-o')
plt.title('noise')
plt.show()

作者图片
理想 u_in 值和提供的 u_in 值之间的差值形成一个漂亮的三角形,除了在三角形的转折点/峰值上。关键是这个噪声的斜率((np.diff(noise) / np.diff(timestep[5:32])))对于这个三角形上的连续点是相等的。我们也可以重新编写积分计算向后工作。

有了这两种见解,我们能够匹配更多的数据:

作者图片
把它放在一起
给定我们的 DL 模型和匹配算法,我们的方法相当简单:
- 用我们的两个 DL 模型生成预测。训练他们在多个种子进行 10 重交叉验证。这为我们提供了多个略有不同的模型。将这些模型的(加权)中值作为一种集成技术,因为当 MAE 是度量标准时,这通常优于平均值。
- 根据这些预测,应用我们的匹配算法。如果我们找到匹配,用匹配器替换 DL 算法的预测。如果不匹配,则保留 DL 预测。
总的来说,我们能够大致匹配 66%的数据,这给了我们在排行榜上巨大的提升。
结束营业的时间
这是我在卡格尔的第一次胜利。虽然我承认 PID 控制器匹配有点厚脸皮,但我仍然对我们的结果感到非常自豪,因为 2650 个团队中只有 2 个能够找到这个漏洞。此外,我们是唯一能够在 u_in 数据中匹配噪声的团队。我要感谢我的队友们兹德米、卡、舒军和 B ,没有他们,这个结果是不可能的!
如果你有任何问题或不清楚的地方,随时留下一些评论或通过其他媒介与我联系(双关语)。
参考
- 主办方论文
- Kaggle 论坛报道
- PID 匹配代码+解释 (Kaggle)
- PID 匹配代码+解释(Github)
- LSTM + CNN 变压器代码(Kaggle)
- LSTM + CNN 变压器代码(Github)
- LSTM 原始数据代码(Github)
胜于替代 1.1 和预期目标 1.1:模型更新和验证
一些小改动。更多的数据,以及它真正的价值。
就在半年多前,我发布了两个评估 NHL 滑手和守门员的描述性模型:预期目标和胜于替补。对于任何不熟悉或想要刷新记忆的人来说,这里有一个每个型号的快速运行:
- 预期目标利用极端梯度推进(extreme gradient boosting),一种先进的机器学习技术,根据射门距离、角度和射门前发生的事件等因素,计算未被阻挡的射门尝试成为目标的概率。预期目标可以解释为加权投篮。
- 胜于替换(战争)使用岭回归(RAPM) 来隔离运动员在力量相等的情况下对预期目标的影响,以及他们对力量打法的预期目标和点球决胜的预期目标的影响。它以前还使用岭回归来分离每个球员对他们球队获得和获得点球的比率的影响,以及射手和守门员对他们获得或面临的未阻挡射门尝试的影响(射门/扑救)。对于滑冰运动员来说,这些回归的结果分为六个部分:力量进攻,力量防守,力量进攻,人手不足防守,处罚和投篮。(注意,这里的力量与 NHL 的定义略有不同,并且不包括空网的比赛;它只包括 3 对 3、4 对 4 或 5 对 5 比赛,其中两队都有一名守门员。这将适用于我在这篇文章中提到的任何力量游戏。)从玩家实际提供的进球数中减去替换级别玩家预期在每个部分中提供的进球数,然后使用与一个进球数的值相当的除法器将该进球数转换成获胜数,以便获得高于替换的获胜数。
从 2013–2014 年到 2019–2020 年的所有赛季都建立了预期目标,而从 2014–2015 年到 2019–2020 年的所有赛季都建立了胜于更替目标。这两种型号都用于 2020-2021 赛季。
我对两个模型都做了一些更新。最激动人心的更新是,我激动地宣布我现在有了这两款车型从 2007-2008 年到 2020-2021 年的数据,并将很快提供所有数据。不幸的是,2007 年至 2008 年和 2009 年至 2010 年间的一些比赛没有完整的比赛数据和位置坐标;我已经选择从这些模型中完全排除这些游戏。您可能会注意到在输出中,某些在某个赛季打了 82 场比赛的球队或球员被列为打得更少;这意味着他们玩的是一个无法模拟的游戏。
其他的更新都是无关紧要的,我可能会实现它们而不提及任何东西,但我认为透明度很重要,最重要的是,我只是喜欢写这些东西。
我的两个模型以前都是用 R 构建的,它们都是完全用 Python 重新构建的。除了我有意做的调整之外,建模过程被尽可能紧密地遵循,但是模型输出之间会有一些微小的差异也是很自然的。某个投篮在一个模型中可能值 0.2 个预期目标,在另一个模型中可能值 0.3 个,没有真正的原因。
考虑到这一点,以下是我选择对每个型号进行的具体调整:
预期目标
- 每一季的模型训练所依据的数据差异很大。从 2007-2008 年到 2009-2010 年,我从样本中删除了 100 个“目标”游戏,根据剩余的数据训练模型,然后在我删除的目标游戏上运行它。在这三个赛季中,我对每 100 个游戏样本重复了这个过程。我这样做是因为与 2010-2011 年至今的数据不同,所有拍摄位置坐标都完全来自 ESPN 的 XML 报告,这些报告与 NHL 的 API 报告在拍摄跟踪方式上略有不同。
- 从 2010–2011 年到 2016–2017 年,我只是从样本中删除了一个目标季节,在剩余的季节训练模型,并在目标季节运行模型。我在这 7 季中重复了这个过程。
- 从 2017–2018 到 2021 年,我使用了与 2007–2008 到 2009–2010 年相同的过程:从样本中删除 100 个目标游戏,在剩余的游戏上训练模型,然后在目标游戏上运行模型。我将这些赛季从 2010-2011 年分为 2016-2017 年,因为 NHL 从 2017-2018 年开始对守门员设备法规进行了调整。守门员设备尺寸的减小带来了射门成为进球的可能性的可预测的增加,并且一个模型应该考虑到这一点。
- 在每个模型被训练的季节之外,这个模型的建模过程保持尽可能类似于先前的建模过程;所有相同的变量都被考虑在内,模型训练的参数使用相同的交叉验证方法获得。
胜于替代
- 这个模型的前一个版本使用了一个先验(贝叶斯)RAPM,用于力量均衡进攻、力量均衡防守、强力进攻和人手不足防守。我从 2013 年至 2014 年的无事先知情(香草)RAPM 开始,然后计算 2014 年至 2015 年的事先知情 RAPM,然后使用 2014 年至 2015 年 RAPM 的输出作为 2015 年至 2016 年的先验。我在 2020-2021 年间重复了这一过程,有效地创造了所谓的菊花链。新型号与旧型号不同,它使用菊花链,但这种菊花链始于 2007 年至 2008 年,并简单地使用 2007 年至 2008 年香草 RAPM 的输出来创建力量进攻、力量防守、力量进攻和人手不足的防守组件。
- 对判罚的影响不再通过岭回归来计算,而是通过抽取和执行的个人判罚来计算。虽然我相信玩家无法控制的因素确实会影响他们抽牌和罚点球的速度,但我不认为回归分析足以适应这种外部环境。类似于使用支持和反对的目标作为目标变量运行岭回归,我相信这在理论上听起来很棒,但在实践中,一个赛季中球员在冰上的目标变量的出现次数可能太低,无法在这样的回归输出中放置太多股票。这个模型的 1.0 版本中反直觉输出的数量和惩罚战的低重复性促使我做出这个决定。
- 对射门和救球的影响不再通过岭回归来计算,而是通过相对于预期目标的进球和允许进球来计算。这个改变主要是将拍摄分解成 3 个部分,然后可以添加到模型的其他部分;该模型的 1.0 版本仅具有一个“投篮”组件和一个“救球”组件,这使得很难真正衡量球员在均匀力量和力量发挥方面的影响。这也使得衡量守门员为每支球队做了什么变得不可能。考虑到一个赛季进球的样本量很小,我目前还没有信心使用岭回归来分离这方面的影响;尤其是在将这些样本进一步细分为三个更小的子样本之后,这三个子样本分别是平均强度、强力打法和短手打法。我计划在某个时候使用某种逻辑脊回归来重新审视这一点,因为我确实认为评估守门员和守门员表现的指标应该考虑到他们面对的各自守门员和射手的影响。不过,就目前而言,我对计算模型的拍摄和保存组件的方式感到满意。
- 因为在一些早期赛季,所有选手的工资数据都不容易获得,所以替代水平不再由 850,000 美元或更低的工资帽和 UFA 签约状态来定义。更确切地说,替换等级是由冰时定义的,每个游戏强度的定义略有不同:
- 实力相当时的替补水平定义为在冰上百分比实力相当时,队内排名低于第 13 位的所有前锋和排名低于第 7 位的所有防守队员。
- 替补级别是指在冰上的力量比赛时间百分比中,所有排名低于第 9 位的前锋和所有排名低于第 4 位的防守队员。
- 点球决胜中的替补级别定义为队中所有排名低于第 8 位的前锋和所有排名低于第 6 位的防守队员在场上的人手不足时间百分比。
- 所有情况下的替补水平(出于处罚目的)被定义为在所有情况下球队中排名低于第 13 位的所有前锋和排名低于第 7 位的所有防守队员的上场时间百分比。
- 守门员替换级别定义为在比赛中排名低于第二的所有守门员。
- 请注意,虽然在所有情况下都使用了罚分,但没有其他组件使用空网比赛。因为这个原因,在战争旁边出现的总冰上时间值,以及随后的战争/冰上时间比率,都没有将冰上时间与空网合并。
- 在所有情况下仍然使用点球,因为不可能从 NHL 的比赛数据中确定地得出点球是由守门员在网内进行的,还是守门员在抽签后被推迟点球。在守门员被拉下的情况下进行的处罚很少,所以这个问题的影响可以忽略不计,但这是值得注意的。
- 我之前使用 Christopher Long 的方法计算了一个进球相对于一场胜利的价值,发现从 2017-2018 年到 2019-2020 年,一场胜利大约相当于 5.33 个进球。对于模型的 1.1 版本,我遵循克里斯托弗的方法,使用 2007-2008 年到 2020-2021 年的每个 NHL 赛季计算一个稳定的毕达哥拉斯指数:2.022。然后,我使用每个赛季每场比赛的联赛平均进球数——排除所有净胜球或点球大战中的进球数,就像我在模型的其余部分所做的那样——来确定每个赛季相当于一场胜利的进球数。以下是我获得的价值:
╔══════════╦═══════════════╗
║ **Season** ║ **Goals Per Win** ║
╠══════════╬═══════════════╣
║ 20072008 ║ 5.051 ║
║ 20082009 ║ 5.345 ║
║ 20092010 ║ 5.201 ║
║ 20102011 ║ 5.115 ║
║ 20112012 ║ 4.989 ║
║ 20122013 ║ 4.975 ║
║ 20132014 ║ 5.002 ║
║ 20142015 ║ 4.908 ║
║ 20152016 ║ 4.853 ║
║ 20162017 ║ 5.053 ║
║ 20172018 ║ 5.375 ║
║ 20182019 ║ 5.438 ║
║ 20192020 ║ 5.422 ║
║ 20202021 ║ 5.285 ║
╚══════════╩═══════════════╝
确认
我总结了对预期目标所做的更改,并在替换模型上取得了成功。但是有了 2007-2008 年的数据,我决定是时候使用整个样本来验证这些模型,看看这些老派的详细数据是否真的像它所描述的那样糟糕。
我用来验证我的预期目标模型的指标包括曲线下面积(AUC),你可以在这里了解更多关于的信息,以及每个实际目标的预期目标。以下是每个季节的测试指标值:
╔══════════╦════════════════╦═══════╦═════════════════╗
║ **Season** ║ **Game Strength** ║ **AUC** ║ **xGoals per Goal** ║
╠══════════╬════════════════╬═══════╬═════════════════╣
║ 20072008 ║ All Situations ║ 0.769 ║ 0.985 ║
║ 20072008 ║ Even Strength ║ 0.778 ║ 0.990 ║
║ 20072008 ║ Power Play ║ 0.710 ║ 0.977 ║
║ 20072008 ║ Shorthanded ║ 0.767 ║ 0.931 ║
║ 20082009 ║ All Situations ║ 0.775 ║ 0.993 ║
║ 20082009 ║ Even Strength ║ 0.783 ║ 0.991 ║
║ 20082009 ║ Power Play ║ 0.713 ║ 1.000 ║
║ 20082009 ║ Shorthanded ║ 0.815 ║ 0.983 ║
║ 20092010 ║ All Situations ║ 0.760 ║ 1.033 ║
║ 20092010 ║ Even Strength ║ 0.767 ║ 1.017 ║
║ 20092010 ║ Power Play ║ 0.700 ║ 1.071 ║
║ 20092010 ║ Shorthanded ║ 0.787 ║ 1.092 ║
║ 20102011 ║ All Situations ║ 0.773 ║ 1.000 ║
║ 20102011 ║ Even Strength ║ 0.782 ║ 0.987 ║
║ 20102011 ║ Power Play ║ 0.715 ║ 1.031 ║
║ 20102011 ║ Shorthanded ║ 0.773 ║ 1.075 ║
║ 20112012 ║ All Situations ║ 0.775 ║ 0.999 ║
║ 20112012 ║ Even Strength ║ 0.781 ║ 0.988 ║
║ 20112012 ║ Power Play ║ 0.722 ║ 1.028 ║
║ 20112012 ║ Shorthanded ║ 0.807 ║ 1.047 ║
║ 20122013 ║ All Situations ║ 0.771 ║ 0.973 ║
║ 20122013 ║ Even Strength ║ 0.779 ║ 0.981 ║
║ 20122013 ║ Power Play ║ 0.697 ║ 0.929 ║
║ 20122013 ║ Shorthanded ║ 0.780 ║ 1.168 ║
║ 20132014 ║ All Situations ║ 0.773 ║ 0.991 ║
║ 20132014 ║ Even Strength ║ 0.781 ║ 0.981 ║
║ 20132014 ║ Power Play ║ 0.715 ║ 1.042 ║
║ 20132014 ║ Shorthanded ║ 0.783 ║ 0.849 ║
║ 20142015 ║ All Situations ║ 0.771 ║ 1.002 ║
║ 20142015 ║ Even Strength ║ 0.778 ║ 0.996 ║
║ 20142015 ║ Power Play ║ 0.706 ║ 1.013 ║
║ 20142015 ║ Shorthanded ║ 0.812 ║ 1.083 ║
║ 20152016 ║ All Situations ║ 0.771 ║ 1.024 ║
║ 20152016 ║ Even Strength ║ 0.780 ║ 1.036 ║
║ 20152016 ║ Power Play ║ 0.696 ║ 0.996 ║
║ 20152016 ║ Shorthanded ║ 0.782 ║ 0.953 ║
║ 20162017 ║ All Situations ║ 0.771 ║ 1.011 ║
║ 20162017 ║ Even Strength ║ 0.777 ║ 1.017 ║
║ 20162017 ║ Power Play ║ 0.709 ║ 1.002 ║
║ 20162017 ║ Shorthanded ║ 0.789 ║ 0.925 ║
║ 20172018 ║ All Situations ║ 0.766 ║ 1.032 ║
║ 20172018 ║ Even Strength ║ 0.772 ║ 1.030 ║
║ 20172018 ║ Power Play ║ 0.697 ║ 1.050 ║
║ 20172018 ║ Shorthanded ║ 0.828 ║ 0.958 ║
║ 20182019 ║ All Situations ║ 0.762 ║ 1.001 ║
║ 20182019 ║ Even Strength ║ 0.771 ║ 1.004 ║
║ 20182019 ║ Power Play ║ 0.676 ║ 0.991 ║
║ 20182019 ║ Shorthanded ║ 0.798 ║ 0.974 ║
║ 20192020 ║ All Situations ║ 0.772 ║ 0.989 ║
║ 20192020 ║ Even Strength ║ 0.779 ║ 0.984 ║
║ 20192020 ║ Power Play ║ 0.696 ║ 1.004 ║
║ 20192020 ║ Shorthanded ║ 0.821 ║ 1.003 ║
║ 20202021 ║ All Situations ║ 0.774 ║ 0.975 ║
║ 20202021 ║ Even Strength ║ 0.782 ║ 0.966 ║
║ 20202021 ║ Power Play ║ 0.706 ║ 0.994 ║
║ 20202021 ║ Shorthanded ║ 0.813 ║ 1.107 ║
╚══════════╩════════════════╩═══════╩═════════════════╝
预期目标模型在 2007-2008 至 2009-2010 赛季的表现略差于后来几年,但仍好于我的预期。注意,根据我引用的 AUC 文件,0.6 和 0.7 之间的值为差,0.7 和 0.8 之间的值为一般,0.8 和 0.9 之间的值为好;这意味着在所有的情况下,在相同的强度下,这个模型是公平的,在每一个赛季中更接近好的而不是坏的。无论前三季的位置坐标存在什么问题,都不足以阻止模型发布令人尊敬的性能。然而,在权力游戏中,该模型在 5 个赛季中被归类为差,在其他 9 个赛季中更接近差而不是好。
这些数字证实了我对公众预期目标模型的总体立场:总的来说,它们是公平的,我要说它们更接近于好而不是坏。但是特别是在权力游戏上,他们忽略了很多重要的内容。
在测试和验证了我的预期目标模型在每个赛季都是公平的之后,是时候测试我的战争模型了。虽然 WAR 本质上是描述性的,但是很难实现模型的描述性测试,我相信对过去结果的准确描述通常可以很好地预测未来的结果,所以我选择测试模型的描述性和预测能力。
WAR 模型的一个常见的描述性测试是测试团队级别的 WAR 总和与另一个指标(如积分或目标差异)之间的相关性,后者明确定义了团队质量。这个测试本质上是对 WAR 的恭维,因为它不一定测试 WAR 如何评估一个团队中的选手,而是 WAR 如何评估该团队选手作为一个整体的总和。
例如,假设我们有一个完美的战争模型,它会告诉我们,在 2018-2019 年,塞德里克·帕盖特为坦帕湾闪电队提供了-1 场战争,尼基塔·库切罗夫为他们提供了 5 场战争。这意味着他们的贡献总和是 4 战。现在,假设我建立了一个可怕的模型,说帕盖特值 5 次战争,库切罗夫值-1。他们战争的总和仍然是 4,这将完全符合他们贡献的真实组合价值。但是我对每个玩家都很不满意,我的模型在隔离他们的影响方面做得很糟糕。这是一个虚构的例子;没有人的模型说 Paquette 或 Kucherov 有任何接近的东西,但在我们开始分析我的描述性测试的结果之前,值得记住。
正如我提到的,我的战争模型的一些输出是通过岭回归得到的。这些回归将一个赛季中的每个球员视为一个整体,不管他们是否在赛季中更换球队。这意味着我不能将为多支球队效力的球员的贡献划分回他们效力的球队,因此测试该模型的最佳方式是完全移除这些球员。这实际上有点不讨好模型。以下是我第一次描述性测试的结果:

0.82 的 R 值告诉我们,该模型可以解释每 82 场比赛中 82%的积分差异。其余的可以用为多支球队效力的球员所贡献的胜利、纯粹的运气和建模错误的某种组合来解释。
虽然这个值听起来很公平,但当我第一次看到它时,我发现它非常令人担忧。当我去年冬天发布我的模型的输出时,我使用了相同的精确测试方法,并获得了 0.89 的显著更高的 R 值。

这里发生了什么?模型变得明显更差了吗?
谢天谢地,没有。这只是适用于一系列季节,它并不十分有效。当在这张图片中测试 WAR 1.0 的同一三季中测试时,WAR 1.1 实际上表现稍好:

我可以一整天都给出 R 值,但是如果没有某种比较或基线,这些值就没什么价值了。为了了解我们从使用战争中真正获得了多少价值,下一步是将其与曲棍球当前最流行的统计数据进行比较:点数。
分析社区的普遍共识是,战争和类似的模型远远优于点。如果这是真的,他们应该更好地描述团队层面的表现。
为了在平等的基础上比较两者,我首先删除了所有守门员,因为没有人关心守门员得分的多少,所以我们不会在任何一个测试中使用它们。从 2007-2008 年到 2020-2021 年,只使用滑冰运动员重复相同的测试,导致 R:

相比之下,点数的表现并不像分析社区可能认为的那样糟糕:

我不得不承认,我对选手们的总分在这里保持得如此之好印象深刻。他们显然是劣等的,但不是我所期望的那样。这一证据表明,在没有更好的度量标准的情况下,用描述性的方式来使用分数绝对没有错。也就是说,我也认为有理由认为,虽然运动员总积分本身在描述团队层面的成功方面做得很好,但在团队中分配个人荣誉方面,他们可能比战争差得多,我们知道战争在这方面远非完美。
也许一个更好的方法来确定这些指标如何在队友之间恰当地分配信用,是转移到测试模型的预测能力。虽然一个模型说塞德里克·帕盖特是坦帕湾在 2018-2019 年成功的真正驱动力,可能已经通过了那个赛季的描述性测试,但一旦帕盖特换了球队,它就会大大失败,并有望以坦帕为代价为他的新俱乐部带来重大成功。
我为预测测试选择的方法仍然相当简单:使用目标指标作为第一年的比率统计,并将其插入到第二年玩家的游戏量中。在这种情况下,我首先在第一年使用 WAR 来预测第二年的排名:

我获得了 0.35 的 R 值,这是相当可靠的:这意味着第二年运动员预期贡献的胜利数足以解释团队层面上 35%的积分差异。另外的 65%可以用一些因素来解释,比如运动员的表现比他们预期的更好或更差,模型从未见过的运动员,第一年比赛少于 10 场因而被忽视的运动员,守门员和纯粹的运气。
然后我用点数重复了同样的测试:

再一次,就像描述性测试一样,溜冰者的分数显然远远高于其他人,但他们比像我这样的分析极客可能建议的做得更好。再说一次,在没有更好的衡量标准的情况下,我认为使用选手的总分就可以了。
我也很好奇,想看看战争的方式是如何累积到像积分这样的团队级别的指标的。使用排名分数进行描述性测试是没有意义的,因为这样做意味着将第一年的排名分数与第一年的排名分数进行比较。这些是完全相同的指标,将为您提供 1.0 的 R。但是使用第一年的积分来预测第二年的积分是完全有效的,所以我也选择这样做:

类似于运动员积分,单独的团体积分可以很好的预测未来的积分,但是它们不能叠加到战争中。如果你想确定下一年谁是最好的球队,并且你很清楚每个人会为每支球队打多少比赛,那么你会比只有去年的积分榜上的分数的情况好得多。
限制
这些模型有很多局限性:预期目标模型甚至不能被归类为好的,并且推动一些战争组成部分的岭回归有很大程度的误差。我也相信在玩家层面,这种特殊的战争模式过于强调射击,而对游戏驾驶重视不够。这有两个原因:
- 播放驱动成分通过岭回归获得,岭回归使系数偏向 0。这对于避免极端古怪的值是必要的,但这也意味着该模型可能会低估玩家在游戏驾驶上的真实每分钟贡献,并假设他们比实际情况更接近于平均玩家。
- 替换级别射击远低于平均水平比替换级别玩驾驶。我肯定这是为什么,但我认为,虽然这种差异在数学上被夸大了,因为对于上场时间较少的球员,模型将系数偏向 0 的程度更高,但这种差异也确实存在,因为教练更容易识别并迅速减少投篮不佳的运动员的冰上时间。
如果我根据我个人认为重要的东西,以完全任意的方式来“衡量”每一个组成部分,这个模型将更少地强调射击,而更多地强调游戏驾驶。如果我用战争来判断一个团队的收购,我会记住这样一个事实,即游戏驱动部分比射击更具可重复性。
这完全是在说,战争不仅是对过去附加值的不完美估计,而且在预测未来业绩时,应该考虑比战争更多的东西。简而言之,战争作为任何讨论的起点比作为终点更好。
考虑到这些限制,我还将声明 WAR 是对仍然被更频繁引用的更基本的度量标准的明显升级。你不一定要喜欢或使用战争,但如果你每次看到战争都捂住眼睛,或每次听到战争都塞住耳朵,并坚持使用滑手点数来代替,你会错得更多。
群体的智慧——投票分类器,装袋粘贴,随机森林和额外的树
使用多种算法,集成学习,用 python 实现
***Table of Contents*****1\. Introduction
2\. Voting Classifier
3\. Bagging and Pasting
3.1\. Out of Bag Evolution
3.2\. Random patches and Random Subspaces
4\. Random Forest
5\. Extremely Randomized Trees**

在 Unsplash 上 Cliff Johnson 拍摄的照片
1.介绍
在大多数问题上,人们在做决定之前都会咨询别人的意见。在决定一个集体环境时,通常是多数人说了算。虽然即使在个人层面上也是如此,但一些公司在全球层面上调查许多事情。由一群人而不是一个专家做出的决定被称为【群体的智慧】亚里士多德在他的著作《政治学》中首次使用了这一论点。这种方法集成到机器学习中的部分是集成学习。最简单的形式是训练多个模型并比较它们的结果来解决复杂的问题。目的是通过考虑泛化来提高监视下的准确率,并获得更成功的结果。本文通过最小化方法以及如何在 python 中实现方法来涉及决策机制。
2.投票分类器
投票分类器,顾名思义,是一种‘投票’——基于民主的分类。用一句话来解释,可以定义为多数表决。让我们假设我们用三种不同的算法来训练我们的分类问题——SVM、逻辑回归、决策树——我们在每一种算法中获得了不同的成功率。另一方面,投票分类器具体评估每个测试数据的结果,并决定支持投票占多数的一方。训练数据的测试结果如图 1 所示,不正确的预测与 y_test 进行比较,并以红色显示。从准确性结果来看,可以看出巨大的差异。当然,没有预料到真实数据集会有这样的差异,但是图 1。演示了投票分类器的工作原理。由于有 3 个分类器,投票分类器决定了其中至少有 2 个是相同的。这种基于民主多数投票的分类被称为硬投票分类器。

图一。投票分类器是如何工作的?,作者图片
软投票分类器采用更加灵活的策略。就像在硬投票中,评估每个分类器的结果,但后退一步就会出现,也就是说,在进行预测时,它会观察分类器的概率值。例如,让我们考虑测试结果为
- SVM[0 级=0.9,1 级=0.1],
- 决策树分类器[class-0 =0.4,class-1=0.6],
- 逻辑回归[0 类=0.35,1 类=0.65]
如果我们用硬投票分类器对其进行评估,因为有 2 个 1 类,而只有 1 个 0 类,硬投票分类器会判定为 1 类。如果用软投票分类器来评价:
- 0 类的概率:0.33 * 0.9+0.33 * 0.4+0.33 * 0.35 = 0.5445
- 第一类的概率:0.33 * 0.1+0.33 * 0.6+0.33 * 0.65 = 0.4455
可以看出,与硬投票不同,软投票分类器的偏好将是 0 类。
现在,让我们用 python 实现上面提到的乳腺癌数据集:
**OUT
LogisticRegression() 0.9525539512498058
DecisionTreeClassifier() 0.9227293898462972
SVC(probability=True) 0.9437820214252446
KNeighborsClassifier() 0.9455519329296692
VotingClassifier(estimators=[('lr', LogisticRegression()),
('dt', DecisionTreeClassifier()),
('svc', SVC(probability=True)),
('knn', KNeighborsClassifier())]) 0.9490451793199813
VotingClassifier(estimators=[('lr', LogisticRegression()),
('dt', DecisionTreeClassifier()),
('svc', SVC(probability=True)),
('knn', KNeighborsClassifier())],
voting='soft') 0.9455364073901569**
SVC 中‘概率’的默认值为假。对于添加predict_proba()方法,该值被定义为真。这是必要的。在投票分类器中,所有模型都用相同的数据集进行训练和测试。如图所示,软和硬的区别由“投票”决定。
3.装袋和粘贴
另一种方法 bagging 及其衍生方法根据训练数据从投票分类器中分离出来。训练数据集被分成子集,分类器用这些子集来训练。在培训后的测试过程中,民主发挥作用,通过硬投票做出决定。如果随机选择行的子集进行替换,这称为引导聚合(Bagging) 。为了解释表达式“with replacement”,对应于一个分类器中用于训练的特定数据被另一个分类器使用的表达式是 with replacement。这个事件被称为引导。背后的想法是基于统计的,它用于估计数量。通过将数据集随机划分为子样本来评估数据集。在该划分期间,在多个子集中具有相同数据的事件用上述 with replacement 解释,并在 sklearn 中用bootstrap=True定义。
另一方面,如果随机选择行的子集而不替换,这被称为粘贴。任何分类器在粘贴过程中使用的数据都不允许被另一个分类器使用。
由于在这些过程中使用了多个分类器,虽然偏差变化不大,但它降低了模型的方差,保护了模型不至于过拟合,模型的稳定性增加,变得更加通用。现在让我们用 python 实现这些提到的方法:
**OUT
bagging results [0.89473684 0.97368421 0.95767196]
average of bagging: 0.9420310034345123
********************************************
pasting results [0.89473684 0.97368421 0.96296296]
average of pasting: 0.9437946718648473
**********************************************
可以看出,装袋和粘贴的区别是由 bootstrap 决定的。如果拾取数据时有替换,即引导,则称为引导聚集(bagging) ,如果拾取数据时没有替换,即没有引导,则称为粘贴。
到目前为止,一直使用分类,但它也可以用于回归:
**OUT
numeric_cols Index(['wheelbase', 'carlength', 'carwidth', 'carheight', 'curbweight','enginesize', 'boreratio', 'stroke', 'compressionratio', 'horsepower','peakrpm', 'citympg','highwaympg'],
dtype='object')
categorical_cols Index(['fueltype', 'aspiration', 'drivewheel', 'enginetype', 'cylindernumber','fuelsystem'],dtype='object')
bagging results [0.88065606 0.84712127 0.7973921 0.72010695]
average of bagging: 0.8113190957164755
**********************************************
使用汽车价格数据集。分类列用OneHotEncoder编码,大多数数字列用StandardScaler标准化,一些数字列用宁滨分组。然后,我们获得的新数据集包含了max_features=0.4,即 40%的特征和max_sampeles=0.8,即 80%的数据。
3.1.出袋进化
理论:如果数据集足够大,bootstrap=True,36.79%的数据根本不会被选择。
证明:提到了一些数据被选择并用于一个以上的带有装袋的分类器。没有被任何分类器使用的选择数据的概率是(1–1/m ),其中 m 是样本数。
让我们概括一下:不拾取任何分类器都不使用的数据的概率是(1–1/m)ᵐ.当“m”足够大时,统计上它相当于(1–1/e)= 0.6321。
这意味着 63.21%的训练样本被每个分类器提取,36.79%的训练样本未被采样。这 36.79%的样本被称为。由于在训练期间没有“oob”数据,这些数据可用于评估( test_data )。我们可以在 Scikit 学习库中实现它,如下所示:
**OUT
bagging oob results: [0.88811189 0.97183099 0.95774648 0.97183099]
average of oob: 0.9473800847040283**
这只能通过设置 oob_score=True 来实现。
3.2.随机补丁和随机子空间
装袋的超参数:
max_features:决定选择多少/多少特征。Float 表示百分比,integer 表示数字。max_samples:决定采样多少数据。Float 表示百分比,integer 表示数字。bootstrap:布尔,真意味着有替换;假的意思是没有替换。bootstrap_features:布尔,True 表示特征是用替换绘制的;False 表示绘制的要素没有替换。
所有训练实例(bootstrap=False,max_samples为高(默认值=1)),但采样特征(bootstrap_features=True)被称为随机子空间。
**OUT
randomsub 0.929824561403508**
训练和特征都被采样,称为随机面片。
**OUT
patch 0.9649122807017544**
这些主要用于高维数据集,如图像数据集。图像中有大量的特征,成百上千的像素,通过使用这些方法可以获得更可靠的结果。
4.随机森林
Bagging 已经用分类器方法的相同数据集的不同训练子集训练了多次。如果要选择分类器 DecisionTreeClassifier ,决策树的更优化版本 RandomForestClassifier 也可以作为 bagging 的替代方案。
**OUT
random forest results: [0.92105263 0.97368421 0.95767196]
average of rf: 0.9508029332590736
********************************************
bagging random forest results: [0.91578947 0.97368421 0.96296296]
average of bagging rf: 0.9508122157244964
**********************************************
5.额外的树
全名是极度随机树,它建造多棵树以及随机森林。与其他不同,自举不提供随机性,而是在每个节点随机分裂节点。因为在每个节点上都提供了随机分割,所以方差非常低。
**OUT
extra tree results: [0.91052632 0.98421053 0.96296296]
average of extra tree: 0.9525666016894087**
比较树间方差,说的是决策树方差>随机森林方差>极度随机化树方差。
回到指引点击这里。
**https://ibrahimkovan.medium.com/machine-learning-guideline-959da5c6f73d **
预测人群的智慧
用 Python 语言实现时间序列的集合预测

Chuotanhls / Chu Viet Don,黑人群体 Pixabay 上的免费照片
在昨天的文章中,我们讨论了飞镖多方法包以及如何在给定的时间序列上运行预测锦标赛。我们审查了五个选择的模型——指数平滑、Theta、SARIMA、nave forecast 和 Facebook Prophet——中的哪一个会更好地拟合源数据,即 Box & Jenkins 的经典航空公司乘客人数。时间序列预测的飞镖瑞士刀| 2021 年 10 月|走向数据科学
如果你读了这篇文章,你可能记得预测图的网格已经离开了右下角的第六个单元格,明显是空的。

作者图片
让我们通过比昨天更进一步来填补这一空白:
我们将创建一个集合预报,它结合了五种方法,不超过一行代码。
集合方案会显示出单独的方法都无法提供的预报质量吗?

作者图片
1.创建集合预报
在我们讨论集合预报背后的概念之前,让我们生成一个集合预报,然后将它的结果与单个模型进行比较,看看它能提供什么见解(如果有的话)。
我们拿起昨天的多方法预测教程中使用的 Jupyter 笔记本,在 dependencies 单元格中添加一行:我们导入 Darts 的**RegressionEnsembleModel。

作者图片
为了连接我们在变量模型中列出的各个方法,

作者图片
我们添加一行代码来处理集合预报:RegressionEnsembleModel 采用
- 型号列表作为其第一个参数;
- 以及我们想要训练集合的周期数。

接下来的三行与他们对单个预测者的描述相同:
- 训练模型
- 计算预测
- 计算残差
我们将RegressionEnsembleModel插入到一个集合评价函数中,该函数与昨天多方法笔记本中的模型评价函数具有相同的结构。在评估功能中,我们还计算准确性指标并绘制预测图。
作者图片
我们调用评估函数,其中包含我们希望它联系在一起的模型列表。

作者图片
作者图片
我修改了多方法 Jupyter 笔记本的准确性指标。由于我们需要不止一次地访问度量标准的公式——首先是单个方法,然后是整体——我将它们包装在一个函数中,我们可以用一行代码调用它,而不是在脚本中重复所有的代码行。
作者图片
我们调用度量函数,收集集合的准确性度量,并将它们与单个方法的准确性度量组合在一个数据框架中。
作者图片
集合预报返回与任何单独方法相同的输出:我们可以绘制的预报值;残差;和预测准确性度量。

作者图片
2.合奏贡献了什么?
集合提供了我们从其他方法中无法获得的洞察力吗?

作者图片
我们回顾了这些指标,以发现集成是否比其他方法有所改进。
请注意,我还添加了一个“avg”列,它是每个指标的 5 个其他方法的简单平均值。当然,总体指标不等于平均栏中的值,这是没有意义的。平均值不会对所有单个方法的改进有所贡献,因为它位于最好和最坏情况之间的 T2。
相比之下,回归集成模型能够做出比最好的个体方法更准确的预测。不能保证这种改善。您可以在度量表中看到,集合模型在三个度量方面领先于该领域:就 RMSE、R 平方和预测的标准误差而言,与 Theta 方法相比,差距较小。但在 MAPE 一行,θ以同样小的幅度领先。
RMSE 对预测误差进行平方,对较大误差的惩罚力度比 MAPE 更大。当残差的分布是左偏或右偏时,就会出现偏差。平均值将高于或低于中间值。最小化 RMSE 的预测将显示出较小的偏差。但是对于具有许多异常值的源数据,对异常值的敏感性可能不是优选的。在文献和评论部分,你可以找到关于 RMSE 和 MAPE 的相对优势和劣势的热烈讨论,以及许多其他指标的利弊。因此,我们不能通过一个简单的判断,一劳永逸地说,MAPE 或 RMSE 在决定模式之间的赛马方面更胜一筹。
这个集合的 R 平方要高半个百分点,所以它解释了实际观测中运动的额外部分。
预报的标准误差 se (也就是预报误差的估计标准偏差),对于集合预报来说要小一点。简单回归数学(duke.edu)
根据我们的指标,投票结果是 3 比 1 支持集合模型。但这种“投票”并不是宣布方法之间竞赛获胜者的硬性标准。对于其他时间序列,集合预报肯定可以获得更好的预报质量,并显示更明显的指标差异。因为创建集成只花费了我们三行代码,所以当我们在我们的源数据上运行方法比赛并发现它是否能比其他方法增加改进时,将集成添加到单独的模型中是值得的。
在这里,我们刚刚证实了 Theta 方法非常接近整个预测者群体的智慧,如此接近,以至于两者几乎可以互换。
3.使用集合结果
我们可以像处理任何单独方法的结果一样处理集合预报值。合奏只是另一个飞镖模型,一个我们在飞行中创建的模型,一个为具体的时间序列量身定制的模型,而不是像西塔或 ARIMA 那样带有公共名称标签的理论方法。
我们可以在系综上运行 Darts 的 plot_residuals_analysis() 函数。

作者图片
我们将合奏添加到右下角先前空着的第六个子剧情中。

作者图片
统计模型的永盒测试和统计模型的正态性测试也可以应用于总体的残差。

作者图片
3.集合预报背后的概念是什么?
集合预报的目标是获得一个更稳健的模型,并且可以更好地推广到新的数据点或其他时间序列。组合预测器的目的是减少预测的标准误差。
在处理具体的时间序列、识别和处理异常值或处理其趋势或季节性变化时,个别方法可能会表现出一些弱点。如果我们简单地用一种不同的方法代替一种方法,我们就有可能在某个时候被第二种方法的某些弱点绊倒。通过组合几种方法,预测者群体的智慧可以——在许多情况下,但不一定在所有情况下——消除单一方法模型的弱点。
用户选择 Darts 作为整体构建模块的方法。然后 RegressionEnsembleModel (如果我们没有指定额外的参数)将运行一个线性回归模型,将预测者作为其回归变量。回归计算所选预测器的线性组合,使预测值与实际观测值最接近。因此,回归模型研究每种方法在集合中的整合(加权)程度,以最小化与实际观测值的偏差。回归变量是复杂预测方法本身的结果,而不仅仅是简单的来源数据。回归目标,即系综,形成加权平均预测值。
RegressionEnsembleModel 接受比简单线性回归更复杂的集成函数,前提是这些函数通过实现 fit()和 predict()方法遵守 scikit-learn 模式。
Jupyter 笔记本可以在 GitHub 上下载:h3ik0th/Darts _ ensembleFC:Python 时间序列用 Darts 进行集合预报(github.com)
随着大数据的出现,了解正确背景变得越来越重要
在开发算法之前,理解 3 个关键问题中的业务目标

布雷特·乔丹在 Unsplash 上的照片
上下文,语境,语境。在数据科学领域,背景就是一切。我认为数据科学家犯的最大错误是不理解他们的工作背景和业务目标。相反,他们非常注重了解人工智能/人工智能技术。复杂算法用在正确的地方是很棒的。理解那个地方应该在何时何地是困难的。
从理解你的商业目标开始。从我的目标开始,这是我在过去两个月里更加关注的事情。当我在解决一个问题时,我不想马上找到解决方案。相反,我需要与 SME 的合作,了解情况的背景以及他们如何看待数据。自从开始做顾问以来,我理解商业目标的策略已经改变了。我们来看一些问题,以及回答这些问题时需要考虑的事项。
为了做好准备,让我们挑选一个精简版的 Kaggle 竞争问题,并假设这是我们给定的业务目标:
预测每个商店销售的产品总量。
这个目标要求什么?
着眼于你的目标,确保你理解企业提出的问题。在本例中,数据是由俄罗斯最大的软件公司之一 1C 公司提供的。了解您的目标的一个方法是与您的主要利益相关者进行演示,并浏览目标和期望的结果。讨论你所知道的,以及你需要澄清或弥补的地方。
乍一看,这个目标似乎很简单——你需要预测每家商店销售的产品总量——但是深入挖掘会发现更多的问题。
回答这个问题需要哪些数据?
经过初步的数据分析,您可以了解您的数据集中有什么和没有什么。有了这些信息,您应该与您的利益相关者讨论如何利用他们当前的数据。同样,你可以讨论什么不是。如果你的目标因为缺少数据而无法实现,这就是你需要让大家知道的时候了。
对于我们的例子,让我们看看数据本身。即使为您提供了每日历史销售数据,这些数据也可能逐月发生变化。商店和产品的列表会发生变化,这意味着创建一个能够处理给定数据的模型会更具挑战性。同样重要的是要注意,我们在这些数据中有唯一的标识符。我们有shop_id、item_id和item_category_id,给你一个独特的商店、产品和物品类别。
这里要注意的另一件事是文本列。给定的目标不会询问这些字段。因此,此分析不需要它们。重要信息包括售出产品的数量、每件商品的当前价格和日期。所有这些都可以在数据集中找到。到目前为止,看起来我们理解了主要目标,并且有可用的数据来实现它,但是最终用户会用这些信息做什么呢?
用户希望用这些数据做什么或实现什么?您的数据能做到这一点吗?
您的用户可能是业务分析师、SME 或其他人。当您为您的目标开发方法时,您需要确定他们将如何利用输出。坐下来与用户交流。分析完成后,他们想用这些数据达到什么目的?当我进行这些对话时,我发现有时候最初的目标并没有涵盖真正想要的东西。这意味着我可能必须返回并确定是否需要任何额外的数据来获得期望的输出。
我们知道我们的示例目标是预测每家商店销售的产品总量,但是如何使用它呢?假设我们正在与利益相关方进行对话,可能会出现以下情况:
- 了解特定商店将订购多少商品有助于确定下个月需要准备多少商品才能发货。我们可以根据已提供的信息来确定该输出。
- 知道有多少不同的项目被购买显示哪些项目是高利润与低利润。了解了这一点,企业就可以决定停止销售什么。如果你同意这种说法,你可能需要更多的数据来了解每个项目的利润率。
- 希望创建一个仪表板,根据一周中的某一天来查看销售项目的趋势。特定商品是否比其他商品更有可能在不同的日子卖出?如果你更深入地挖掘你最初的目标,你可能会发现不同的目标从中浮现出来。如果是这种情况,你需要重新组织你的工作,并确保你有数据。在这个数据集中,我们的数据是按天给出的,因此这种类型的分析可以根据一周中的某一天来分析商品的销售趋势。
我们可以继续,但我想你明白了。当你开始分析你的业务目标时,你可能会发现最初的目标发生了变化或增长。这是完全正常的。我发现如果你允许的话,这些对话可以持续很长时间。我喜欢把这种讨论的开始时间限制在最多一个小时。有了在那次会议中学到的信息,我就可以去修改我的方法,并根据更新寻求反馈。通过这项工作,您和利益相关者都将更好地理解您的数据、您修改的方法以及什么是可能的。
概括起来
业务目标在开始时可能看起来非常简单,直到它们不再简单。这就是为什么在我开始写代码和构建模型之前,我开始问自己三个问题。这些问题有助于确定问题的框架,了解是否有解决问题的数据,并计算出结果。通常,你会发现用户想要的结果并不总是他们最初所说的那样。相反,当你开始深入讨论他们在寻找什么以及数据能告诉他们什么时,他们的愿景会发生变化。这些问题是:
- 这个目标要求什么?
- 回答这个问题需要哪些数据?
- 最终用户希望用这些数据做什么或实现什么?您的数据能做到这一点吗?
在接近业务目标时,你还有其他想问的问题吗?如果是,它们是什么?它们如何帮助您构建解决方案?
如果你想阅读更多,看看我下面的其他文章吧!
有了 MAPIE,不确定性又回到了机器学习中
展示 MAPIE,这是 Quantmetry 开发的 scikit-learn 兼容包,可让您轻松估计与您最喜爱的 ML 模型相关的不确定性

在 25 年的成功故事之后,现代机器学习(ML)仍然与不确定性的概念不一致。置信区间并不流行,在致力于机器学习的顶级开源库中几乎不存在。然而,从商业角度来看,不确定性的概念深深植根于风险管理中,是任何可靠的人工智能(AI)在生产中的必备条件。在这篇文章中,我们介绍了 MAPIE,这是一个开源包,由 Quantmetry 作为 Quantlab R&D 项目开发,重新引入了人工智能中的不确定性概念,由模型无关和用户友好的开源实现提供支持。
人工智能预测总是不确定的
机器学习模型使用历史数据来进行预测。本质上,机器学习模型的预测总是不确定的,因为数据在质量和数量方面都是不完美的。因此,与人工智能模型预测相关的不确定性可分解为两种主要类型:分别因数据质量和数据数量而异的随机和认知不确定性。
任意的不确定性。第一,历史数据总是有噪音的。例如,噪声可能是由于用于测量工业机器的物理尺寸的捕捉器的有限精度造成的。这也可能是由于人类在图像标记过程中不可避免的错误造成的,这对于像医学图像这样的敏感领域来说可能是复杂的。
认知的不确定性。其次,用于训练我们的模型的采集数据总是有限的,并且不能完美地捕捉生活中发现的整体和复杂的真实分布。不完整的分布将导致模型参数的不确定性,这些不确定性将传播到模型预测中。例如,假设您想根据过去一周温度计上显示的温度来预测明天花园的每小时温度。然而,当你睡觉的时候,你不能读出午夜到早上 8 点之间的温度。因此,你的人工智能模型估计的预测将捕捉这种不确定性:它在夜间比在白天更不确定,仅仅是因为夜间缺乏数据!
人工智能对不确定性的需求
人工智能模型需要将不确定性与其预测关联起来,主要有三个原因。首先,让公众对人工智能算法做出的决定放心。第二,更好地确定公司部署的人工智能模型在生产中的限制。第三,为企业的决策和风险管理提供强有力的有用指标。
在自动驾驶或医学成像等敏感领域,估计与人工智能预测相关的不确定性已经是强制性的,在这些领域,人工智能做出的或基于人工智能做出的决定会对人类生活产生直接影响。量化不确定性在其他领域也将很快成为强制性的,因为模型准确性和稳健性的概念在欧洲议会和理事会关于人工智能监管的提案中起着核心作用此处。例如,我们可以引用该提案的第 3.3 节:“这些要求将涉及数据、文档和可追溯性、信息和透明度的提供、人工监督以及稳健性和准确性,并将是高风险人工智能系统的强制性要求。”。因此,评估人工智能模型的不确定性正成为所有处理高风险人工智能系统的公司的监管问题,无论他们的业务线是什么。除了高风险部门,所有认可机构都可以通过建立信任和支持变革管理来利用不确定性量化。
我们开发 MAPIE 的原因
在过去的几十年中,已经开发了各种旨在量化不确定性的方法。我们可以将它们主要分为四类:分位数回归、数据扰动、模型扰动和贝叶斯推断。

回归分析中主要不确定性量化方法的比较。
上表列出了一些方法的主要特点,强调了它们的优缺点。首先,像分位数回归这样的简单方法可以很容易地用来估计任何类型的模型的不确定性,但没有理论上的保证。另一方面,贝叶斯推理等复杂技术使得捕捉不同类型的不确定性成为可能,并得到强大的数学框架的支持,但通常是特定于模型的,计算量大,需要对方法进行深入分析和理解。
允许数据科学家容易地估计与任何类型的 ML 模型的预测相关联的不确定性的健壮包在数据科学界仍然缺乏。例如,scikit-learn 库没有提供量化不确定性的可靠和标准方法,只有少数模型提供了不确定性量化方法(如 50 年代由来已久的刀切法)。
这就是我们开发 MAPIE 的原因,MAPIE 代表模型不可知预测区间估计器。MAPIE 允许您通过一个简单的用户友好的 API,使用您最喜欢的 scikit-learn 兼容 ML 模型轻松估计不确定性。该软件包基于最先进的不确定性量化方法,具有强大的理论保证。目前,MAPIE 只能用于回归问题,但它在分类和其他设置方面的扩展正在积极开发中。
“和梅皮在一起,我确定我的不确定性”
MAPIE 基于 R. Foygel-Barber 等人(2021) [1]在一篇最新研究论文中介绍的重采样方法,用于估计回归设置中的预测区间,并提供了强有力的保证。MAPIE 实现了本文中至少 8 种不同的方法,特别是 Jackknife+和 CV+。
所谓刀切+方法是基于一组留一模型的构建:每一个被扰动的模型都是在去掉一个点的整个训练数据上进行训练的。然后根据这些扰动模型估计的留一残差的分布来估计区间预测。这种优雅方法的新颖之处在于,对新测试样本的预测不再像标准刀切法那样以基础模型估计的预测为中心,而是以每个扰动模型的预测为中心。这个看似微小的变化引发了一个重要的结果:估计的预测区间总是稳定的,并且在理论上是有保证的!

标准重叠和新重叠+策略的比较。
在实践中,当你的目标是 90%的置信区间时,这意味着你希望 90%地确信你的新观察的真实值在你的预测区间内。像标准自助法或刀切法这样的历史方法并不能给你任何保证,而且会有很大的不稳定性。利用这种方法,Foygel-Barber 等人描述的定理保证这种机会总是高于 80%,并且在实践中大多数时候非常接近 90%。换句话说,这意味着大约 90%的新测试样本的目标值将位于用刀切+方法估计的预测区间内。
但是,标准的 Jackknife+方法计算量很大,因为它需要计算与训练样本数量一样多的模型,因此可以采用一种更简单的交叉验证方法,称为 CV+。CV+方法充当标准交叉验证:在去除了每个折叠的整个训练集上训练 K 个扰动模型,其中 K 的范围通常从 5 到 10,并且计算相应的残差。对于 Jackknife+,预测间隔以每个折叠模型执行的预测为中心。因此,该理论保证了相同的稳定性,尽管预测区间通常稍宽,因为每个扰动模型是在较少数量的样本上训练的。
在交叉验证的成本过高的情况下,例如由于模型非常庞大或数据集非常庞大,仍然可以估计预测区间。你只需要把你的训练集分成一个训练文件夹和一个验证文件夹。训练折叠用于训练基础模型,而验证折叠用于通过残差估计来校准预测区间。下图描述了这一过程。

用于训练模型、校准不确定性和评估模型的数据集拆分。
从最先进的研究到最先进的包装
我们在 SimAI R&D 项目的框架内开发了 MAPIE。这个项目是位于巴黎的人工智能咨询公司 Quantmetry 和 ENS Paris-Saclay 在法兰西岛地区的财政支持下合作完成的。这个项目的关键点是开发一个 Python 包,允许数据科学家使用任何类型的 scikit-learn 兼容模型作为基本估计器来轻松估计不确定性。因此,这个包是用最先进的开发标准编写的:100%覆盖率的单元测试,在几个环境中与 Github actions 的持续集成,readthedocs 上的完整文档,以及 PyPi 和 conda 上的包发布。
实践中的 MAPIE
现在,我们以一个简单的x*sin(x)函数生成的一维数据集为例,说明如何利用 MAPIE 轻松估算不确定性,该函数具有正常的同方差(即,其与 x 保持恒定)噪声。数据如下图所示。

我们示例中使用的一维合成数据。
如前所述,MAPIE 可以与任何种类的 sklearn 兼容回归器一起使用。这里,我们使用三种模型比较 CV+方法估计的预测区间:
-
用 scikit-learn 定义的简单多项式函数。
-
一个
XGBRegressor模型,基于著名的并行树提升算法,附带一个 scikit-learn API。 -
一个简单的神经网络,这里是一个具有三个密集层的多层感知器,用 Tensorflow-Keras 定义,并使用
KerasRegressor包装器兼容。
现在让我们使用 MAPIE 来估计使用 CV+方法的预测区间,并比较它们的预测区间。为此,MapieRegressor对象可以像任何 scikit-learn 回归器一样使用标准的fit和predict顺序过程。我们只需要定义基本模型、方法和在初始化对象时估计残差的折叠次数。然后,我们在训练数据上拟合回归量,并在测试集上估计预测和预测区间。这里,我们将 alpha 值设置为 0.05,以便为我们的预测区间实现 95%的置信度。预测间隔的下限和上限保存在单独的 numpy 数组中。
下图比较了 MAPIE 估计的预测区间(由蓝色区域给出)和用于生成数据噪声的真实置信区间(由虚线给出)。

MAPIE 使用三种基本模型估计的预测区间:多项式函数(左)、XGBoost 回归器(中)和多层感知器(右)。
可以看出,用 MAPIE 估计的预测区间确实非常接近真实的置信区间。事实上,有效覆盖分数,即真实值位于预测区间内的测试样本的数量,对于 95%的目标覆盖分数是 97%。
这个例子说明了 MAPIE 在一个非常简单的案例中的使用。当然,我们可以将 MAPIE 应用于更复杂的回归任务,如果您想探索其他问题,我们邀请您查看 MAPIE 文档的图库示例。
关键里程碑和要点
我们已经开始开发 MAPIE,这是一个用 Python 编写的开源包,允许您估计与机器学习模型预测相关的不确定性,具有双重目标。首先,MAPIE 与 scikit-learn 完全兼容,因此可以应用于任何具有 scikit-learn API 的模型。其次,MAPIE 使用最先进的重采样不确定性量化方法,使您能够为预测区间获得强有力的理论保证。
MAPIE 可以通过 pip 或 conda 轻松安装,现在作为 scikit-learn-contrib 项目成为 scikit-learn 生态系统的一部分。我们在 ICML 的一个研讨会上展示了 MAPIE,该研讨会聚焦于无分布不确定性量化。
目前,MAPIE 只能用于单输出回归问题。但是 MAPIE 才刚刚开始它的旅程!我们正在积极地将 MAPIE 扩展到其他设置,如时间序列、多输出回归、多类分类或图像分割。欢迎在 github MAPIE 网页上提交问题或建议适合 MAPIE 的新项目。
加入
我在量子力学公司工作。Quantmetry 自 2011 年成立以来一直是先驱和独立的公司,是法国领先的纯人工智能咨询公司。在提供卓越的数据治理和最先进的人工智能解决方案的愿望的驱动下,Quantmetry 的 120 名员工和研究顾问将他们的热情投入到为所有行业的公司提供高业务成果的服务中。
参考
[1] Rina Foygel Barber,Emmanuel J. Candès,Aaditya Ramdas 和 Ryan J. Tibshirani,用折叠刀+ 进行预测推理(2021)。安。统计学家。, 49(1):486–507.
数据科学中的女性(WiDS)ka ggle 上的 Datathon
我参与 WiDS 的经验和预测糖尿病的有希望的尝试

克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片
第四届年度 WiDS Datathon 关注社会影响,即患者健康,重点关注糖尿病的慢性疾病。
该比赛由 WiDS 全球团队在斯坦福、西部大数据创新中心、 WiDS 数据大会委员会组织,并于 Kaggle 发起。
比赛的数据集由麻省理工学院的 GOSSIS(全球开源疾病严重程度评分)倡议提供。
此外,在线会议将于 2021 年 3 月 8 日举行。顶级数据科学女性之声将亲临现场,就各种主题发表见解。
只是为了达成共识
APACHE —急性生理、年龄和慢性健康评估,美国开发的严重性评分和死亡率估计工具。
ICU —重症监护病房,医院或医疗保健机构中提供重症监护药物的特殊部门。
身体质量指数——身体质量指数,一种基于身高和体重的身体脂肪测量方法。
问题描述
比赛的目标是确定入住 ICU 的患者之前是否被诊断患有糖尿病(一种特殊类型的糖尿病)。
您应该使用在患者重症监护的前 24 小时内收集的数据,建立糖尿病预测模型。
你很可能不是医学背景,所以我会推荐你在这里(美国糖尿病协会)阅读一篇简短的疾病概述。
数据探索
我从datadictionarywids 2021 . CSV文件开始数据探索,详细描述了所有特性。
共有 181 项功能,分为几个类别:
- 人口统计数据
- 阿帕奇协变量
- 阿帕奇共病
- 重要器官
- 实验室
- 实验室血液气体
- 糖尿病标志—目标变量
每个类别提供了每个特性的详细信息,即测量单位、描述、示例等。
在人口统计功能组中,我决定看一看每一个功能,并决定它是否对模型构建有用。
- Age 功能会很有用,但是我们需要在功能工程阶段进一步改造它。
- IBM 的功能对我们来说也很有趣,因为根据美国糖尿病协会的规定,任何体重指数高于 25 的人,无论年龄大小,都应该接受糖尿病筛查。
- 种族应该不会影响糖尿病,而且看起来大多数情况下它只包含一个种族,所以这个不会被进一步使用。

按种族分列的患者人数
- 性别可能有用,但需要使用编码进行转换,因为初始数据集仅包含“F”和“M”值。
- 身高和体重已经包含在 IBM 的计算中,因此不再需要。
- 除了 ICU id 和医院入院来源之外,我不会使用任何与医院或 ICU 类型、、相关的功能。
APACHE 协变量、生命体征、实验室和实验室血气组与各种医学测试的结果相关,因此包含许多内部特征。我决定检查它们和目标变量之间的相关性,而不是使用相关矩阵逐个检查。
对于模型训练,我将使用相关性大于 0.1 阈值的特征。在大多数情况下,选择的特征与葡萄糖浓度和血压有关。

实验室功能组中某些功能的相关矩阵
阿帕奇共病组从一些问卷中获取关于患者是否有某些诊断或病症的信息。该组中没有特征被选择,因为与糖尿病没有线性相关性。

APACHE 共病特征组的相关矩阵
探索数据集中有多少 NA 值也很重要。总的来说,大约有。130k 观测值,使用超过 50k NAs 的特性毫无意义。为它们处理缺失值是没有意义的,因为这会用相同的值创建 50k 个观察值,并使模型更加有偏差。
数据集是半空的。
深入到这个主题,7 个具有显著相关性的特性每个都有超过 70k 的 NAs,因此我们将放弃它们。作为参考,它们是
- h1_bun_min 和*_max,
- h1 _ 肌酐 _ 最小值和* _ 最大值,
- h1 _ 葡萄糖 _ 最小值和* _ 最大值,
- h1_diasbp_invasive_min。
也有四个或更多特征缺失的观测值。在缺失值处理过程中,您将获得只是其他观察的副本的观察。我们有 ca。我删除了 19k 个这样的观察值,以保持数据集中的原始分布。
性别特征缺失值替换为模式。其他 NAs 将被替换为中间值。
应在训练/测试分割后处理缺失值,因为训练数据集可能会影响测试数据集。
特征工程
我使用常识、与目标变量的相关性和 NAs 阈值选择了最终的特征集。他们是
- 年龄,
- bmi,
- icu_id,
- 医院 _ 入院 _ 来源,
- 性别,
- d1_bun_min 和*_max,
- d1 _ 肌酐 _ 最小值和* _ 最大值,
- d1 _ 葡萄糖 _ 最小值和* _ 最大值,
- h1 _ 葡萄糖 _ 最小值和* _ 最大值,
- arf_、bun_、葡萄糖 _*和肌酐 _apache。
如图所示,年龄和诊断为糖尿病的人的百分比之间存在相关性。

年龄特征也可以被分组到箱中。然而,不清楚应该创建多少个箱,以及选择哪一个——固定宽度宁滨还是自适应宁滨。所以你需要试验一下,找到最合适的形式。我添加了年龄特性,没有做任何转换。
所有分类特征都应该使用编码进行转换。例如,具有“F”和“M”值的年龄特征应该转换为具有 0 和 1 值的特征。这可以通过使用 scikit-learn 编码器之一来实现。
模特培训
为了找到最佳参数,我使用了带有两个评分标准的 GridSearch ,并优化了以下参数:n_estimators、max_depth、learning_rate 和 alpha。GridSearch 也使用交叉验证,所以我把折叠数设为 3 (cv=3)。这非常有用,因为这样你就不需要单独运行交叉验证了。
scor = {'AUC': 'roc_auc',
'Accuracy': metrics.make_scorer(metrics.accuracy_score)}
grid_param = {"colsample_bytree": [1],
"learning_rate": [0.01, 0.1, 0.5],
"n_estimators": [250, 300, 500],
"alpha": [0.1, 0.5, 1],
"max_depth": [2, 4, 5, 10]}
model = xgb.XGBClassifier(learning_rate = 0.1, alpha = 0.1)
grid_mse = GridSearchCV(estimator=model, param_grid=grid_param,
scoring=scor, cv=3, verbose=1, refit='AUC')
grid_mse.fit(x_train, y_train)
print("Best parameters found: ", grid_mse.best_params_)
有时参数的最佳值可能是我的网格值的最大值或最小值。所以我用这个参数的不同值再次运行 GridSearch。此外,您可以指定几个评分标准来进行更全面的评估。
也有像 GridSearch 这样的其他选择,例如随机搜索或任何其他方法。
模型评估
看一下学习曲线,很明显曲线之间的差距很小。它表明了我们模型的低方差。训练集中的误差并不是很高,因为该模型的精度约为 0.85。尽管如此,我们希望它变得更好。
在这种情况下,添加更多的观察值对我们没有帮助,因为模型的方差很低。看起来,增加模型的复杂性应该可以改善它,即增加新的功能或使用更复杂的算法。
如果学习曲线与您想要的准确度更高之间有很小的差距,额外的复杂性会有所帮助。

中华民国 UAC 证实了通过探索学习曲线得出的结论。曲线下面积为 0.85,这是一个很好的结果,但还可以改进。

尽管这是一个很好的起点,但模型本身肯定不能给我们所期望的精确度。它可以用作集成模型中的分类器之一。
摘要
总的来说,参与 WiDS 竞赛和研讨会让您有机会学习新方法、提出问题并与志同道合的专业人士讨论解决方案。
竞争。学习。成长。
不管你是数据科学的新手还是老手,你都可以从 WiDS Datathon 委员会找到一些教程来帮助你入门。
单词嵌入技术:Word2Vec 和 TF-IDF 讲解
需要让这些词对机器学习或深度学习算法有意义。因此,它们必须用数字来表示。诸如 One Hot Encoding、TF-IDF、Word2Vec、FastText 之类的算法使单词能够以数学方式表示为用于解决此类问题的单词嵌入技术。

Camille Orgel 在 Unsplash 上拍摄的照片
单词嵌入
单词嵌入技术用于以数学方式表示单词。One Hot Encoding、TF-IDF、Word2Vec、FastText 是常用的单词嵌入方法。根据数据处理的状态、大小和目的,这些技术中的一种(在某些情况下是几种)是首选的。
*一个热编码
用数字表示数据的最基本的技术之一是热编码技术[1]。在这种方法中,按照唯一单词总数的大小创建一个向量。向量的值被分配,使得属于其索引的每个单词的值是 1,而其他的是 0。作为一个例子,可以研究图 1。

图一。一个热编码的样本
在图 1 中,名为“X”的列由 3 个不同的单词组成。当对该列应用一个热编码时,创建了表示每个表达式的 3 个不同的列(换句话说,为每行创建了 3 个单位向量)。对应于每行中单词的列用值 1 填充,其他的用 0 填充。因此,这些表达被数字化了。它通常用于没有太多语言数据多样性的情况,并且不需要表示数据之间的语义和统计关系。
* TF-IDF
TF-IDF 是一种统计方法,用于确定文档中单词的数学意义[2]。矢量化过程类似于热编码。或者,对应于该单词的值被赋予 TF-IDF 值而不是 1。TF-IDF 值通过将 TF 和 IDF 值相乘获得。作为一个例子,让我们找到由 1 个句子组成的 3 个文档的 TF-IDF 值。
[他是沃尔特],
[他是威廉],
[他不是彼得或九月]
在上面的例子中,“他”在所有 3 个文档中使用,“是”在 2 个文档中使用,“或”只在一个文档中使用。根据这些,我们分别求出 TF,然后求出 IDF 值。
- TF(词频)
用最简单的术语来说,术语频率是文档中目标术语的数量与文档中术语总数的比率。如果根据上面的例子计算 TF 值,它将是
[0.33, 0.33, 0.33],
[0.33, 0.33, 0.33],
[0.20, 0.20, 0.20, 0.20, 0.20]
- IDF ( 逆文档频率 )
IDF 值是文档总数与目标术语出现的文档数之比的对数。在这个阶段,这个术语在文档中出现多少次并不重要。确定是否通过就足够了。在这个例子中,要取的对数的底值被确定为 10。但是,使用不同的值没有问题。
“他”:Log(3/3)= 0,
“是”:Log(3/2):0.1761,
“或者,彼得,.”:对数(3/1) : 0.4771
因此,获得了 TF 和 IDF 值。如果使用这些值创建矢量化,首先会为每个文档创建一个向量,该向量由等于所有文档中唯一单词数量的元素组成(在本例中,有 8 个术语)。在这个阶段,有一个问题需要解决。如术语“he”所示,由于 IDF 值为 0,因此 TF-IDF 值也将为零。但是,在矢量化过程中没有包含在文档中的单词(例如,第一句中没有包含短语“Peter”)将被赋值为 0。为了避免混淆,对 TF-IDF 值进行平滑处理以进行矢量化。最常见的方法是在获得的值上加 1。根据目的,可以在以后对这些值应用规范化。如果矢量化过程是根据上述内容创建的;
[1. , 1.1761 , 1.4771 , 0. , 0. , 0. , 0. , 0.],
[1. , 1.1761 , 0. , 1.4771 , 0. , 0. , 0. , 0.],
[1. , 0. , 0. , 0. , 1.4771 , 1.4771, 1.4771 , 1.4771],
* Word2Vec
Word2vec 是另一种常用的单词嵌入技术。扫描整个语料库,并通过确定目标单词更经常出现的单词来执行向量创建过程[3]。这样,单词之间的语义接近度也就显现出来了。例如,让序列中的每个字母..x y A z w..,..x y B z k..和..x l C d m…代表一个单词。在这种情况下,word_A 会比 word_C 更接近 word_B,当在向量构成中考虑到这种情况时,单词之间的语义接近程度就用数学来表示了。

图二。单词相似度
图 2 显示了 Word2Vec 中最常用的图像之一。这些单词之间的语义接近度是向量值彼此之间的数学接近度。经常举的一个例子是“国王-男人+女人=王后”这个等式。这里发生的情况是,作为向量彼此相减和相加的结果而获得的向量值等于对应于“queen”表达式的向量。可以理解,单词“king”和“queen”彼此非常相似,但向量差异仅因其性别而产生。
在 Word2Vec 方法中,与 One Hot Encoding 和 TF-IDF 方法不同,执行无监督学习过程。通过人工神经网络对未标记的数据进行训练,以创建生成单词向量的 Word2Vec 模型。与其他方法不同,向量大小不像语料库中唯一单词的数量那么多。向量的大小可以根据语料库的大小和项目的类型来选择。这对于非常大的数据尤其有益。例如,如果我们假设在大型语料库中有 300 000 个唯一单词,当用一个热编码执行向量创建时,为每个单词创建 300 000 大小的向量,其中只有一个元素的值为 1,其他元素的值为 0。然而,通过在 Word2Vec 侧选择向量大小 300(它可以根据用户的选择或多或少),避免了不必要的大尺寸向量操作。

图 3。Word2Vec 模型中单词的矢量化
“Royal”的矢量化可以在图 3 中看到。如果用一个热编码将五个单词句子中的单词“Royal”矢量化,则获得第一个向量表达式(输入向量)。可以看出,这个向量的大小和句子中的总字数一样多。但是,如果矢量化过程是用 Word2Vec 完成的,这一次将创建一个包含三个单位[5,12,2]的向量。
ka ggle(https://www.kaggle.com/anu0012/hotel-review)中的酒店评论数据集用于应用 Word2Vec 模型训练。作为例子给出的所有代码都可以在这里找到。
因为区分大小写,所以所有单词都转换成小写。然后,清除特殊字符和停用词。nltk 库用于无效单词。如果需要,这些单词也可以完全手动确定。执行这些操作之前的一个例句如下。
“我丈夫和我在这家酒店住过几次。虽然不是最高档的酒店,但我们喜欢这样的事实,我们可以步行大约英里到芬威。它很干净,工作人员也很通融。我唯一的抱怨是浴室里的风扇很吵,而且当你开灯的时候它会自动运转,我们也尽可能地让灯保持关闭。我们住过收费较高的酒店,包括网络和早餐。会再次留在那里。”
数据预处理后会出现的新情况如下。
“丈夫住过的酒店时代虽然最华丽的酒店爱事实步行英里芬威清洁员工住宿投诉范浴室噪音去自动关灯试着保持轻多可能我们住过的酒店收费较高互联网早餐包括住宿”
这些过程完成后,进行 Word2Vec 训练。培训期间使用的参数:
min_count : 目标词在语料库中出现的最小次数。特别是对于非常大的复合病毒,保持这个限制较高会增加更多的成功率。但是,对于小型数据集,保持较小的大小会更准确。
窗口:直接影响目标表达式向量计算的是相邻词的数量。比如“他是一个很好的人。”对于 window =1,单词“a”和“good”在“very”单词向量的形成中是有效的。当 window = 2 时,单词“是”、“一个”、“好”和“人”在创建“非常”单词向量时是有效的。
size : 它是为每个元素创建的向量的大小。
alpha : 初始学习率
min_alpha : 是训练时学习率会线性下降的最小值。
sg : 指定训练算法将如何工作。如果 sg 的值为 1,则使用 skip-gram 算法,否则使用 CBOW 算法。

图 4。Skip-gram vs CBOW
CBOW(连续单词包)和 Skip-gram 算法之间的区别可以在图 4 中看到。在使用 CBOW 算法的训练中,与目标单词相邻的单词作为输入给出,而目标单词本身作为输出获得。在 skip-gram 算法中,目标单词本身作为输入给出,相邻单词作为输出获得。
工作人员:培训可以并行进行。用于此目的的内核数量可以通过 workers 参数来确定。
如果您想通过使用作为训练结果获得的模型来查看单词“great”的向量;
w2v_model["great"]
>>>array([ 3.03658217e-01, -1.56424701e-01, -8.23674500e-01,
.
.
.-1.36196673e-01, 8.55127215e-01, -7.31807232e-01, 1.36362463e-01],
dtype=float32)print(w2v_model["great"].shape)
>>>(300,)
最接近“伟大”、“可怕”、“波士顿”、“代客”的 10 个词如下。
w2v_model.wv.most_similar(positive=["great"])
>>>[('excellent', 0.8094755411148071),
('fantastic', 0.7735758423805237),
('perfect', 0.7473931312561035),
('wonderful', 0.7063912153244019),
('good', 0.7039040327072144),
('amazing', 0.6384587287902832),
('loved', 0.6266685724258423),
('nice', 0.6253951787948608),
('awesome', 0.6186609268188477),
('enjoyed', 0.5889394283294678)]
---------------------------------
w2v_model.wv.most_similar(positive=["terrible"])
>>>[('bad', 0.5275813341140747),
('poor', 0.504431962966919),
('horrible', 0.4722219705581665),
('awful', 0.42389577627182007),
('worst', 0.40153956413269043),
('dirty', 0.3467090427875519),
('disgusting', 0.32588857412338257),
('horrendous', 0.3157917261123657),
('lousy', 0.30114778876304626),
('uncomfortable', 0.3005620837211609)]
---------------------------------
w2v_model.wv.most_similar(positive=["boston"])
>>>[('chicago', 0.519180417060852),
('seattle', 0.5126588940620422),
('dc', 0.4830571711063385),
('bostons', 0.4459514617919922),
('copley', 0.4455355107784271),
('city', 0.44090309739112854),
('newbury', 0.4349810481071472),
('fenway', 0.4237935543060303),
('philadelphia', 0.40892332792282104),
('denver', 0.39840811491012573)]
---------------------------------
w2v_model.wv.most_similar(positive=["valet"])
>>>[('parking', 0.7374086380004883),
('car', 0.6263512969017029),
('garage', 0.6224508285522461),
('retrieving', 0.5173929929733276),
('self', 0.5013973712921143),
('inandout', 0.4847780168056488),
('selfpark', 0.47603434324264526),
('fee', 0.47458043694496155),
('per', 0.4741314947605133),
('parked', 0.4707031846046448)]
*快速文本
FastText 算法的工作逻辑类似于 Word2Vec,但最大的不同是它在训练时也使用了 N 元词[4]。虽然这增加了模型的大小和处理时间,但它也赋予了模型预测单词的不同变化的能力。例如,假设单词“Windows”在训练数据集中,我们希望在训练结束后获得单词“Wndows”的向量。如果将 Word2Vec 模型用于这些操作,将会给出一个错误,即字典中不存在单词“Wndows ”,并且不会返回任何向量。但是,如果 FastText 模型用于此过程,则 vector 将返回,单词“Windows”将是最接近的单词之一。如上所述,训练中不仅包括单词本身,还包括 N-gram 变体(例如单词“Windows”-> Win、ind、ndo、dow、ows 的 3-gram 表达式)。尽管 FastText 模型目前在许多不同的领域中使用,但它经常是首选,尤其是在 OCR 工作中需要单词嵌入技术时。特别是与其他不能容忍最轻微 OCR 错误的技术相比,FastText 在获取不直接在其自身词汇表中的偶数单词的向量方面提供了很大的优势。正因如此,在可能出现用词错误的问题上,它比其他备选方案领先一步。
上述矢量化方法是当今最常用的技术。每一种都有不同的用途。在需要进行单词矢量化的研究中,应该首先确定问题,然后根据这个问题优先选择矢量化技术。事实上,每种技术都有不同的优势。
除此之外,还有语境表征,如 ELMO 和伯特[5]。这些问题将在下一篇文章中讨论。
开源代码库
所有代码都可以在https://github.com/ademakdogan/word2vec_generator找到。在这个项目中,word2vec 训练可以根据任何所需 csv 文件中确定的列自动完成。将作为操作结果创建的 word2vec 模型保存在“model”文件夹下。下面是用法示例。这个项目也可以在 docker 上运行。src/training.py 中的参数可以根据数据大小进行优化。
python3 src/w2vec.py -c </Users/.../data.csv> -t <target_column_name>
Github:【https://github.com/ademakdogan】T4
领英:https://www.linkedin.com/in/adem-akdo%C4%9Fan-948334177/
参照符号
[1]史蒂文斯,S. S. (1946)。《论测量的尺度》。科学,新系列,103.2684,677–680。
[2]会泽明子(2003)。“TF-IDF 测量的信息论观点”。信息处理与管理。 39 (1),45–65。doi:10.1016/s 0306–4573(02)00021–3
[3]托马斯·米科洛夫等人(2013 年)。“向量空间中单词表示的有效估计”。arXiv:1301.3781
[4]阿曼德·朱林,爱德华·格雷夫,皮奥特·博亚诺斯基,托马斯·米科洛夫,(2017)。“有效文本分类的锦囊妙计”,会议:计算语言学协会欧洲分会第 15 届会议论文集:第 2 卷。
[5]德夫林·雅各布,常明伟,李·肯顿,图塔诺娃·克里斯蒂娜,(2018)。“BERT:用于语言理解的深度双向转换器的预训练”
词、子词和基于字符的标记化:了解区别
实践教程
从事 NLP 项目的任何人都应该知道的区别

自然语言处理(NLP) 是人工智能(AI)的一个分支,它为机器(计算机)提供了像人类一样理解书面和口头人类语言的能力。NLP 几乎无处不在,帮助人们完成日常任务。😍这是一项如此普遍的技术,以至于我们经常认为这是理所当然的。一些例子是拼写检查,自动完成,垃圾邮件检测,Alexa 或谷歌助理。NLP 可以被认为是理所当然的,但人们永远不能忘记,机器是与数字而不是字母/单词/句子一起工作的。因此,为了处理互联网上的大量文本数据,我们需要对文本进行处理和清理,我们通常称之为自然语言处理中的文本预处理。
预处理是处理文本和建立模型来解决业务问题的第一步。预处理本身是一个多阶段的过程。在本文中,我们将只讨论标记化和标记化器。那么,我们开始吧。🏄🏼
注: 我们主要关注的是英语。
标记化
标记化是文本预处理中最重要的步骤之一。无论是使用传统的 NLP 技术还是使用先进的深度学习技术,都不能跳过这一步。🙅🏻
记号化简单来说就是将一个短语、句子、段落、一个或多个文本文档拆分成更小单元的过程。🔪这些更小的单元中的每一个都被称为令牌。现在,这些标记可以是任何东西——一个单词、一个子单词,甚至是一个字符。不同的算法在执行标记化时遵循不同的过程,但是下面给出的例子将让您对这三者之间的差异有一个基本的了解。
考虑下面的句子/原文。
“让我们学习记号化。”
一个基于单词的分词算法将把句子分解成单词。最常见的是基于空间的拆分。
【“让”、“我们”、“学”、“标记化”。】
基于子词的记号化算法将把句子分成子词。
【“让”、“我们”、“学”、“令牌”、“化”。】
基于字符的记号化算法将把句子分解成字符。
【" L "、" e "、" t "、" u "、" s "、" L "、" e "、" a "、" r "、" n "、" t "、" o "、" k "、" e "、" n "、" I "、" z "、" a "、" t "、" I "、" o "、" n "、" n "】]
标记实际上是 NLP 的构建块,所有的 NLP 模型都在标记级别处理原始文本。这些标记用于形成词汇表,词汇表是语料库(NLP 中的数据集)中的一组唯一标记。这个词汇表然后被转换成数字(id ),帮助我们建模。😎
我们在这里提到了三种不同的标记化技术。这些技术的工作方式各不相同,各有优缺点。让我们深入了解这些技术的细节。🏇🏻
基于单词的标记化
这是最常用的标记化技术。它根据分隔符将一段文本分割成单词。最常用的分隔符是空格。您也可以使用多个分隔符来拆分文本,如空格和标点符号。根据您使用的分隔符,您将获得不同的单词级标记。
使用定制的正则表达式或 Python 的 split()方法可以很容易地完成基于单词的标记化。除此之外,Python 中还有大量的库——NLTK、spaCy、Keras、Gensim,它们可以帮助您轻松地执行标记化。
示例:
“我不喜欢咖啡是不是很奇怪?”
通过使用空格作为分隔符执行基于单词的标记化,我们得到:
【“是”、“它”、“怪异”、“我”、“不要”、“喜欢”、“咖啡?”]
如果我们看看“不要”和“咖啡?”,我们会注意到这些单词都附有标点符号。如果我们的语料库中有另一个类似这样的原始文本(句子)会怎么样— “我爱咖啡。”这一次将会有一个令牌“咖啡”哪个可以引导模型学习单词 coffee(“coffee?”还有“咖啡”)并将使单词(记号)的表示不是最佳的。🙆🏻
在执行标记化时,我们应该考虑标点符号的原因是,我们不希望我们的模型使用每种可能的标点符号(当然是可以跟在单词后面的标点符号)来学习同一单词的不同表示。如果我们允许我们的模型这样做,我们将会被模型将要学习的表示的数量(一种语言中每个单词×标点符号的数量)所震惊。😳所以,让我们考虑点状。
【“是”、“它”、“wierd”、“我”、“唐”、“'”、“t”、“喜欢”、“咖啡”、“呢?”]
这比我们先前的要好。然而,如果我们注意到,标记化为单词“don”做了三个标记——“don”,“t”。更好的“不做”标记应该是“做”和“不做”,这样,如果模型在未来看到单词“不做”,它会将其标记为“做”和“不做”,因为模型在过去已经学习了“不做”,它会在这里应用它的知识。这个问题听起来很复杂,但是可以通过一些规则来解决。🤓
您一定已经注意到,最新的 NLP 模型有自己的标记化器,因为每个模型使用不同的规则来执行标记化以及使用空格进行标记化。因此,不同 NLP 模型的标记器可以为同一文本创建不同的标记。空格和标点符号,以及基于规则的标记化都是基于单词的标记化的例子。
然后每个单词用一个 ID 表示,每个 ID 包含大量信息,因为句子中的一个单词通常包含大量上下文和语义信息。😲
这种技术听起来令人印象深刻,但是这种类型的标记化会产生大量的语料库,进而产生大量的词汇。😏最先进的型号 Transformer XL ,使用空格和标点符号化,词汇量为 267,735。这是巨大的!这种巨大的词汇规模导致用于输入和输出层的巨大嵌入矩阵,导致模型更重并且需要更多的计算资源。
这种标记化还为英语中几乎相似的单词(一个是单数,另一个是复数)如“boy”和“boys”赋予了不同的 id。我们实际上希望我们的模型知道像这样的单词是相似的。
为了解决这个巨大的词汇问题,我们可以限制可以添加到词汇中的单词数量。例如,我们可以在词汇表中只保存最常见的(基于单词在语料库中出现的频率)5000 个单词。然后,该模型将为这 5000 个常用单词创建 id,并将其余单词标记为 OOV(不在词汇表中)。但是这导致了信息的丢失,因为模型不会学习任何关于 OOV 单词的内容。这对于模型来说可能是一个很大的妥协,因为它将为所有未知单词学习相同的 OOV 表示。🙄
另一个缺点是关于拼写错误的单词。如果语料库中的“知识”被拼错为“knowldge ”,则模型会将 OOV 令牌分配给后面的单词。
因此,为了解决所有这些问题,研究人员提出了基于字符的标记化。
基于字符的标记化
基于字符的记号赋予器将原始文本分割成单独的字符。这种标记化背后的逻辑是,一种语言有许多不同的单词,但有固定数量的字符。这导致词汇量非常小。😍
例如,在英语中,我们使用 256 种不同的字符(字母、数字、特殊字符),而它的词汇表中有将近 170,000 个单词。因此,与基于单词的标记化相比,基于字符的标记化将使用更少的标记。
基于字符的标记化的一个主要优点是不会有或很少会有未知或 OOV 单词。因此,它可以使用每个字符的表示来创建未知单词(在训练期间看不到的单词)的表示。另一个优点是拼写错误的单词可以被正确拼写,而不是将它们标记为 OOV 令牌并丢失信息。
这种类型的标记化非常简单,可以大大减少内存和时间复杂度。那么,它是标记化的最好或最完美的算法吗?🤔答案是否定的(至少对于英语来说)!一个字符通常不像一个单词那样携带任何意义或信息。😕
注: 几种语言的每个字符中都承载着大量的信息。因此,基于字符的标记化在这里会很有用。
此外,在基于字符的标记化中,减小词汇表的大小需要权衡序列长度。每个单词被分成每个字符,因此,标记化的序列比原始文本长得多。例如,单词“知识”将有 9 个不同的标记。🙄
注: 研究者karpathy, 拉德福德等人 , Kalchbrenner 等人,*Lee 等人 已经演示了基于字符的标记化的使用,并得出了一些令人印象深刻的结果阅读这些论文了解更多!*
基于字符的标记化尽管存在一些问题,但已经解决了基于单词的标记化所面临的许多问题。让我们看看我们是否也能解决基于字符的符号化所面临的问题。
基于子词的标记化
另一种流行的标记化是基于子词的标记化,这是一种介于基于词和基于字符的标记化之间的解决方案。主要思想是解决基于单词的标记化(非常大的词汇量,大量的 OOV 标记,以及非常相似的单词的不同含义)和基于字符的标记化(非常长的序列和不太有意义的单个标记)所面临的问题。
基于子词的标记化算法使用以下原则。
- 不要将常用词拆分成更小的子词。
- 将生僻字拆分成更小的有意义的子字。
例如,不应该将“男孩”拆分,而应该将“男孩”拆分为“男孩”和“s”。这将有助于模型了解单词“boys”是由意思略有不同但词根相同的单词“boy”构成的。
在本文的开始,我们将单词“tokenization”分为“token”和“ization”,其中“token”是词根,“ization”是第二个子单词,作为词根的附加信息。子词分割将帮助模型了解与“token”具有相同词根的词,如“tokens”和“tokenizing”在含义上是相似的。它还将帮助模型了解“标记化”和“现代化”由不同的词根组成,但具有相同的后缀“化”,并在相同的句法情况下使用。另一个例子可以是“惊讶”这个词。基于子词的标记化将把它分成“惊奇”和“谎言”,因为这些独立的子词会更频繁地出现。
基于子词的记号化算法通常使用特殊符号来指示哪个词是记号的开始,哪个词是记号开始的结束。例如,“标记化”可以分为“token”和“# #化”,这表明“token”是单词的开始,而“# #化”是单词的结束。
不同的 NLP 模型使用不同的特殊符号来表示子词。“##”由 BERT 模型用于第二个子字。请注意,特殊符号也可以添加到单词的开头。
在英语语言中获得最先进结果的大多数模型使用某种子词标记化算法。几个常见的基于子词的分词算法是 BERT 和 DistilBERT 使用的词块,由 XLNet 和 ALBERT 使用的 Unigram ,以及由 GPT-2 和罗伯塔使用的 Bye-Pair 编码。😊
基于子词的标记化允许模型具有适当的词汇量,并且还能够学习有意义的上下文无关的表示。模型甚至有可能处理一个它以前从未见过的单词,因为分解可以导致已知的子单词。😇 🙌🏻
因此,我们看到了标记化方法如何不断发展,以适应 NLP 领域不断增长的需求,并提出更好的问题解决方案。
参考文献:
- https://huggingface.co/docs/tokenizers/python/latest/
- 文章还提供了相关研究论文的链接。
感谢大家阅读这篇文章。请分享您宝贵的反馈或建议。快乐阅读!📗 🖌
词汇向量和词义
词向量能捕捉意思吗?这取决于“意义”是什么意思。

你知道,意义是什么意思吗?罗斯蒂斯拉夫·萨维钦在 Unsplash 上的照片
这篇文章旨在回答一个简单的问题:单词向量真的能捕捉到单词的意思吗?
答案是……看情况。特别要看你认为一个意义是什么。但是我想得太多了。首先,让我们快速分解一下什么是词向量。然后才能谈最适合他们的意义理论。
什么是词向量?
单词向量试图用数学方法表示单词的意思。从本质上说,计算机会遍历一些文本(理想情况下是大量文本)并计算单词相邻出现的频率。这些频率用数字来表示。因此,如果单词“good”总是出现在单词“friend”的旁边,那么“good”的单词向量的一部分将反映这种联系。一个给定的单词会有大量这样的值,通常是数百个,有时是数千个。
一旦你有了一个单词的这组数字,你就可以比较不同单词的向量。例如,你可以比较“香蕉”、“猕猴桃”和“雨云”这些词的不同向量。由于向量是数学对象,您可以计算不同向量的数值相似性。由于“香蕉”和“猕猴桃”在很多相似的上下文中使用,而不是在“雨云”出现的上下文中使用,它们的向量会彼此更接近,而远离“雨云”的向量
从这里,人们可以进行直观的跳跃,说向量代表单词“香蕉”的意思毕竟,向量可以产生类比(例如,它可以告诉你香蕉对于猕猴桃来说就像土豆对于胡萝卜一样),它可以将香蕉归类到正确的类别中(例如,告诉你香蕉更接近于水果而不是肉或蔬菜),它甚至可以用来标记句子中“香蕉”的正确和错误用法。
但是一组数字真的能代表一个词的全部含义吗?换句话说…
一个词向量是一个意思吗?
在用于生成单词向量的实际代码层面上,我们一直称之为单词的只是一个字符串——一系列字符,除了我们人类之外,没有特定的表示内容。单词向量的目标是模拟我们人类赋予该字符串的代表性内容,以便它不仅可以成为我们的单词,也可以成为计算机的单词。换句话说,我们正试图教计算机‘香蕉’这个词的意思。
现在,有许多不同的意义理论。在这里,我想把重点放在一个具体的例子上,这个例子可以用来支持这样一个观点,即词的向量事实上的确代表了意义。这是哲学家路德维希·维特斯坦根提出的观点。维特根斯坦在他的书《哲学研究》中提出,我们应该把一个词的意义理解为仅仅是这个词如何被使用。我们可以称之为“意义即用途论题”

一个有自己的意义理论的人的脸。摘自维基共享资源。
这听起来可能有点奇怪,或者是显而易见的,所以让我们花点时间把它和其他观点进行比较,这样我们就能明白为什么维特根斯坦的概念是一个突破。在你读到这里之前,如果有人问你一个单词的意思是什么,你会怎么说?也许你会说它是字典里的任何东西。或者你会说这就是这个词的意思。“香蕉”是什么意思?这意味着字面上的物理对象——香蕉。
这些观点有一些严重的问题。毕竟,字典不创造意义,它只是写下单词已经表达的意思;早在第一本字典出现之前,人们就已经在用他们的词来表达意思了。如果这个词的意思就是它所代表的事物,那么一些随机的声音又是如何代表任何事物的呢?为什么“香蕉”指的是实物香蕉,而“蛋糕”指的是蛋糕?这些声音并没有什么特别之处,可以让它们指向不同的方向。
维特根斯坦的意义即使用的观点回避了这两个问题。意义不是声音和物体之间的某种虚无缥缈的联系,也不是韦氏词典流传下来的某种宏大计划;只是社会习俗和日常使用的问题。如果你问我要一杯咖啡,说“bingle bangle bongle”,然后我给你买了一杯咖啡,那么“bingle bangle bongle”的意思是“请给我一杯咖啡。”这就是全部了。
意义即使用和词向量
也许你现在已经有了一些线索,为什么这个意义理论比其他理论对单词向量更友好。如果意义是声音和物体之间的某种神奇联系,那么向量这个词就没有希望捕捉到它。不管你扫描多少条短信,你都找不到那个链接。
然而,单词向量可以准确地告诉你一个单词是如何使用的。事实上,如果计算机已经阅读了所有的文本,听到了你在生活中听到的所有单词,它可能会比你更精确、更清楚地描述这些单词的用法。如果一个单词的意思只是它在句子中的用法,而单词向量可以捕捉到这一点,那么单词向量实际上捕捉到了单词的意思。
当然,维特根斯坦的理论有其自身的问题。特别要注意的是,在他看来,词语只是导航我们这个受社会制约的世界的工具。这意味着它们不一定代表自身之外的任何东西,这可能看起来是反直觉的。但也许一个词向量是否抓住了一个词的意思并不重要;你可能会说,重要的是我们能否让计算机正确使用这个词,或者做出准确的预测。
但是如果你对词向量(或者一般的 NLP)的工作是否真的不仅仅是产生一些功能产品感兴趣,那么花点时间考虑一下什么是意义是值得的。谢谢你花时间和我在一起。
词向量直觉和共现矩阵

照片由@拉斐尔摄影—Unsplash.com 拍摄
如果你对自然语言处理做过一些研究,你可能会偶然发现单词向量的概念。虽然这个概念似乎很直观,但如果你没有数据科学或机器学习方面的知识,可能会有点难以理解。
在这篇文章中,我们将检查构建单词向量背后的一些直觉,并了解当我们谈到几个 NLP 应用程序时,为什么它们是相关的。我们将使用一个简单的共现概念来解释为什么一键向量不是将单词表示为数字的最佳方法。
让我们先用两个词来举例——车和吉普。对于人类来说,这两个词可能有某种程度的相似性,因为我们知道吉普车和汽车有相似的特征——它们都是有发动机的四轮车辆,用于运输或休闲目的。我们可以用这样的短语:
- 吉普车是汽车的一种。
- 我的车是一辆吉普。
- 吉普车类似于其他汽车
- 吉普类似于车,但不是车。

一辆吉普车与其他汽车有一些共同的特征(照片由@ neon brand—Unsplash.com 拍摄)
一个有意思的事情是车和吉普都可能在一句话里做替身:
- 我正骑着我的车去海滩;
- 我正骑着我的吉普车去海滩;
这两个词都被描绘成主体用来走向海滩的物体。这两个词也可以属于车辆的范畴: 通常有轮子和发动机的机器,用于运送人或货物,尤指在陆地上。
我们人类有能力以一种非常简单的方式理解这两个词之间的相似性,因为当我们阅读这两个词时,我们可以在脑海中创建一辆汽车或一辆吉普车的图像。但是计算机怎么能像你我一样理解这两个词之间的这些概念呢?
让我们先做一些基本的实验——在排序单词时,我们能想到的第一个方法是按字母顺序排序。我们现在把字母表看成是某种固有的顺序,所以,让我们天真地假设这可能会把相似的单词组合在一起。
- 汽车以 c 启动
- 吉普始于 j
我们可以想出一大堆介于字母表中的车和吉普之间的单词——比如爸爸、欧洲、决赛、痒。所有这些单词都有完全不同的意思,所以我们可以排除这个想法。
如果我们比较单词的字母呢?有些词表达了相似的意思,有相似的字母,例如:
- 创建和创建
- 书和书架
不幸的是,英语并没有那么简单。吉普和轿车,为例,不共用任何字母。而鹿和深呢?他们共享四个字母中的三个,他们的意思不可能不同。
似乎使用单词中包含的字母是一个微不足道的练习。我们还可以根据上下文,在单词和相似单词之间建立一个巨大的查找表。但是想象一下,为了跟踪特定语言的自然进化而更新那个表会有多混乱。
让我们尝试另一种方法——用更“数学”的方式来看待我们的单词。
一个热点向量方法
直接看我们的信看起来像是一个不会给我们带来任何结果的练习。很自然,随之而来的是某种想法,将我们的文字表示为数字。
将一个单词表示为单个数字会导致我们得到与按字母顺序比较单词相同的结果——我们必须附加一个单词的单个数字。这个数字旁边的单词在某种程度上会被认为是相似的单词,我们仍然会有正确排序的问题。
所以,一个可行的方法是,我们可以用多维数字的方式来表示我们的单词。我们可以使用的一种数据结构是数组(或向量)——例如:
[1, 0, 0, 0, 0]
上面的数组是一个独热向量的例子——一个向量在单个值中包含 1,在其他值中包含 0。这些热点向量可以用来表示我们称之为词汇表的一组单词中的特定单词。
回到我们的例子,让我们想象我们的词汇将由以下单词组成:
- 自行车
- 步行
- 吉普车
- 汽车
- 卡车
- 自行车
看着这些单词,我们可以建立几个心理“集群”:
- 所有这些单词都与一个人从一个地方移动到另一个地方的方法有关。
- 看起来最“关”的词是走这个词
- 人们可以把两轮车(自行车和三轮车)与四轮或四轮以上的车(卡车、轿车和吉普车)区分开来。
我们能否重建数组,以某种方式传达这些单词的细节?
让我们先来整理一下我们的词汇:
bike, bicycle, car, jeep, truck, walk
现在,用我们的单词代替 0,将上面的元素转换成数组格式:
[0, 0, 0, 0, 0, 0]
我们现在可以用一个热点向量来表示我们的单词,例如:
- 自行车用[1,0,0,0,0,0]来表示
- 行走由[0,0,0,0,0,1]表示
这些词向量的主要问题是它们是正交的。我在这里就不跟你讲数学了,但是,这意味着这些向量的点积大约为 0——如果我们以二维方式可视化汽车和吉普车的向量:

一个热点向量的二维表示——由平面图标表示的图标
这些向量之间的角度约为 90 度。这将引起一些麻烦,因为代表汽车和吉普车的向量之间的相似性将与汽车和步行或汽车和自行车之间的相似性完全相同。
我们如何以向量反映相似性的方式来表示这些向量?
共现表示
同现表示帮助你建立单词的上下文。没有什么比语言学家约翰·弗斯的话更能阐明这个概念了:
“从一个人交的朋友就可以知道他说的一句话”
事实上,一个词是由其上下文反映出来的。让我们想象一些简单的句子来反映这一点:
- 我正开车去海滩。
- 我开着我的吉普车去海滩。
- 我的汽车是一辆吉普车。
- 我的吉普车是一辆小汽车。
- 我昨天吃了一根香蕉。
- 我昨天吃了一个桃子。
正如你可能意识到的,意思完全不同的单词在同一上下文中同时出现的可能性极小。让我们称之为单词的上下文,即围绕在特定单词两边的两个单词——例如在句子中,我昨天吃了一个桃子,桃子这个单词被以下单词所包围:
- 昨天吃了一个
在这些单词中,你找到不同于食物的东西的可能性很小。你不会发现下面这句话的可能性:
我昨天吃了一辆吉普车
或者
我昨天吃了一辆自行车
这就是共现表征背后的主要原理——彼此相似的单词往往会一起共现。最常见的共现表示是单个单词的单词表示。让我们检查一下!
共生矩阵
为了构建共现矩阵,我们必须从特定语料库中的完整词汇开始,就像我们在 one-hot vector 部分中所做的那样——让我们看看上面示例中这组句子的词汇,考虑单个 gram:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, yesterday
在上面所有的句子中,我们的词汇由一组不同的单词组成。与 one-hot vectors 类似,我们的向量将由大小为 k、的向量组成,其中 k 是不同单词的数量——让我为单词 car 初始化一个示例向量:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
在我们的句子中,单词 car 出现在三个句子中:
我正骑着我的车去海滩。
我的车是一辆吉普。
我的吉普是一辆车。
我已经标出了句子中所有与单词 car 同时出现的单词。如果我们将 1 加到每个共现的元素上,返回的单词向量会是多少?—更新单词 car 的向量:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [2, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 1, 1, yesterday
0]
这个共现向量现在将表示单词 car。
如果我们为单词 jeep 建立相同的向量会怎么样?查看我们的示例:
我的车是一辆吉普车。
我正骑着我的吉普车去海滩。
我的车是一辆吉普。
产生的共现向量将是:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [2, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 1, 1, yesterday
0]
如果你注意到了,这个向量和我们对单词 car 的向量完全一样。发生这种情况是因为,在我们的句子中,这些单词往往与相同的单词同时出现。这似乎是一个有前途的方法!
如果我们检查单词 banana 的向量,共现向量完全不同:
我昨天吃了一根香蕉。
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, yesterday
1]
我们为单词 banana 生成的向量与为 Jeep 和 Car 生成的向量完全不同。
我几乎能听到你在想——“*等一下……这些例子似乎都是为了符合解释*”而吹毛求疵。**
这倒是真的!为了检查这在更大的文本中是否有意义,让我们在维基百科的一篇文章上做一个快速实现。
之后我们将学习如何量化向量之间的关系(提示:相似性度量!)
大文章单词共现
让我们直接进入 Python 实现,在这里我们将提取维基百科美利坚合众国文章中所有单词的共现向量— 首先,让我们检索数据并将其标记化(不要担心代码,我们最后会有一个要点!):
def retrieve_page(page_name: str) -> list:
'''
Retrieves page data from wikipedia
and stores words in lower case format in
a list - tokenized format.
'''
usa_article = wikipedia.page(page_name)
# Strip puncuation from page
usa_article = (
usa_article.content.translate(str.maketrans('', '', string.punctuation))
)
# Lower text case
usa_article = usa_article.lower()
# Tokenize using NLTK word tokenizer
usa_article_token = word_tokenize(usa_article)
return usa_article_tokenusa_article_token = retrieve_page('United States of America')
****我们的 usa_article_token 以标记化的格式包含了整个维基百科文章——让我们看看文章的第一段:

美国维基百科文章的第一段
从文章中提取所有句子后,我们能够理解每个单词的上下文——在上面的文本中,单词平方(第四行)具有以下上下文,去掉标点符号: 3.8,百万,英里,9.8
鉴于此,我们现在可以为文本中的每个单词构建共现向量,让我们编码吧!
从构建我们的词汇开始,词汇将由我们的共现向量的大小组成:
def build_vocabulary(page:list) -> list:
'''
Builds vocabulary with all the words
present in the list page.
'''
vocab = list(set(page))
vocab.sort()
vocab_dict = {}
for index, word in enumerate(vocab):
vocab_dict[word] = index
return vocab_dictvocab_dict = build_vocabulary(usa_article_token)
警告:由于维基百科文章的更新,当您运行这段代码时,词汇表的大小可能会有所不同。
我们的词汇表中有大约 3.640 个单词——我们的 vocab_dict 对象包含单词和索引之间的映射——这个索引将使我们能够更快地填充共现向量。
将我们的同现矩阵初始化为 0-我将使用熊猫数据框来简化可视化:
co_ocurrence_vectors = pd.DataFrame(
np.zeros([len(vocab_dict), len(vocab_dict)]),
index = vocab_dict.keys(),
columns = vocab_dict.keys()
)
这是我们初始化的共现矩阵的大致外观——行由单词 i 的索引组成,列由单词 j 与单词 i. 共现 n 次组成

现在,根据上下文,每当我们在我们在行中看到的单词的上下文中看到特定的单词时,我将添加 1 。例如,在本文中,单词表示应该与单词 United 以及可能的 America 同时出现。****
使用 Python 更新矩阵:
def build_context(
page:str,
co_ocurrence_vectors: pd.DataFrame
) -> pd.DataFrame:
'''
Updates co-ocurrence vectors based on
text read from the page.
'''
for index, element in enumerate(page):
# Build start and finish of context
start = 0 if index-2 < 0 else index-2
finish = len(page) if index+2 > len(page) else index+3 # Retrieve Context for word
context = page[start:index]+page[index+1:finish]
for word in context:
# Update Co-Occurrence Matrix
co_ocurrence_vectors.loc[element, word] = (
co_ocurrence_vectors.loc[element, word]+1
)
return co_ocurrence_vectorsco_ocurrence_vectors = build_context(usa_article_token, co_ocurrence_vectors)
现在,我们可以从我们的共现矩阵中提取与状态共现更多的词,例如前 10 个词:
co_ocurrence_vectors.loc['states'].sort_values(ascending=False).head(10)

与州共现的前 10 个单词
是与文本共现次数最多的第一个词——在我们的文本中出现了 189 次(在两个邻居的窗口中),其次是 united 和 is。另一方面,美国是第九个——如果你注意到这篇文章,美国在整篇文章中被用作美利坚合众国的别名,所以,这是有道理的。**
爽!所以我们现在有了 3.640 个单词的多维向量。
我们如何衡量它们的相似性?
余弦相似性
测量两个向量之间相似性的最常见方法之一是使用余弦相似性——测量任意向量A 和向量 B 之间的相似性:**

余弦相似性公式(来自维基百科)
在我们的例子中,我们将有 3.640 个元素
余弦相似性为我们提供了一种相似性度量,其范围在以下值之间:
- 1 如果向量真的相似;
- 如果向量没有任何关系(正交向量!);
- 如果向量相反,则为-1;
按逻辑,相似的词会有更高的余弦相似度。如果向量完全相同,则余弦相似度将为 1。让我们看看维基百科文章中的一些例子——搜索与单词 States — 具有高余弦相似度的单词。首先,我们需要使用 scikit-learn 实现 : 在文章中的每个单词之间创建余弦相似度
**similarity_words = pd.DataFrame(
cosine_similarity(co_ocurrence_vectors),
columns = vocab_dict.keys(),
index = vocab_dict.keys()
)**
就相似性而言,检查顶部单词与单词的状态:**

有些词看起来确实有道理——比如国家、王国和宪法。其他词有点偏— 离开,的,在— 但是,即便如此,这些结果还是很有意思。****
让我们检查另一个词,如中国:**

同样,有些词似乎很有意义,比如菲律宾、加拿大和墨西哥等其他国家——但也有一些词看起来很“奇怪”,因为它们与“中国”这个词很接近。
当然,我们只使用了维基百科中的一篇文章,所以,我们并不期待完美。如今,共现矩阵是很好的第一手资料,可以很好地掌握单词向量背后的必要性和直觉。
使用共现向量有一些缺点,即:
- 同现向量变得很大,很快。你的词汇量越大,你的向量就越大。
- 大多数共生矩阵包含零和无信息。您可以通过使用稀疏格式来解决空间问题,但即使如此,一些罕见的单词可能具有非常糟糕的向量表示。
- 共现向量无法把握对立词的概念。
其他技术已经被用于表示单词向量,例如包含压缩版本的 Word2Vec 和可能的隐藏的共现表示,后者概括得更好一些。我们将把它留给下一篇文章!**
以下是这篇文章代码的要点:
感谢你花时间阅读这篇文章!我希望你喜欢它,并随时添加我到 LinkedIn 或者给我发消息!
这个例子摘自我的 NLP 课程,该课程面向 Udemy 平台上的绝对初学者——该课程非常适合初学者和希望学习自然语言处理基础知识的数据科学家。该课程还包含 50 多个编码练习,使您能够在学习新概念的同时进行练习。******
****https://ivopbernardo.medium.com/membership ****
Word2Vec 解释道
解释 Word2Vec 的直观性&用 Python 实现它

这张照片是由 Unsplash 的 Raphael Schaller 拍摄的
目录
- 介绍
- 什么是单词嵌入?
- Word2Vec 架构
- CBOW(连续单词包)模型
-连续跳格模型
- CBOW(连续单词包)模型
- 实现
-数据
-需求
-导入数据
-数据预处理
-嵌入
-嵌入的 PCA - 结束语
- 资源
介绍
Word2Vec 是 NLP 领域的最新突破。 Tomas Mikolov 捷克计算机科学家,目前是 CIIRC ( 捷克信息、机器人和控制论研究所)的研究员,是 word2vec 研究和实现的主要贡献者之一。单词嵌入是解决自然语言处理中许多问题不可缺少的一部分。它们描述了人类如何理解机器的语言。你可以把它们想象成文本的矢量表示。Word2Vec 是一种常见的生成单词嵌入的方法,具有多种应用,如文本相似性、推荐系统、情感分析等。
什么是单词嵌入?
在进入 word2vec 之前,让我们先了解一下什么是单词嵌入。了解这一点很重要,因为 word2vec 的整体结果和输出将是与通过算法传递的每个唯一单词相关联的嵌入。
单词嵌入是一种将单个单词转换成单词的数字表示(向量)的技术。其中每个单词被映射到一个向量,然后这个向量以类似于神经网络的方式被学习。向量试图捕捉该单词相对于整个文本的各种特征。这些特征可以包括单词的语义关系、定义、上下文等。有了这些数字表示,你可以做很多事情,比如识别单词之间的相似或相异。
显然,这些是机器学习各个方面的输入。机器不能处理原始形式的文本,因此将文本转换成嵌入将允许用户将嵌入馈送到经典的机器学习模型。最简单的嵌入是文本数据的一个热编码,其中每个向量将被映射到一个类别。
For example: have = [1, 0, 0, 0, 0, 0, ... 0]
a = [0, 1, 0, 0, 0, 0, ... 0]
good = [0, 0, 1, 0, 0, 0, ... 0]
day = [0, 0, 0, 1, 0, 0, ... 0] ...
然而,像这样的简单嵌入有多种限制,因为它们不能捕获单词的特征,并且它们可能非常大,这取决于语料库的大小。
Word2Vec 架构
Word2Vec 的有效性来自于它能够将相似单词的向量组合在一起。给定一个足够大的数据集,Word2Vec 可以根据单词在文本中的出现次数对单词的含义做出强有力的估计。这些估计产生了与语料库中其他单词的单词关联。例如,像“国王”和“王后”这样的词彼此非常相似。当对单词嵌入进行代数运算时,你可以找到单词相似性的近似。例如,“国王”的 2 维嵌入向量-“男人”的 2 维嵌入向量+“女人”的 2 维嵌入向量产生了与“皇后”的嵌入向量非常接近的向量。注意,下面的值是任意选择的。
King - Man + Woman = Queen
[5,3] - [2,1] + [3, 2] = [6,4]

你可以看到 King 和 Queen 这两个词的位置很接近。(图片由作者提供)
word2vec 的成功主要得益于两种架构。跳跃图和 CBOW 架构。
连续单词袋
这种架构非常类似于前馈神经网络。这种模型架构本质上试图从上下文单词列表中预测目标单词。这个模型背后的直觉非常简单:给定一个短语"Have a great day",我们将选择我们的目标词为“a”,我们的上下文词为[“have”、“great”、“day”]。这个模型要做的是利用上下文单词的分布式表示来尝试和预测目标单词。

CBOW 架构。图片取自向量空间中单词表示的有效估计
连续跳格模型
skip-gram 模型是一个简单的神经网络,具有一个经过训练的隐藏层,以便在输入单词出现时预测给定单词出现的概率。直觉上,你可以想象跳格模型是 CBOW 模型的对立面。在这种架构中,它将当前单词作为输入,并尝试准确预测当前单词前后的单词。该模型本质上试图学习和预测指定输入单词周围的上下文单词。基于评估该模型准确性的实验,发现在给定大范围的词向量的情况下,预测质量提高了,然而这也增加了计算复杂度。该过程可以直观地描述如下。

为 skip-gram 模型生成训练数据的示例。窗口大小为 3。图片由作者提供
如上所述,给定一些文本语料库,在一些滚动窗口上选择目标单词。训练数据由该目标单词和窗口中所有其他单词的成对组合组成。这是神经网络的最终训练数据。一旦模型被训练,我们基本上可以产生一个单词成为给定目标的上下文单词的概率。下图显示了 skip-gram 模型的神经网络体系结构。

跳格模型架构(图片由作者提供)
语料库可以表示为大小为 N 的向量,其中 N 中的每个元素对应于语料库中的一个单词。在训练过程中,我们有一对目标和上下文单词,输入数组中除目标单词外的所有元素都为 0。目标字将等于 1。隐藏层将学习每个单词的嵌入表示,产生 d 维嵌入空间。输出层是具有 softmax 激活功能的密集层。输出层基本上会产生一个与输入大小相同的向量,向量中的每个元素都由一个概率组成。这个概率指示了目标单词和语料库中的关联单词之间的相似性。
对于这两个模型的更详细的概述,我强烈推荐阅读概述这些结果的原始论文这里。
履行
我将展示如何使用 word2vec 来生成单词嵌入,并通过 PCA 使用这些嵌入来查找相似的单词和嵌入的可视化。
数据
出于本教程的目的,我们将使用莎士比亚数据集。你可以在这里找到我在本教程中使用的文件,它包括了莎士比亚为他的剧本写的所有台词。
要求
nltk==3.6.1
node2vec==0.4.3
pandas==1.2.4
matplotlib==3.3.4
gensim==4.0.1
scikit-learn=0.24.1
注意:因为我们正在使用 NLTK,你可能需要下载下面的语料库来完成本教程的剩余部分。这可以通过以下命令轻松完成:
import nltk
nltk.download('stopwords')
nltk.download('punkt')
输入数据
注意:将**PATH**变量更改为您正在处理的数据的路径。
预处理数据
停用词过滤注释
- 请注意,从这些行中删除的停用词是现代词汇。应用程序和数据对于清理单词所需的预处理策略的类型非常重要。
- 在我们的场景中,单词“you”或“yourself”将出现在停用词中,并从行中删除,但是由于这是莎士比亚文本数据,因此不会使用这些类型的单词。相反,“你”或“你自己”可能是有用的删除。保持对这些类型的微小变化的热情,因为它们对好模型和差模型的性能产生了巨大的差异。
- 出于这个例子的目的,在识别不同世纪的停用词时,我不会涉及太多细节,但是请注意,您应该这样做。
把…嵌入

莎士比亚资料中与 thou 最相似的单词(图片由作者提供)
嵌入的主成分分析

彼此相似的单词将被放置在彼此更靠近的地方。图片由作者提供
Tensorflow 对 word2vec 模型做了非常漂亮、直观和用户友好的表示。我强烈建议您探索它,因为它允许您与 word2vec 的结果进行交互。链接在下面。
https://projector.tensorflow.org/
结束语
词嵌入是解决自然语言处理中许多问题的重要组成部分,它描述了人类如何理解机器语言。给定一个大的文本语料库,word2vec 产生一个与语料库中的每个单词相关联的嵌入向量。这些嵌入的结构使得具有相似特征的单词彼此非常接近。CBOW(连续单词包)和 skip-gram 模型是与 word2vec 相关的两个主要架构。给定一个输入单词,skip-gram 将尝试预测输入上下文中的单词,而 CBOW 模型将采用各种单词并尝试预测缺失的单词。
我还写过 node2vec,它使用 word2vec 生成给定网络的节点嵌入。你可以在这里读到它。
资源
- https://arxiv.org/pdf/1301.3781.pdf
- https://www . kdnugges . com/2019/02/word-embeddings-NLP-applications . html
- 【https://wiki.pathmind.com/word2vec
- https://projector.tensorflow.org/
如果您喜欢阅读这篇文章,请考虑关注我的后续文章,了解其他数据科学材料以及解决数据科学不同领域相关问题的材料(如 word2vec)。这里有一些我写的其他文章,我想你可能会喜欢。
[## 贝叶斯 A/B 测试解释
towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)
Word2Vec 研究论文解释

作者图片(经过语义和句法关系训练的 Word2Vec 向量)。
对 word2vec 模型的直观理解和解释。
我们知道什么是 Word2Vec,以及词向量在 NLP 任务中是如何使用的,但是我们真的知道它们是如何被训练的,以及以前训练词向量的方法是什么吗?好吧,这里尝试解释一下我对 Word2Vec 研究论文【T. Mikolov 等人】的理解。你可以在这里找到研究论文。
1。简介
在许多 NLP 应用程序中,单词被表示为“一键编码”,这种编码不能捕捉单词之间的关系。选择独热编码的原因是
简单、稳健,观察到基于大量数据训练的简单模型优于基于较少数据训练的复杂系统。[T. Mikolov 等人]
简单模型的一个例子可以是 N 元模型。这些模型是马尔可夫模型,假设在第 I 个位置的单词依赖于从第 i-(n-1)个单词直到第(i-1)个单词的单词历史。它基本上是基于频率的方法,来源于训练语料库。但是这些简单的模型需要高质量的训练数据,而这些数据通常是不可用的,并且它们不能很好地概括看不见的数据。
然而,随着机器学习领域的改进,在大得多的数据集上训练的复杂算法比简单的模型表现得更好。
Word2vec 模型捕捉单词之间的句法和语义相似性。关于训练的 word2vec 向量的向量代数的一个众所周知的例子是
向量(“国王”)-向量(“男人”)=向量(“女王”)-向量(“女人”)。
2。用于单词的矢量表示的先前方法
这一部分集中于使用神经网络的单词的矢量表示的早期方法。
2。1 神经网络语言模型(NNLM) 【本吉奥,约舒厄等】
这是一个两步语言模型,包括用于单词嵌入的线性投影层和非线性隐藏层,后面是输出层。在 NNLM 中同时训练单词向量和语言模型。

NNLM 建筑模型。图片来自 NNLM 研究论文。[ 来源
NNLM 模型由输入层、投影层、隐含层和输出层组成。超参数 N 决定在下一个单词预测中应该考虑多少来自当前单词的历史单词。在输入层,单词被编码为一个词汇大小为 v 的热编码。矩阵 C 被共享维数为 N × D 的投影矩阵,其中 D 是嵌入大小。隐藏层紧密地连接到投影层,这导致从投影到隐藏层的 N×D×H 的权重矩阵,其中 H 是隐藏层大小。生成隐藏向量后,输出层的权重为(H×V ),后跟一个 softmax 层。因此,NNLM 涉及的总权重为(N×D+N×D×H+H×V)。使用分层 soft max【f . Morin】可以将 Term (H×V)简化为 H×log2(V),其中词汇表示为二叉树。当 N×D > V 时,大部分复杂性来自于(N×D×H)项,这在 word2vec 模型中通过在模型中不使用隐藏层而得以避免。当训练 NNLM 模型时,投影矩阵被用作训练的词向量的查找表。
2.2 递归神经网络语言模型(rnn lm)【t . miko lov 等】
在这个模型中,不需要像 NNLM 模型那样指定 N。RNN 对隐藏状态中所有单词的过去历史进行编码。隐藏状态 S(T)用当前输入 X(T)和 S(T-1)处的隐藏向量来更新。

RNNLM 模型架构。图片来自 RNNLM 研究论文。[ 来源
简单 Elman 网络被用作 RNNLM 中的 RNN 模型,其中输入是 X(T)和 S(T-1)的简单级联,X(T)是 T 处的输入令牌,S(T-1)表示(T-1)处的隐藏/上下文向量。该网络中仅涉及时间维度中的递归权重和对输出层隐藏的权重,导致模型的整体复杂度为(H×H+H×V)。注意,与 NNLM 模型相比,模型复杂性中没有 N 项。使用分层 softmax,可以将(H×V)项进一步简化为 H×log2(V)。在这个模型中,计算瓶颈项是(H×H)。
从上述两个模型中可以明显看出,“非线性隐藏层”是整体复杂性最大的原因。由于硬件限制,这对于不利用所有的训练数据数据施加了限制。因此,Word2vec 提出了两个更简单的模型,可以用更大的数据集进行训练,而不会影响单词向量的质量。
3。Word2Vec 型号
本节介绍用于训练 word2vec 的模型。
- 连续文字袋模型(CBOW)
CBOW 模型是由 NNLM 模型衍生而来的,只是没有线性隐含层。CBOW 模型的目标函数是在给定过去 N/2 个历史单词和 N/2 个未来单词的情况下预测中间单词。使用 N=8 可获得最佳结果。在投影层中,简单地平均 N 个上下文单词的单词向量。在确定中间单词的单词向量时,单词的位置没有相关性,因此命名为“单词包”。术语连续的代表向量空间 d

CBOW 模型架构。图片取自 Word2Vec 研究论文。【来源】
一个平均向量被传递到输出层,然后是分层 softmax,以获得 v 上的分布。CBOW 是一个简单的对数线性模型,其中模型输出的对数可以表示为模型权重的线性组合。训练 CBOW 模型所涉及的总权重是 N×D+D×log(2)V
2.连续跳格模型
该模型与 CBOW 模型的目标相反。给定当前单词,它预测历史和未来的邻近上下文单词。顾名思义,模型预测除当前单词之外的 N 个单词作为模型的输入,因此得名 skip-gram。N 的值被选择为 10。由于距离较远的单词与当前单词不太相关,因此在生成输出标签时,与距离较近的单词相比,对它们的采样较少。当 N=10 时,利用上述采样策略产生 1 到 10 之间的随机数 R,并且 R 个历史和未来单词被用作 skip-gram 模型的正确标签。

跳格模型体系结构。图片取自 Word2Vec 研究论文。【来源】
该模型的总复杂度为 N×D+N×D×log2(V)。注意,N 也乘以 D×log2(V)项,因为与 CBOW 相比,它不是单个类别分类问题,而是 N 个类别分类问题。因此,skip gram 模型的总体复杂性大于 CBOW 模型。
4。结果
本节讨论了由 word2vec、NNLM 和 RNNLM 模型获得的结果。
用于评估已训练单词向量质量的测试集准备:-
早期的方法以表格形式显示了几个相似词的例子,而 word2vec 模型准备了一个全面的测试集,该测试集具有词之间的五个语义关系和九个句法关系。货币:国家,城市:州,男人:女人,首都:国家是测试集中单词之间语义关系的一些例子。单词的反义词、最高级、现在分词、过去时态和单词本身具有句法关系。问题是从测试集中单词的语义和句法关系列表中准备的。一个这样的例子——“在最大和大相似的意义上,和小相似的词是什么?”big 和 maximum,small 和 small 之间有一个最高级的句法关系。从向量代数 X =向量(“最大”)-向量(“大”)+向量(“小”)中找到上述问题的答案。使用余弦距离相似度最接近 X 的单词向量被预测为答案。预测输出只有在与输出正确匹配时才是正确匹配。在评估中,输出的同义词仍被视为不正确的匹配。在测试集上使用准确性度量。
4.1。准确率 v/s 词向量维数和训练语料库规模

表 1:具有不同的训练数据词汇大小和单词嵌入向量大小的测试数据集子集的准确度。
如表 1 所示,随着单词向量和训练语料库大小维度的增加,准确性在两个方向上都增加,而与训练语料库大小相比,随着嵌入大小的增加,增益减小。
培训标准:
- 数据-带有 6B 标记的谷歌新闻语料库。基于频率,词汇量限制在 1M 以内。
- 历元数:3
- 优化器:随机梯度下降
- 初始学习率:0.025,线性衰减。
4.2。准确性 v/s 模型架构
这里,训练数据和向量维数保持不变,并且比较不同模型架构的性能。
培训标准
- Data - LDC 语料库,320 万单词,82K 词汇
- 向量尺寸:640

表 2:不同模型架构的测试集精度。
RNNLM 模型对于语法查询工作得更好,而对于语义查询性能较差。与 RNNLM 模型相比,NNLM 模型在语义和句法性能上有很大的提高,因为 RNNLM 模型更简单并且不能更好地泛化。CBOW 比 NNLM 模型工作得更好,用更少的训练时间在句法测试集上取得了最好的结果。与 CBOW 相比,Skip-gram 模型在语义集上取得了最好的结果,但语法性能略有下降。
4.3。大规模平行训练
Word2vec 模型也使用了 DistBelief 分布式框架【Jeffrey Dean】进行模型的大规模并行训练。由于 word2vec 模型的复杂性较低,因此使用 dist faith 分布式训练在巨大的语料库上训练模型,这加快了训练过程。NNLM、CBOW 和 Skip-gram 模型分别在具有 100、1000 和 1000 个词向量维度的 6B 标记的 Google 新闻语料库上进行训练。训练时间取决于模型的复杂程度。由于 NNLM 模型具有很高的复杂度,相比于 CBOW 和 Skip-gram 的 1000 维,我们选择了 100 维的词向量。令人惊讶的是,CBOW 和 skip-gram 的单词向量比 NNLM 模型训练得更快。Skip-gram 使用小批量异步梯度下降更新和 Adagrad optimizer 使用 DistBelief 在 2.5 天的训练时间内实现了 65.6%的最高测试准确度。

表 3:dist faith 框架上的模型性能比较。
5。习得关系的例子
单词“A”和“B”之间的关系向量(“R”)是通过从单词向量(“B”)中减去单词向量(“A”)而获得的。最接近 vector("C")+vector("R ")的单词 vector 与 C 的关系与 A 和 b 之间的关系相同。

表 4:使用 D=300 的 skip-gram 模型的单词之间关系的例子。
来自 skip-gram 模型的单词向量之间的训练关系的一些有趣的例子在表 4 中示出。人物:职业爱因斯坦:科学家梅西:中场和毕加索:画家之间的关系如下。
6。总结
Word2vec 向量可以用 CBOW 或 Skip-gram 方法训练。由于模型的复杂度较低,它们在大规模的语料库上进行训练,并使用更高维的向量,以获得更好的词向量的连续表示。在 NLP 任务中使用 word2vec 模型有很多有趣的应用。这些向量在构建神经网络模型时充当单词的特征。单词之间的相似性,不在列表中的单词问题也可以使用 word2vec 解决。
7。用 Python 实现 Word2vec 模型。
- word2vec 的 Gensim 实现:-
https://radimrehurek.com/gensim/models/word2vec.html
- word2vec 的 Keras 实现:-https://github . com/abaheti 95/Deep-Learning/tree/master/word 2 vec/keras
- word2vec 的 Tensorflow 实现:-
https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/2_BasicModels/word2vec.py
- Pytorch 实现 word 2 vec:-https://github.com/Andras7/word2vec-pytorch
8。资源
[1]原创研究论文——向量空间中单词表征的高效估计:【https://arxiv.org/pdf/1301.3781.pdf】
【2】一篇神经概率语言模型研究论文:https://ai . Google blog . com/2016/06/wide-deep-learning-better-together-with . html
【3】基于递归神经网络的语言模型研究论文:https://www . isca-speech . org/archive/archive _ papers/inter seech _ 2010/i10doi = 10 . 1 . 1 . 221 . 8829&rep = rep 1&type = pdf # page = 255
感谢你花时间阅读这篇文章。我希望它有帮助。请让我知道你的想法/意见。如果您有任何疑问和建议,请随时联系我。
使用 PyTorch 的 Word2vec:实现原始论文
思想和理论
涵盖所有实施细节,跳过高层概述。代码附后。

作者图片
词语嵌入是深层自然语言处理中最基本的概念。word2vec 是最早用于训练单词嵌入的算法之一。
在这篇文章中,我想更深入地了解关于 word2vec 的第一篇论文— 向量空间中单词表示的有效估计 (2013),截至目前,该论文已被引用 24k 次,并且这个数字仍在增长。
我们的计划如下:
- 回顾论文中描述的模型架构;
- 使用 PyTorch 从零开始训练 word2vec 模型;
- 评估我们得到的单词嵌入。
我附上我的 Github 项目与 word2vec 培训。我们将在这篇文章中讨论这个问题。
今天我们只复习 word2vec 的 第一 篇。然而,有几篇后来的论文,描述了 word2vec 的演变:
- 单词和短语的分布式表示及其组合性 (2013)描述了对原始 word2vec 的几个扩展,以加速训练并提高嵌入质量。
- 句子和文档的分布式表示 (2014)展示了如何使用 word2vec 背后的思想来创建句子和文档嵌入。这种方法被称为 doc2vec。
- 用子词信息丰富词向量 (2017)为 word2vec 引入了更多扩展。这种方法在字符级别(而不是像以前那样在单词级别)上操作,被称为 fastText。
我相信,如果你理解了第一篇文章,你会很容易理解后面文章中描述的观点。所以我们走吧!
披露。Wor2vec 已经是一个老算法了,还有更新的选项(例如, 伯特 )。这篇文章是为那些刚刚开始深入 NLP 之旅的人,或者那些对阅读和实现论文感兴趣的人写的。
内容
—什么是 word2vec?
—模型架构
—数据
—数据准备
—用 PyTorch 进行文本处理
—训练细节
—检索嵌入
—用 t-SNE 可视化
— —相似词
— —国王—男人+女人=女王
—接下来呢?
word2vec 是什么?
以下是我的三句话解释:
- Word2vec 是一种创建单词嵌入的方法。
- 单词嵌入是将单词表示为数字向量。
- 除了 word2vec,还存在其他方法来创建单词嵌入,例如 fastText、GloVe、ELMO、BERT、GPT-2 等。
如果您不熟悉单词嵌入的概念,下面是几个重要资源的链接。跳过细节,但抓住背后的直觉。并返回到我的帖子中查看 word2vec 的详细信息和代码。
- 为什么我们在 Natasha Latysheva 的 NLP 中使用单词嵌入
- 杰伊·阿拉姆马的插图版 Word2vec
- 谢恩·林恩的《文本分析中的单词嵌入介绍》
现在好些了吗?
词嵌入实际上用在每个自然语言处理任务中——文本分类、命名实体识别、问题回答、文本摘要等。到处都是。模型不理解单词和字母,它们理解数字。这就是单词嵌入派上用场的地方。
模型架构
Word2vec 基于这样一种思想,即单词的含义是由其上下文定义的。上下文被表示为周围的单词。
想想吧。假设,你正在学习一门新的语言。你正在读一个句子,所有的单词你都很熟悉,除了一个。你以前没见过这个词,但你可以很容易地说出它的词性,对吗?有时候,甚至猜测它的意思。那是因为周围单词的信息对你有帮助。
对于 word2vec 模型,上下文被表示为当前单词之前的 N 个单词和之后的 N 个单词。n 是一个超参数。使用较大的 N,我们可以创建更好的嵌入,但同时,这样的模型需要更多的计算资源。在最初的论文中,N 是 4–5,在我下面的可视化中,N 是 2。

图片 1。一个词和它的上下文。作者图片
本文提出了两种 word2vec 架构:
- CBOW(Continuous Bag-of-Words)——基于上下文单词预测当前单词的模型。
- Skip-Gram —基于当前单词预测上下文单词的模型。
例如,CBOW 模型将“机器”、“学习”、“方法”作为输入,将“是”作为输出返回。跳格模型正好相反。
根据定义,CBOW 和 Skip-Gram 模型都是多类分类模型。下面详细的可视化应该会清楚。

图片 2。CBOW 模型:高级概述。作者图片

图片 3。跳格模型:高层次的概述。作者图片
黑匣子里发生了什么?
第一步是用 id 对所有单词进行编码。ID 是一个整数(索引),用于标识单词在词汇表中的位置。“词汇”是描述文本中一组独特词汇的术语。这个集合可以是文本中的所有单词或者仅仅是最频繁出现的单词。“数据准备”一节将详细介绍这一点。
Word2vec 模型非常简单,只有两层:
- 嵌入层,取 word ID,返回其 300 维向量。Word2vec 嵌入是 300 维的,因为作者证明了这个数字在嵌入质量和计算成本方面是最好的。您可以将嵌入层视为一个简单的具有可学习权重的查找表,或者视为一个没有偏差和激活的线性层。
- 然后是带有 Softmax 激活的线性(密集)层。我们为多类分类任务创建一个模型,其中类的数量等于词汇表中的单词数量。
CBOW 和 Skip-Gram 模型之间的区别在于输入单词的数量。CBOW 模型采用几个单词,每个单词都经过相同的嵌入层,然后在进入线性层之前对单词嵌入向量进行平均。跳格模型取而代之的是一个单词。详细的架构如下图所示。

图片 4。CBOW 模型:细节中的建筑。作者图片

图 5。跳跃式模型:细节中的建筑。作者图片
单词嵌入在哪里?
我们训练不会被直接使用的模型。我们不想从上下文中预测一个词,也不想从一个词中预测上下文。相反,我们想要得到单词向量。原来这些向量就是嵌入层的权重。更多细节在“检索嵌入”一节中。
数据
Word2vec 是一个无监督的算法,所以我们只需要一个大的文本语料库。原来 word2vec 是在 Google 新闻语料库上训练的,包含 6B 的令牌。
我用 PyTorch 中可用的较小数据集进行了试验:
- WikiText-2 :列车部分 36k 文本行和 2M 记号(记号为单词+标点)
- WikiText103 :列车部分 180 万条线路和 100 万个令牌
在为商业/研究任务训练单词嵌入时,请仔细选择数据集。例如,如果你想对机器学习论文进行分类,就用关于机器学习的科学文本来训练 word2vec。如果你想对时尚文章进行分类,一个时尚新闻数据集会是更好的选择。这是因为“模型”这个词在机器学习领域意味着“方法”和“算法”,但在时尚领域意味着“人”和“女人”。
当重用训练过的单词嵌入时,注意它们被训练的数据集,以及这个数据集是否适合你的任务。
数据准备
数据准备的主要步骤是创建一个词汇表。词汇表包含将为其训练嵌入的单词。词汇可能是文本语料库中所有独特单词的列表,但通常不是。
最好创造词汇:
- 要么通过过滤掉在语料库中出现少于 N 次的罕见单词;
- 或者通过选择前 N 个最频繁出现的单词。
这种过滤很有意义,因为词汇表越小,模型训练就越快。另一方面,您可能不希望对文本语料库中只出现过一次的单词使用嵌入,因为这些嵌入可能不够好。为了创建好的单词嵌入,模型应该在不同的上下文中多次看到一个单词。
词汇表中的每个单词都有其唯一的索引。词汇中的单词可以按字母顺序或基于它们的频率排序,也可以不排序——这不应该影响模型训练。词汇通常表示为字典数据结构:
vocab = {
"a": 1,
"analysis": 2,
"analytical": 3,
"automates": 4,
"building": 5,
"data": 6,
...
}
标点符号和其他特殊符号也可以添加到词汇表中,我们也为它们训练嵌入。你可以小写所有的单词,或者为单词“Apple”和“apple”训练单独的嵌入;在某些情况下,它可能是有用的。
根据您希望您的词汇表(和单词嵌入)是什么样的,对文本语料库进行适当的预处理。小写与否,去掉标点符号与否,对文本进行记号化。

图片 6。如何从文本语料库中创建词汇?作者图片
对于我的模型:
- 我只从一篇文章中出现至少 50 次的单词中创造词汇。
- 我使用了 PyTorch 的 basic_english tokenizer ,它将文本小写,用空格分割成记号,但将标点符号放入单独的记号中。
因此,在单词进入模型之前,它们被编码为 id。ID 对应于词汇表中的单词索引。不在词汇表中的单词(词汇表外的单词)用某个数字进行编码,例如 0。

图 7。如何用词汇 id 对单词进行编码?作者图片
使用 PyTorch 进行文本处理
训练词 2vec 的完整代码在这里。让我们来看看重要的步骤。
模型是通过从 nn 子类化在 PyTorch 中创建的。模块。如前所述,CBOW 和 Skip-Gram 模型都有两层:嵌入和线性。
下面是 CBOW 的模型类,这里的是 Skip-Gram 的。
import torch.nn as nn
EMBED_DIMENSION = 300
EMBED_MAX_NORM = 1 class CBOW_Model(nn.Module):
def __init__(self, vocab_size: int):
super(CBOW_Model, self).__init__()
self.embeddings = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=EMBED_DIMENSION,
max_norm=EMBED_MAX_NORM,
)
self.linear = nn.Linear(
in_features=EMBED_DIMENSION,
out_features=vocab_size,
)
def forward(self, inputs_):
x = self.embeddings(inputs_)
x = x.mean(axis=1)
x = self.linear(x)
return x
注意,线性层没有 Softmax 激活。这是因为 PyTorch CrossEntropyLoss 期望预测是原始的、非标准化的分数。而在 Keras 中,您可以定制 CrossEntropyLoss 的输入是什么— 原始值或概率。
模型输入是单词 ID。模型输出是一个 N 维向量,其中 N 是词汇量。
EMBED_MAX_NORM 是限制单词嵌入规范的参数(在我们的例子中为 1)。它作为一个正则化参数,防止嵌入层中的权重不受控制地增长。EMBED_MAX_NORM 值得一试。我所看到的:当限制嵌入向量范数时,类似的词如“母亲”和“父亲”具有更高的余弦相似度,与 EMBED_MAX_NORM=None 时相比。
我们使用 PyTorch 函数build _ vocab _ from _ iterator从数据集迭代器创建词汇表。WikiText-2 和 WikiText103 数据集用标记< unk >替换了罕见的单词,我们添加这个标记作为 ID=0 的特殊符号,并且所有不在词汇表中的单词也用 ID=0 编码。
from torchtext.vocab import build_vocab_from_iterator MIN_WORD_FREQUENCY = 50def build_vocab(data_iter, tokenizer):
vocab = build_vocab_from_iterator(
map(tokenizer, data_iter),
specials=["<unk>"],
min_freq=MIN_WORD_FREQUENCY,
)
vocab.set_default_index(vocab["<unk>"])
return vocab
数据加载器 我们用 collate_fn 创建。该函数实现了如何对单个样本进行批处理的逻辑。当遍历 PyTorch WikiText-2 和 WikiText103 数据集时,检索到的每个样本都是一个文本段落。
例如,在用于 CBOW 的 collate_fn 中,我们“说”:
- 取每一段文字。
—小写,标记,用 id 编码(函数 text_pipeline )。如果段落太短,跳过它。如果太长—将其截断。
—使用大小为 9 的移动窗口(4 个历史单词、中间单词和 4 个未来单词)在段落中循环。
—所有中间词合并成一个列表——它们将是 Ys。
—所有上下文(历史和未来单词)合并成一个列表列表—它们将是 Xs。 - 将所有段落中的 x 合并在一起,它们将是批 x。
- 将所有段落中的 y 合并在一起,它们将成为批量 y。
请注意,当我们调用 collate_fn 时,最终批次的数量(Xs 和 Ys)将与 Dataloader 中指定的参数 batch_size 不同,并且将因不同的段落而异。
CBOW 的 collate_fn 的代码如下,用于 Skip-Gram —在这里。
import torch CBOW_N_WORDS = 4
MAX_SEQUENCE_LENGTH = 256 def collate_cbow(batch, text_pipeline):
batch_input, batch_output = [], []
for text in batch:
text_tokens_ids = text_pipeline(text) if len(text_tokens_ids) < CBOW_N_WORDS * 2 + 1:
continue if MAX_SEQUENCE_LENGTH:
text_tokens_ids = text_tokens_ids[:MAX_SEQUENCE_LENGTH] for idx in range(len(text_tokens_ids) - CBOW_N_WORDS * 2):
token_id_sequence = text_tokens_ids[idx : (idx + CBOW_N_WORDS * 2 + 1)]
output = token_id_sequence.pop(CBOW_N_WORDS)
input_ = token_id_sequence
batch_input.append(input_)
batch_output.append(output)
batch_input = torch.tensor(batch_input, dtype=torch.long)
batch_output = torch.tensor(batch_output, dtype=torch.long)
return batch_input, batch_output
下面是如何将 collate_fn 与 PyTorch Dataloader 一起使用:
from torch.utils.data
import DataLoader
from functools import partial dataloader = DataLoader(
data_iter,
batch_size=batch_size,
shuffle=True,
collate_fn=partial(collate_cbow, text_pipeline=text_pipeline),
)
我还创建了一个职业训练器,用于模型训练和验证。它包含一个典型的 PyTorch 训练和验证流程,所以对于那些有 PyTorch 经验的人来说,它看起来非常简单。
如果你想更好地理解代码——我推荐你克隆我的库并使用它。
培训详情
使用交叉熵损失将 Word2vec 训练为多类分类模型。
您选择批量大小以适应内存。只要记住:那个批量大小就是数据集段落的数量,它会被处理成输入输出对,这个数字会大很多。
论文优化器是 AdaGrad,但是我用了一个更新的——Adam。
我已经跳过了层次化的 Softmax 的论文部分,只使用了普通的 Softmax。也没有使用霍夫曼树来构建词汇表。分层的 Softmax 和 Huffman 树是加速训练的技巧。但是 PyTorch 在引擎盖下有很多优化,所以训练已经足够快了。
正如论文中所推荐的,我从 0.025 的学习率开始,并在每个时期线性降低,直到在最后一个时期结束时达到 0。这里 PyTorch LambdaLR 调度器帮了大忙;这里是我如何使用它的。
作者在大多数实验中只训练了 3 个时期的模型(但在非常大的数据集上)。我尝试了越来越小的数字,并决定坚持使用 5 个纪元。
对于 WikiText-2 数据集:
- 我的词汇量大约是 4k 个单词(这些单词在文章中至少出现了 50 次)。
- CBOW 和 Skip-Gram 模型在 GPU 上的训练时间不到 20 分钟。
对于 WikiText103 数据集:
- 我的词汇量大约是 50k 个单词。
- 模特通宵训练。
检索嵌入
完整的程序在这本笔记本中有描述。
单词嵌入存储在嵌入层中。嵌入层的大小是(vocab_size,300),这意味着我们可以嵌入词汇表中的所有单词。
当在 WikiText-2 数据集上训练时,CBOW 和 Skip-Gram 模型都在大小为(4099,300)的嵌入层中具有权重,其中每一行都是单词向量。以下是获取嵌入层权重的方法:
embeddings = list(model.parameters())[0]
下面是如何获得与嵌入矩阵中相同顺序的单词:
vocab.get_itos()
在您的模型中使用嵌入之前,有必要检查他们是否接受了适当的培训。对此有几种选择:
- 对单词嵌入进行聚类,并检查相关单词是否形成单独的聚类。
- 使用 t-SNE 可视化单词嵌入,并检查相似的单词是否彼此靠近。
- 为一个随机单词寻找最相似的单词。
t-SN 可视化
你可以使用 sklearn t-SNE 和 plotly 来创建一个两个组件的可视化,如下图所示。这里的数字串是绿色的,它们形成了两个独立的簇。

图 8。基于 WikiText-2 语料库的 CBOW 嵌入可视化。
带有以绿色绘制的数字字符串的双组分 t 型 SNE。
放大后,我们可能会发现将数字字符串分成两个簇很有意义。左上角的聚类是由年份组成的,而右下角的聚类是由普通数字组成的。

图片 9。放大时的数字聚类。左上角的簇是由年份和组成的
,右下角的簇是由普通数字组成的。
你可以探索这种奇妙的视觉化来发现更有趣的关系。创建这个可视化的代码在这里。
相似的词
单词相似度计算为单词向量之间的余弦相似度。显然,余弦相似度越高,假设的相似单词就越多。
例如,对于单词“父亲”,下面是词汇表中最相似的单词:
#CBOW model trained on WikiText-2
mother: 0.842
wife: 0.809
friend: 0.796
brother: 0.775
daughter: 0.773 #Skip-Gram model trained on WikiText-2
mother: 0.626
brother: 0.600
son: 0.579
wife: 0.563
daughter: 0.542
这里是查找相似单词的代码。
国王——男人+女人=王后
根据这篇论文,一个经过适当训练的 word2vec 模型可以解方程“king — man + woman =?”(回答:“女王”),还是“更大—大+小=?”(回答:“小一点”),还是“巴黎—法国+德国=?”(回答:“柏林”)。
通过对单词 vectors 执行数学运算来求解方程:vector(“king”)—vector(“man”)+vector(“woman”)。而最终的矢量应该是最接近矢量的(“皇后”)。
不幸的是,我不能复制这一部分。我在 WikiText-2 和 WikiText103 上训练的 CBOW 和 Skip-Gram 模型不能捕捉这种关系(这里的代码是)。
与矢量(“国王”)—矢量(“男人”)+矢量(“女人”)最接近的矢量是:
#CBOW model trained on WikiText-2 dataset
king: 0.757
bishop: 0.536
lord: 0.529
reign: 0.519
pope: 0.501 #Skip-Gram model trained on WikiText-2 dataset
king: 0.690
reign: 0.469
son: 0.453
woman: 0.436
daughter: 0.435 #CBOW model trained on WikiText103 dataset
king: 0.652
woman: 0.494
queen: 0.354
daughter: 0.342
couple: 0.330
与 vector(" bigger ")-vector(" big ")+vector(" small ")最接近的向量是:
#CBOW model trained on WikiText-2 dataset
small: 0.588
<unk>: 0.546
smaller: 0.396
architecture: 0.395
fields: 0.385 #Skip-Gram model trained on WikiText-2 dataset
small: 0.638
<unk>: 0.384
wood: 0.373
large: 0.342
chemical: 0.339 #CBOW model trained on WikiText103 dataset
bigger: 0.606
small: 0.526
smaller: 0.273
simple: 0.258
large: 0.258
有时,正确的单词在前 5 名之内,但从来不是最接近的。我认为有两个可能的原因:
- 代码中的一些错误。为了仔细检查,我用 Gensim 库和相同的数据集——wiki text-2,wiki text 103——训练了 word2vec。Gensim 单词嵌入也不能解决这些方程。
- 所以更可能的原因是数据集太小。WikiText-2 包含 2M 标记,WikiText103 包含 1 亿个标记,而本文使用的 Google 新闻语料库包含 6B 个标记,即 60(!!!)倍大。
在训练单词嵌入时,数据集大小确实很重要。作者在论文中也提到。
下一步是什么?
我希望这篇文章能帮助你在深层 NLP 中建立一个基础,这样你就可以继续学习更高级的算法。对我来说,深挖原论文,一切从零开始训练,非常有趣。推荐。
原载于 2021 年 9 月 29 日https://notrocketseconomy . blog。
如果你想阅读更多类似的教程,可以订阅我的博客“非火箭科学”——电报 和 推特 。
基于子词的标记化算法
了解最新的自然语言处理模型使用的基于子词的标记化算法

在过去的几年里,在人工智能领域,尤其是自然语言处理领域,有很多讨论。😎理解和分析人类语言不仅是一个具有挑战性的问题,也是一个迷人的问题。人类的语言看起来很简单,但却非常复杂,因为即使是一篇很短的文章也可能涉及到个人生活和外部世界。🧐这种复杂性带来了很多挑战。世界各地的研究人员正在努力克服这些挑战,并正在构建更智能的现实世界应用程序。👩💻
当我们开始处理文本时,我们执行一组预处理步骤来将文本转换为数字。这些步骤在任何模型开发过程中,甚至在分析文本时都是至关重要的。在这个多阶段预处理过程中,一个重要的步骤是标记化,其也可以是不同的类型。有一个单词、子单词和基于字符的标记化。每一种都有自己的目的、优点和缺点。让我们先了解一下基于子词的标记化算法。
基于子词的标记化
基于子词的标记化是介于基于词和基于字符的标记化之间的一种解决方案。主要思想是解决基于单词的标记化(非常大的词汇量,大量的 OOV 标记,以及非常相似的单词的不同含义)和基于字符的标记化(非常长的序列和不太有意义的单个标记)所面临的问题。
基于子词的记号化算法不会将频繁使用的词分成更小的子词。而是将生僻的单词拆分成更小的有意义的子单词。例如,“男孩”不是分裂的,而是“男孩”分裂为“男孩”和“s”。这有助于模型了解单词“boys”是使用单词“boy”形成的,单词“boy”的意思略有不同,但词根相同。
一些流行的基于子词的记号化算法是单词块、字节对编码(BPE)、单语法和句子块。在本文中,我们将详细介绍单词块算法。WordPiece 用于语言模型,如 BERT、DistilBERT、伊莱克特。词块算法有两种实现方式——自底向上和自顶向下。最初的自下而上的方法是基于 BPE。BERT 使用自顶向下的 WordPiece 实现。在本文中,我将讨论基于 BPE 的最初的自底向上实现。
如果你想知道这三种标记化技术之间的区别,那么你可以阅读这篇文章,这是一篇关于 TDS 的实践教程。😍
让我们从工件算法开始。🏃♀️
文字片
WordPiece 是一种基于子词的标记化算法。在论文“日韩语音搜索(Schuster et al .,2012) 中首次概述。该算法通过著名的最新模型 BERT 而广受欢迎。这种算法与 BPE 没有太大的不同,所以我建议你在阅读本文之前先了解一下 BPE。
如果你正在寻找一个好的来源,这里有一篇关于字节对编码(BPE)算法的文章。😇你可以阅读这篇文章,它将向你解释 BPE 算法的一步一步的过程。
BPE 获取一对令牌(字节),查看每对令牌的频率,并合并具有最高组合频率的令牌对。该过程是贪婪的,因为它在每一步寻找最高的组合频率。
那么,BPE 有什么问题呢?🤔它可以有多种方式来编码一个特定的单词。然后,算法很难选择子词标记,因为没有办法区分优先使用哪一个。因此,相同的输入可以由不同的编码来表示,这影响了所学习的表示的准确性。🤦♀️
请看下面的子词标记表。

假设这是一个小型语料库的词汇,我们想要标记我们的输入短语“线性代数”。我们可以将其标记如下:
linear = li +附近 或 li + n + ea + r
代数= al + ge + bra 或 al + g + e + bra
我们可以看到,有两种不同的方法来标记给定短语中的每个单词,总共有四种方法来标记这个短语。因此,相同的输入文本可以用四种方式编码,这确实是一个问题。🤷♀️
每当我们考虑任何领域的进步/改进时,我们总是寻找更好和更现实的方法。一种可能比 BPE 的频率方法更有效的方法是考虑特定字节对(符号对)的合并在每一步的影响。👍
在数据科学中,当我们比频率或计数领先一步时,我们会寻找概率方法。在 WordPiece 中,我们实际上也是这样做的。WordPiece 和 BPE 之间的唯一区别是符号对添加到词汇表中的方式。在每一个迭代步骤中,WordPiece 选择一个符号对,该符号对在合并时将导致最大的可能性增加。最大化训练数据的似然性等同于找到这样的符号对,其概率除以该符号对中第一个符号的概率,然后除以第二个符号的概率,大于任何其他符号对。
例如,该算法将检查“es”的出现概率是否大于“e”后跟“s”的出现概率。只有当“es”除以“e”,“s”的概率大于任何其他符号对时,才会发生合并。
因此,我们可以说,WordPiece 通过合并这两个符号来评估它将会失去什么,以确保它正在采取的步骤实际上是值得的还是不值得的。🤗
工件算法是迭代的,根据论文的算法总结如下:
- 用基本字符初始化单词单元目录。
- 使用来自 1 的单词 inventory 在训练数据上建立语言模型。
- 通过组合当前单词库中的两个单元来生成新的单词单元。在添加这个新的单词单元之后,单词单元库存将增加 1。新的单词单元是从所有可能的单词单元中选择的,因此当添加到模型中时,它最大程度地增加了训练数据的可能性。
- 转到 2,直到达到单词单位的预定义限制或者可能性增加低于某个阈值。
你一定在想,如果使用暴力,训练一定是一个计算量很大的过程。如果是,那么你是对的。🤨时间复杂度是 O(K ),其中 K 是当前字单元的数量。对于每次迭代,我们需要测试所有可能的对组合,并且每次都训练一个新的语言模型。然而,通过遵循本文中讨论的一些简单技巧,训练算法可以显著加快速度。🏃♂️我们可以只测试实际存在于训练数据中的对,只测试有很大机会成为最佳的对(具有高先验的对),将几个聚类步骤组合成单个迭代(对于一组互不影响的对是可能的)。根据这篇论文,这些贪婪的加速帮助在一台机器上仅用几个小时就为日本和韩国数据集构建了 20 万个词汇。是不是很神奇?🥳:这个清单可以用于语言建模、字典构建和解码。
它还贪婪吗?
是的,即使遵循了概率方法,WordPiece 算法仍然是贪婪的,因为它在每次迭代中挑选最佳对来合并,并自下而上地构建一个记号化器(从字符到对,等等)。完全概率模型将使用概率来选择要合并的对以及是否合并它们。尽管如此,这种算法还是很受欢迎,并给出了很好的结果。
所以,我们总结一下。👍
单词块算法在基本词汇上训练语言模型,挑选具有最高可能性的对,将该对添加到词汇中,在新词汇上训练语言模型,并重复重复的步骤,直到达到期望的词汇大小或可能性阈值。
我希望你对工件算法有所了解。如果你也想了解自上而下的方法,我鼓励你阅读 Tensorflow 的这篇博客。
参考文献:
- https://static . Google user content . com/media/research . Google . com/ja//pubs/archive/37842 . pdf
- https://huggingface.co/transformers/tokenizer_summary.html
- https://www . tensor flow . org/text/guide/subwords _ tokenizer # applying _ word piece
感谢大家阅读这篇文章。请分享您宝贵的反馈或建议。快乐阅读!📗 🖌
使用 Python 和 ErlangC 优化劳动力计划
了解如何找到管理传入流量所需的最佳位置数量

很长一段时间以来,寻找队列系统中使用的位置数量一直是一个研究案例;它在许多领域和行业都有应用,例如,寻找呼叫中心代理的最佳数量,决定支持站的银行家数量,网络流量分析等等。
分析这个问题有几种方法;在本文中,我们将看看如何使用 Erlang C 和 Python 的 Pyworkforce 包来解决这个问题。
1。排队系统
在最基本的 Erlang C 方法中,我们使用以下假设将系统表示为一个队列:
- 有恒定速率的输入流量;到达遵循泊松过程
- 系统中有固定的容量;通常,一个资源一次只能处理一个事务
- 在时间间隔中有固定数量的可用位置
- 当所有位置都有总容量时,请求等待位置空闲的队列长度是无限的。
- 指数分布描述了队列中的等待时间
- 没有人退出队列。
具有这些特征的队列系统可能如下所示:

排队系统。图片由作者提供。
在这个表示中,我们可以看到几个有助于我们描述系统的度量;这是它们的定义,以及从现在开始我们将如何称呼它们:
- 事务:传入请求的数量
- 资源:处理事务的元素
- 到达率:一个时间间隔内的传入交易数
- 平均应答速度(ASA): 事务在队列中等待资源处理的平均时间
- 平均处理时间(AHT): 单个资源参与事务的平均时间
图中还有其他变量,但这对模型很重要,它们是:
- 缩减:服务器不可用的预期时间百分比,例如,由于休息、预定培训等原因。
- 占用率:资源处理事务的时间百分比
- 服务水平:在目标 ASA 之前到达资源的事务百分比
Erlang C 查找该系统中资源数量的方法是通过查找事务在队列中等待的概率,而不是立即被处理,它采用目标 ASA 和服务级别,并使用其他变量作为系统参数,如果您想了解更多关于 Erlang 公式的细节,可以在文章末尾查看其他资源。
2。Python 示例
作为一个例子,我们将找到一个呼叫中心处理来电流量所需的代理数量。
在给定的约定下,资源将是代理的工作站,事务将是这个场景下的调用。
假设在 30 分钟的时间间隔内,平均有 100 个来电,AHT 为 3 分钟,预期缩水 30%。
作为呼叫中心管理员,我们希望事务在队列中等待的平均时间为 20 秒,并达到 80%的服务水平。我们还希望确保代理的最大占用率不超过 85%。
为了解决这个问题,我们将使用 Pyworkforce,这是一个用于劳动力管理、调度和优化问题的 python 包,所以让我们安装它。建议使用虚拟 env。
pip install pyworkforce
这个包的使用非常简单;我们导入 ErlangC 并用给定的参数初始化该类,然后我们使用“required_positions”方法找到处理事务的最小资源数。考虑到班级期望所有时间变量都以分钟为单位:
这段代码的输出应该如下所示:
这本字典给我们的是:
raw_positions: 假设收缩率= 0 时找到的仓位数量
位置:根据用户提供的收缩量找到的位置数量
service_level: 队列中等待时间不超过目标 ASA 的事务的预期百分比
占用率:系统将拥有的预期占用率
waiting_probability: 事务在队列中等待的概率
注意:该示例以 30 分钟的时间间隔对系统进行建模。Erlan C 假设可能适用;如果您想延长时间间隔,您可以使用 Erlang C 与每个间隔的参数多次。
3。运行多个 Erlang C
如果您想同时尝试不同的参数,pyworkforce 附带了一个 MultiErlangC 类,它允许您定义一组要迭代的参数。
首先,创建一个参数网格;它必须是一个 dict,并且每个参数选项必须在一个 iterable 中,作为一个列表。
然后 MultiErlangC 将使用 ErlangC 方法,并提供所有可能的参数组合。
例如,如果网格有三个事务值选项和两个缩减选项,那么将有六个不同的模型。
如果您有一个巨大的网格,您还可以使用参数“n_jobs”来控制并发运行的作业,“-1”意味着使用计算机中所有可用的 CPU。
在本例中,我们只尝试三种不同的服务级别选项:
输出是每个场景的列表,其结果与单个 Erlang C 方法相同:
如果您喜欢表格格式的输出,可以使用 pandas,例如:

列表式 MultiErlangC。图片由作者提供。
4。最后备注:
Pyworkforce 是一个软件包,使劳动力管理优化问题更容易解决;除了规模之外,还有其他的子问题,比如排班和排班。要了解更多信息,你可以查看我所在的 Github 项目。非常欢迎对该包的任何建议或贡献:【https://github.com/rodrigo-arenas/pyworkforce
5。参考文献
[1] Pyworkforce 包:https://pypi.org/project/pyworkforce/
[2] Erlang C 公式:https://en . Wikipedia . org/wiki/Erlang _(unit)
为一家数据驱动的初创公司工作,这家公司的价值在不到一年的时间里飙升了 700%
数据如何帮助您应对如此惊人的增长

克里斯汀·罗伊在 Unsplash 上的照片
2020 年初,我开始创业,做自由软件工程师。从那以后,我一直与许多初创公司合作,试图帮助他们开发产品,并作为公司成长。在创办自己的公司之前,我曾计划研究其他成功的企业。
在我合作过的所有初创公司中,有一家给我留下了特别深刻的印象。这是一家意大利初创公司,试图通过开发基于数据分析、整合和探索的高度定制的软件产品来重新定义体育行业的规则。在不到一年的时间里,它的财务价值从 300 万欧元增加到 2100 万欧元。
在这个七倍增长的过程中,我有机会为世界各地所有数据驱动的初创公司学习有见地的经验。虽然这发生在欧洲,但我所经历的很容易推广,适用于任何地方。
你的数据驱动型创业公司可能会在未来几个月内爆发。让我们来看看你应该期待和准备什么。
数据是用来帮助而不是取代的
在这样一个上升的环境中,你应该期待新的人和公司对你的产品感兴趣。首先,重要的是传达为什么数据可以帮助最终用户实现他们的目标以及如何实现。特别是像体育这样以决策为导向的行业,数据应该作为帮助人类决策的工具来呈现。
这是克服关于数据驱动的算法将完全取代人类的想法的耻辱和恐惧的最佳方式之一。例如,在与一流的体育工作人员打交道时,他们寻求提高策略和套路的方法。他们想要帮助他们做决定的工具,而不是为他们做决定的工具。
这就是为什么开发最好的数据分析师和勘探系统可能还不够。与目前市场上的工具或人们习惯的工具相比,提供过于复杂或先进的工具可能会阻碍用户使用它们。请记住,数据驱动的软件产品在许多行业中仍然相对较新。只开发高级工具不太可能让人们克服转换成本障碍。
让每个人都理解数据
尽管自从我开始为这样一家以数据为中心的初创公司工作以来,我在数据科学方面的经验有了很大的提高,但我无法轻松理解来自更有经验的数据科学家的算法、概念或分析。
在这样的成长过程中,你应该期待其他人加入你的团队。这就是为什么应该有适当的程序来解释之前由其他数据科学家开发或构思的关键元素。根据我的经验,定期召开内部网络研讨会并分享知识是非常必要的。在这些会议中,专家应该解释为什么要进行特定的分析,如何探索数据,最关键的数据驱动功能是什么,以及他们目前正在做什么。此外,通过记录它们,你已经有了培训新员工的内容。
另外,请记住,数据是你创业的关键。因此,每个员工,无论其职业如何,都应该能够解释当前数据是如何被分析、探索和整合的。例如,这有助于开发人员理解如何设计新功能。同样,销售人员应该知道如何解释创业公司可以对数据进行操作的独特过程,顺利地向新的潜在客户传达和展示产品。
不要让客户迷失在数据中
与此同时,随着你的创业公司的成长,你可能会倾向于只专注于获得新的客户和顾客。这可能会变成一个巨大的错误,尤其是如果你的创业公司正在提供先进的、前所未见的数据探索工具。不要抛弃你的顾客、客户和合作伙伴。相反,尽可能地帮助他们,花时间向他们介绍新的特性,并确保他们真正理解了你卖给他们的工具的潜力。
例如,一个好的做法可能是在客户购买您的产品后安排 15 或 30 分钟的通话,以回答他们的问题(如果他们使用过该工具)或解释如何使用它。同样,如果你坚持这种做法,不时向他们寻求反馈,也会有所帮助。然后,您应该随时准备引导他们以最有效的方式探索数据,以达到他们的目标。
如果你使用监控和行为分析工具,比如 HotJar 或 Google Analytics ,这将变得更加容易。事实上,通过将这些工具收集的数据与您已经拥有的数据相结合,您应该能够确定哪些是用户最常犯的错误。同时,您可以发现他们程序中的低效之处,帮助他们相应地解决或避免这些问题。
利用数据被收购
不管你对创业公司的想法和目标是什么,考虑出售永远不会太晚。当它的金融价值变得巨大时,尤其如此。具体来说,我从这次经历中了解到,无论你的财务结果如何,你都不应该指望买家会从任何地方出现。
相反,你的目标是让所有人都知道你的创业公司蒸蒸日上。毕竟,如果你的生意蒸蒸日上,其他公司或投资者可能会想加入进来。那么,为什么不应用你的数据相关专业知识来制作财务和营销报告,以展示为什么你应该是下一个购买或投资的公司呢?毕竟,这是你擅长的,所以你不应该把钱投入到几乎无效的营销实践中,而是应该专注于设计以数据为中心的报告。
因此,开始收集和探索你的创业公司的内部数据,以了解你成长的关键因素。你应该能够运用你日常所见的相同概念。这可能不会确保你实现退出目标,但同时,它会相应地增强你的技能。
结论
为一家数据驱动的初创公司工作,这家公司的财务价值在不到一年的时间里从 300 万欧元上升到 2100 万欧元,这让我体验到了如此不可思议的增长之后会发生什么。在我学到的所有经验教训中,有四个方面是最重要的,这是我们刚刚在本文中看到的。理解如何使用数据来解决这些问题是应对面向数据的业务中意外或计划外繁荣的关键。相反,忽视它可能是你失败的后果。
感谢阅读!我希望这篇文章对你有所帮助。请随意留下任何问题、评论或建议。
使用基本 Python 库处理数据框和可视化
本文旨在通过支付服务行业的案例研究,介绍与结构化数据交互的基本 Python 方法。

语境
去年,我的老板来到我的住处,给了我一个小任务,分析现状在线支付行业。在当时我对 Python 的热情的激励下,我想为什么我只是尝试用这种编程语言来完成这个练习。关于上下文,Excel、SQL 和 Tableau 是我工作中最受欢迎的三种工具,考虑到它们对于我们的简单分析来说既方便又简单。与此同时,Python 被优先用于更具预测性和更高级的分析。然后,在接下来的两天里,我研究了集成 Python 来分析和与非 Python 观众分享我的工作的可能方法。
本文将涵盖的内容
包括一个支付服务行业的案例研究,这篇文章结合了技术和业务流程,其中我使用了带有数据框架和可视化的基本分析,从支付解决方案提供商的角度来解决业务问题。除了以非技术格式展示发现之外,我还将包括相关的 Python 代码和库。
这篇文章不会涉及的内容
首先,我同意许多其他工具可以帮助得到同样的结果,甚至更有效。例如,人们可以在 Excel 中快速地进行一些数据错误检测,在 SQL 中合并一些代码较短的数据表,或者在 Tableau 中轻松地可视化一些数字。r 也是另一个处理数据框的好工具。我在这里试验的是 Python。其次,Python 在预测分析、机器学习或神经网络等高级数据分析领域非常强大。然而,这些应用程序将不在本文中讨论。
案例研究和数据
好,那我们来谈谈我的“小作业”我的团队在一家市场研究公司工作,为其中一位客户提供支持,他们对金融服务行业的一个当前趋势进行了初步分析:先买后付。应该对这种商业模式的两个方面:卖方和买方提出一些见解,例如:“哪些行业正在采用这种新的支付解决方案?”,“这些新的支付解决方案提供商正在吸引哪些客户群体?”。为了准备这篇文章,我修改了一些原始数据。它意味着这里的结果分析不是“真实的”,只服务于模拟和“教育”目的。我们在这里的重点应该是方法本身。请不要将案例研究数据和分析(有些是经过审查的)用于任何实际用途。
(1)漏斗数据:关于支付服务提供商的结账产品上发生的事件的数据(在像 Casper 这样的商家尝试一下,了解一下流程)。它包括使用该服务的商家、用户——买家,以及用户在结账漏斗中的当前状态。
(2)贷款数据:来自“已完成结帐”操作的每笔贷款的数据。它包括更多关于用户贷款的详细信息,如金额、利率和期限。它还涵盖了用户/买家的数据:个人信息,人口统计,信用评分等。
(3)商户数据:整合了支付解决方案提供商产品的各商户的数据(行业、公司信息)。
分析
所以我将在我的案例分析中涵盖三个部分:(1)数据清洗;(2)检测数据异常;(3)数据分析。我使用这些库进行分析:
**import** **requests** *# library to handle requests*
**import** **pandas** **as** **pd** *# library for data analsysis*
**import** **numpy** **as** **np** *# library to handle data in a vectorized manner*
**import** **random** *# library for random number generation*
*# libraries for displaying images*
**from** **IPython.display** **import** Image
**from** **IPython.core.display** **import** HTML
*# Tranforming json file into a pandas dataframe library*
**from** **pandas.io.json** **import** json_normalize
*# libraries for visualization*
**import** **matplotlib.pyplot** **as** **plt** *# Library for 2D plots of arrays*
**from** **mpl_toolkits.mplot3d** **import** Axes3D *# Library for 3D plotting*
**import** **matplotlib** **as** **mpl** *# Library for creating static, animated, and interactive visualizations*
**import** **seaborn** **as** **sns** *# Library based on matplotlib*
**import** **plotly.express** **as** **px** *# Contains functions that can create entire figures at once*
(1)数据清理
作为任何数据项目的第一步,数据清理与后续工作的相关效率密切相关。例如,在简要回顾了贷款数据表之后,我尝试进行一些基本的清理和转换,比如创建/删除列;并通过根据条件对列进行分组来合并信息:
*# Create a new column age by using current year - user_dob_year*
loansdata['age'] = (2021 - loansdata['user_dob_year'])*# Create a list of our conditions*
conditions = [
(loansdata['age'] < 30),
(loansdata['age'] >= 30) & (loansdata['age'] <= 44),
(loansdata['age'] >= 45) & (loansdata['age'] <= 64),
(loansdata['age'] >= 65)
]
*# create a list of the values we want to assign for each condition*
values = ['Younger than 30', 'Age 30-44', 'Age 45-64', '65 and older']
*# create a new column age_group and use np.select to assign values to it using our lists as arguments*
loansdata['age_group'] = np.select(conditions, values)
(2)检测数据异常
有几种方法可以检测数据异常或异常值,如识别缺失值、使用频率计数或直方图/箱线图可视化。我从每个变量切片的三个数据表的缺失值的主要计数开始,并以百分比视图显示它们:
*# Check missing data*
funnelmissing = pd.DataFrame({'Total missing values': funneldata.isnull().sum(),
'% missing values': round((funneldata.isnull().sum() * 100/ len(funneldata)),2).sort_values(ascending=**False**)})
funnelmissing.sort_values(by=['% missing values'], ascending=**False**)

但是,这并不适用于所有情况,因为 0 和缺失值之间可能存在重叠。除了用你的领域知识去研究每个变量,没有其他方法可以解决这个困惑。然后,我对我们的数据列进行了频率计数,以获得丢失值的另一个视图。我们可以看到有 67202 个用户的值为 0,这可能是缺失值。
*# Frequency Counts of all columns in funnel data*
**for** col **in** funneldata.columns:
**try**:
print('Frequency Count of ', col)
print(funneldata[col].value_counts())
print('')
**except** **ValueError**:
print('This column can not be represented as a histogram')

检测数据异常的另一个选项是使用一个汇总统计表来观察数字数据变量的不同度量。例如,贷款数据,我们可以看到一些潜在的异常,如最高年龄为 122 岁;有些买家退房时 fico 评分为 0;首付和贷款金额异常高。
*# Looking at the histograms, we may see some potential outliers most of the features. Lets use summary stats for more info:*
loansdata[['age', 'apr', 'down_payment_amount',
'fico_score', 'loan_amount','loan_length_months',
'loan_return_percentage', 'mdr', 'user_dob_year']].describe()

最后,我使用了一些可视化技术来深入研究潜在的数据问题。这些图表提供了先前分析(频率和统计摘要)中所涵盖的洞察力的可视化视图。最流行的是直方图和箱线图,它们非常擅长快速显示异常值。

在我们的分析中应用这些层来检查其他数据表将优化我们对潜在数据问题的检测。我没有在这个分析中包括缺失值和异常值的解决方案,因为除了广泛讨论的技术方法之外,我们还需要考虑领域知识和对业务情况的理解。然而,知道如何检测数据问题对几乎所有情况都适用。
(3)数据分析
让我们在这个案例研究中深入了解业务洞察力。本节将涵盖与数据框的不同交互,包括添加新列、移除不相关的列、创建计算/指标、分组数据以及创建自定义的数据透视表。
回答第一个问题,“关注哪个商户行业?”我们将使用两个指标来评估商业行业:收入表现和结账漏斗。
首先,寻求收入绩效指标需要三个步骤:合并两个数据集:贷款数据和商户数据;添加计算收入的新列,并按商家类别分组,以查看按类别/行业的总收入表现。
*# The first step is to merge loansdata and merchansdata*
loansmerchants = pd.merge(loansdata, merchantsdata,
on='merchant_id',
how='left')*# Use formula: (mdr + loan_return_percentage) * loan_amount)*
*# We have two revenue sources: returns from consumers and transaction charges from merchants*
loansmerchants['revenue'] = round((loansmerchants['mdr'] + loansmerchants['loan_return_percentage']) * loansmerchants['loan_amount'])*# Group by merchant category to see the total revenue performance by category*
loansmerchants2 = loansmerchants.groupby(['category']).sum()*# Drop irrelevant columns that might be incorrectly calculate using sum, keep revenue column*
*# We have the total revenue by merchant category*
columns = ['revenue']
loansmerchantsrevenue = loansmerchants2[columns]
即使来自商家的总收入按顺序递减:家具、服装、音乐、珠宝,我们也可能要考虑每个商家类别中每个用户的客户总数。这就是领域知识与我们的技术技能相结合的时候了。使用一个新的指标似乎更合理:四个类别中每个类别的每个用户/客户的收入。
*# Group by merchant category to see the total revenue performance by category*
loansmerchantscount = loansmerchants.groupby(['category']).nunique()*# Drop irrelevant columns*
*# We have the total number of user by merchant category*
columns = ['user_id']
loansmerchantscount_user = loansmerchantscount[columns]
loansmerchantscount_user.rename(columns={"user_id": "number_of_customers"}, inplace=**True**)*# Dive deep into the analysis by computing the revenue per user/customer for each of four category*
*# Merge two data*
loansmerchants_revenuesummary = pd.merge(loansmerchantsrevenue, loansmerchantscount_user,
on='category',
how='left')
*# Create revenue per user column*
loansmerchants_revenuesummary['revenue_per_customer'] = (round(loansmerchants_revenuesummary['revenue'] / loansmerchants_revenuesummary['number_of_customers']))
因此,我们可以看到,根据每位顾客的收入,家具类别更有潜力。此外,我们不应该因为珠宝的市场规模小而让大量珠宝分散我们的分析。现在让我们建立一个可视化来总结我们的分析。

第二,我们希望使用漏斗数据和商户数据的组合来找到当前的结账漏斗。我们使用漏斗数据按商户 id 计算结账漏斗,然后按商户 id 和活动分组,并计算商户 id 和每种活动的结账 id。然后我们把行动因素放入不同的栏目。
*# Now we come back and calculate the current checkout funnel by merchant id using the first dataset: funneldata*
*# Group by merchant_id and action and count the checkout_id for merchant id and each type of action*
funnelcount2 = funneldata.groupby(['merchant_id','action']).count()

*# Pivot factors of action into different columns*
funnelcountpivot2 = pd.pivot_table(funnelcount2, index='merchant_id', columns='action', values='checkout_id')

*# Reorder the columns and change the column names to be the same as our data structure*
funneldata3 = funnelcountpivot2[['Checkout Loaded', 'Loan Terms Run', 'Loan Terms Approved', 'Checkout Completed']]
funneldata3.rename(columns={"Checkout Loaded": "num_loaded"}, inplace=**True**)
funneldata3.rename(columns={"Loan Terms Run": "num_applied"}, inplace=**True**)
funneldata3.rename(columns={"Loan Terms Approved": "num_approved"}, inplace=**True**)
funneldata3.rename(columns={"Checkout Completed": "num_confirmed"}, inplace=**True**)

然而,我们需要将这个漏斗数据 3 与第三个数据集:商家合并,以获得关于商家 id 的信息,然后按商家类别分组,以查看按类别的漏斗性能。最后,我们向当前数据表添加三个计算列。同样,使用可视化来总结我们的分析最终为家具类别提供了额外的支持,这是支付解决方案提供商的潜在关注点。
*# Merge this funneldata3 with the third dataset: merchants to get information about merchant id*
funnelmerchants = pd.merge(funneldata3, merchantsdata,
on='merchant_id',
how='left')*# Group by merchant category to see the funnel performance by category*
funnelmerchantsperformance = funnelmerchants.groupby(['category']).sum().reset_index()*# Add three calculation columns to the funnelmerchantsperformance*
*# We end up with the current funnel performance by category*
funnelmerchantsperformance['application_rate'] = round(funnelmerchantsperformance['num_applied'] / funnelmerchantsperformance['num_loaded'], 2)
funnelmerchantsperformance['approval_rate'] = round(funnelmerchantsperformance['num_approved'] / funnelmerchantsperformance['num_applied'], 2)
funnelmerchantsperformance['confirmation_rate'] = round(funnelmerchantsperformance['num_confirmed'] / funnelmerchantsperformance['num_approved'], 2)

现在,转到第二个问题,“关注哪个用户群体?”,可以考虑年龄和 FICO 因素。我们先从年龄组开始,通过计算收入表现指数。我们希望按年龄组对数据进行分组,以查看按年龄组的收入表现。
*# Group by age_group to see the revenue performance by age_group*
loansmerchantsage = loansmerchants3.groupby(['age_group']).sum().reset_index()*# Drop irrelevant columns*
*# We have the total revenue by merchant category*
columnsage = ['age_group','revenue']
loansagerevenue = loansmerchantsage[columnsage]

尽管年龄组的总收入在下降:45-64 岁;年龄 30-44 岁;65 岁及以上;对于 30 岁以下的人,我们可能希望了解每个年龄组中每个用户的客户总数。我们使用相同的方法来计算一个新的指标:四个类别中每个用户/客户的收入。
*# Group by age_group to see the total revenue performance by age group*
loansagecount = loansmerchants.groupby(['age_group']).nunique()*# Drop irrelevant columns*
*# We have the total number of user by age group*
columns = ['user_id']
loansagecount_user = loansagecount[columns]
loansagecount_user.rename(columns={"user_id": "number_of_customers"}, inplace=**True**)*# Dive deep into the analysis by computing the revenue per user/customer for each of four category*
*# Merge two data*
loansage_revenuesummary = pd.merge(loansagerevenue, loansagecount_user,
on='age_group',
how='left')
*# Create revenue per user column*
loansage_revenuesummary['revenue_per_customer'] = (round(loansage_revenuesummary['revenue'] / loansage_revenuesummary['number_of_customers']))


第二,我们可能希望研究年龄组、贷款金额和贷款期限之间的关系。下图显示,由于不同群体的贷款期限相同,提供商应继续瞄准年轻-中年人,因为他们在大额贷款金额方面的差异较大。

为了支持我们的用户统计分析,我还考虑了买家的 FICO 分数。使用类似的技术步骤来处理相关的数据框,我们将得出如下结论:

然而,由于可能希望了解 fico 得分和漏斗转化率之间的关系,我使用了散点图来更好地可视化。
*# Group by fico to see the funnel performance by fico*
funnelloansperformance = funnelloans.groupby(['fico_score']).mean().reset_index()*# Add three calculation columns to the funnelloansperformance*
*# We end up with the current funnel performance by fico_score*
funnelloansperformance['application_rate'] = round(funnelloansperformance['num_applied'] / funnelloansperformance['num_loaded'], 2)
funnelloansperformance['approval_rate'] = round(funnelloansperformance['num_approved'] / funnelloansperformance['num_applied'], 2)
funnelloansperformance['confirmation_rate'] = round(funnelloansperformance['num_confirmed'] / funnelloansperformance['num_approved'], 2)*# 2D visualization*
**import** **seaborn** **as** **sns**
**import** **matplotlib.pyplot** **as** **plt**
v12 = sns.catplot(data=funnelloansperformance, y="approval_rate", x="fico_score")
v13 = sns.catplot(data=funnelloansperformance, y="confirmation_rate", x="fico_score")

从商业角度来看,我们可以考虑额外的研究,比如“我们应该在哪里找到我们的用户?”;“我们如何提高用户保留率?”通过请求更多关于结账位置/平台的数据或时间序列数据来获取用户终身价值。回答这些问题将为优化促销和合作打开大门,以推动用户支出,并增加高增长细分市场的保留率
最后的想法
无论我们是数据科学家还是商业/金融分析师,我们都越来越多地花费大量时间处理数据。它变得越来越混乱、复杂、不一致,由缺失和嘈杂的值组成。尽管目前优秀的工具可以帮助诊断、清理和分析数据集,但 Python 应该被认为是具有集中和多样化应用程序的优化程度最高的语言之一。出于这些原因,学习一些基本的 Python 库是非常有帮助的,比如它的内置模块 Pandas,它提供了一种快速有效的方法来管理和探索数据。
在 Python 中使用日期时间
编程;编排
立刻成为日期和时间的主人

来源:Undraw.co
介绍
在您作为数据科学家的职业生涯中,不可避免地会遇到包含日期和时间的数据。出于这个原因,这篇内容丰富的指南旨在帮助使用 Python 的datetime模块轻松操作日期和时间数据。我们将涉及一系列主题,比如处理日期和时间字符串、测量时间跨度以及在pandas中使用 datetime 对象。
Python 的日期时间类
为了向您介绍datetime模块,有五个主要的对象类可以方便地用于不同的目的。它们如下:
datetime—允许我们一起操作日期和时间(月、日、年、小时、秒、微秒)。date—仅允许我们操作日期(月、日、年)。time—你大概猜到了;这个类只允许我们操纵时间(小时、分钟、秒、微秒)。timedelta—用于测量持续时间,即两个日期或时间之间的差异。tzinfo—用于处理时区。我们不会在本教程中涉及这一点。
让我们来看看何时以及如何使用这些类。
使用日期时间
首先,重要的是要注意到datetime既是一个模块,也是该模块中的一个类,用于编写您的导入。
我们将演示使用datetime对象的两种方法。
首先,您可以将参数传递给datetime,从最大的时间单位开始,以最小的时间单位结束(年、月、日、小时、分钟、秒)。值得一提的是,你在现实生活中不会经常遇到这种方法,因为你很可能会发现自己正在使用现成的数据,但尽管如此,知道这一点还是有好处的。
第二,如果你想得到当前的datetime,你可以很容易地使用.now()函数。
# import datetime
import datetime from datetime# create datetime object
# datetime_object1 = datetime(2021, 6, 1, 15, 23, 25)# get current date
datetime_object2 = datetime.now()
使用datetime很酷的一点是,我们可以对不同的datetime对象进行运算。例如,让我们假设我们想要找出前两个日期时间之间经过的秒数。我们可以这样做:
duration = datetime_object2 - datetime_object1
duration.total_seconds()
访问单个组件
您可以使用日期时间的属性访问datetime对象的单个组件;例如年、日和小时属性。
# extract data
datetime_object2.year
datetime_object2.day
datetime_object2.hour
此外,您还可以使用.weekday()功能找到星期几。
datetime_object2.weekday()
运行前一行得到的结果是2,这是一个星期三。请注意,Python 从 0 开始计算工作日,从星期一开始。这意味着:
- 0 =星期一
- 1 =星期二
- 2 =星期三
- …
- 6 =星期日
既然我们已经介绍了datetime对象的来龙去脉,你也可以很容易地使用date和time对象,因为它们使用相同的方法。
使用时间增量
在我们想要测量持续时间的情况下,timedelta类很方便,因为它表示两个日期或时间之间的时间量。当我们想要从日期或时间中加减时,这特别有用。
我们将继续演示如何一起使用timedelta对象和datetime对象进行数学运算。例如,我们将在当前时间上增加 27 天。
# import timedelta
from datetime import timedelta# create a 27 day timedelta
td = timedelta(days=27)#add 27 days to current date
27_days_later = datetime_object2 + td
类似于我们上面所做的,我们也可以使用timedelta从当前日期或任何其他日期中减去。
注意timedelta可用于任意周数、天数、小时数、分钟数等。它可以使用小到一微秒的时间单位,大到270 万年!令人印象深刻。
使用日期和时间字符串
通常,我们会发现我们想要将对象从字符串转换成datetime对象,反之亦然。datetime包括两个有用的方法,可以帮助我们实现这一点;也就是说,strptime()和strftime().我们可以使用strptime()读取包含日期和时间信息的字符串并将它们转换成datetime对象,使用strftime()将datetime对象转换回字符串。
当使用strptime()时,我们必须考虑到它不能将任何字符串转换成日期和时间,因此我们必须自己指示时间格式。最好用一个例子来说明这一点:
date_string = '2020-11-27'
# Create date object with time format yyyy-mm-dd
date = datetime.strptime(date_string, "%Y-%m-%d")
注意strptime()有两个参数:
- 字符串-字符串格式的时间
- 格式—字符串中时间的特定格式
如果你不熟悉帮助strptime()解释我们的字符串输入所需的格式化代码,你可以在这里找到有用的参考。
使用熊猫日期时间对象
pandas是最受欢迎的 Python 模块之一,这是有充分理由的。其中之一是它使处理时间序列数据变得轻而易举。与datetime模块相似,pandas 也有与datetime模块功能相似的datetime和timedelta对象。
简单介绍一下,我们可以使用以下函数将日期时间和持续时间字符串转换为 pandas 日期时间对象:
to_datetime()—将字符串格式的日期和时间转换为 Pythondatetime对象。to_timedelta()—用于测量持续时间。
这些函数在将字符串转换成 Python datetime时做得很好,因为它们自动检测日期的格式,而不需要我们像对strptime()那样定义它。
让我们用一个具体的例子来检验这一点:
# import pandas
import pandas as pd# create date object
date = pd.to_datetime("4th of oct, 2020")
即使我们的输入看起来是一个复杂的字符串,pandas仍然能够正确地解析该字符串并返回一个datetime对象。
提取单个组件
假设我们想从日期中提取月、小时或分钟,并将其作为一个新列存储在 pandas 数据帧中。我们使用dt属性很容易做到这一点。
例如,我们可以使用df['date'].dt.month从包含完整日期的 pandas 列中提取月份,并将其存储在一个新列中。这个例子当然也可以扩展到不同的时间单位。
类似于我们之前使用的datetime类,pandas 也能够提取元素,例如星期几。我们再次使用dt属性来完成这项工作。
比如我们可以用df['date'].dt.weekday提取一天的数字,0 代表星期一等等。如果我们想直接提取工作日的名称,我们可以方便地使用df['date'].dt.weekday_name来完成。
结论
在本指南中,我们介绍了如何通过处理字符串、使用timedelta测量持续时间和进行简单的算术运算以及使用pandas中的日期时间对象来成功地浏览日期和时间的世界。
如果你有任何问题,我很乐意在评论中回答!
我希望这个指南对你有所帮助。如果你这样做了,你可能会发现我的其他一些文章也很有趣:
[## Python 中代码剖析的快速简易指南
towardsdatascience.com](/a-quick-and-easy-guide-to-code-profiling-in-python-58c0ed7e602b) https://medium.com/codex/web-scraping-using-scrapy-and-python-7ad6d1cd63d0
使用 DICOM-线程
标准很重要,您应该能够与它们一起工作!

卢多维克·沙雷特在 Unsplash 上的照片
标准很重要。我是德国人,所以我知道我在说什么。我们有很多标准和规则,比如“德国工业规范”(DIN)。在这里,我们以纸张的大小或打印文档的样式为例。今天我们要讨论的是在医学图像计算中非常重要的一个标准,DICOM。这是医学数字成像和通信的简称。当我在 Kaggle 上进行SIIM Covid-Detection Challenge时,我偶然发现了这个规范,今天我想向你展示如何使用它们进行机器学习。
在这个挑战中,以及在野外,DICOM 最常用作交换医学图像的文件格式。但问题出现了,如何打开和解析它们?我们如何在图像分类任务中使用它们?这就是我想向你解释它们的原因。我们将使用 Python 和 BIMCV 新冠肺炎+ 数据集一起完成这项工作。它包含超过 2000 个扫描,可以在 Kaggle 挑战页面下载。
py DICOM 库
首先我们需要安装 pydicom 库。用 pip 或 conda 安装后。我们导入 lib 并输出第一幅图像的内容。
如果您现在打印我们的 dicom_img 对象,您可以看到一长串信息。这是 dicoms 最大的优点和缺点。你有许多可选字段,很容易填错,或者根本不填。但是我们现在讨论的是最重要的属性:
我们的转换代码
但是现在我们只有一个 DICOM 数据集对象,我们需要的是一个实际的图像。通过这个简单的方法,我们可以将 DICOM 像素阵列转换成 8 位的 numpy 阵列:
正如你所看到的,我们有两个额外的开关 voi_lut 和 fix_mono 。 voi_lut 开关始终为真,因为如果我们对像素进行转换,我们会使用它,但这里有一个转折。如果我们的 DICOM 扫描中没有 VOI·LUT 序列,pydicom 会保持数组不变。第二个开关解决了以单色 1 格式存储的扫描的问题,因为在这种格式中,最小值以白色显示,而不是黑色,同样,这仅在存在单色 1 扫描时才会发生。
转换后,我们现在可以显示我们的图像,但有一些困扰我。
一些信息可能会丢失!DICOM 扫描提供了另一个有趣的属性:位存储和位分配。DICOM 扫描总是为每个像素分配一些位(1、8 或 16),并实际存储一些位(8、12、16)。在上面的 DICOM 扫描中,我们存储了 12 位,因此我们需要一个更好的函数来根据分配的位进行转换,因此我们不使用详细信息:
到目前为止做得不错。现在让我们显示 PIL 的图像:

卡格尔·SIIM·科维德 19 号挑战赛的肺部 CT 扫描
正如您在我的代码的顶部看到的,您可以调整图像的输出大小以及应该使用多少线程。这段代码不会改变路径或数据,而是创建一个带有后缀 _converted 的新文件夹。如果您想添加一个 argparser,可以随意修改代码。
建议:
- 检查 DCM 扫描的可用大小,否则您会缩放得太多/太少
- 使用 PyTorch 的 8 位 PNGs,否则您将运行在 seg fault 错误!
感谢您的阅读,如果您有任何进一步的问题,请联系我。
参考资料:
如前所述,数据取自卡格尔比赛SIIM·科维德 19 。
在拥抱面中枢上使用 Flux.jl 模型🤗

作者图片
本文将详细介绍如何在flux . JL(100%朱莉娅·迪普·学习包)中保存模型,然后从 Hugging Face Hub 上传或检索它。对于那些不知道什么是抱脸 (HF)的人来说,它就像 GitHub 一样,只是针对机器学习模型。传统上,机器学习模型通常会被锁起来,只有创建它们的团队才能访问。HF 正在机器学习生态系统中掀起风暴,因此了解如何在您的机器学习工作流程中使用 platfrom 至关重要。让我们深入了解如何使用您最喜欢的 Julia ML 软件包来处理 HF🤗!
如果你以前从未使用过 Flux.jl,你可以在这里找到安装说明:【https://fluxml.ai/Flux.jl/stable/#Installation,下面还有一个关于你为什么会想使用它的快速视频:
编辑:我和我的合著者很高兴地告诉大家,我们的新书《朱莉娅速成班》已经开始预售了
https://logankilpatrick.gumroad.com/l/juliacrashcourse
好了,现在我们已经安装了 Flux,并且您知道我们为什么可能要使用它,让我们创建一个简单的模型:
julia> using Flux
julia> model = Chain(Dense(10,5,relu),Dense(5,2),softmax)
Chain(Dense(10, 5, relu), Dense(5, 2), softmax)
然后,我们可以导入 BSON 包(这就是我们如何保存我们的模型,以便他们可以被其他人在拥抱脸上消费)
julia> using BSON: [@save](http://twitter.com/save)
最后,我们实际上可以通过执行以下操作来保存模型:
julia> [@save](http://twitter.com/save) "mymodel.bson" model
接下来,我们将把模型上传到拥抱脸中心,然后尝试在新的会话中下载它。如果您想了解更多关于 Flux 中的模型构建,请查看下面的视频:
前往:https://huggingface.co并创建一个帐户。我不会经历所有这些步骤,因为它相当简单,但如果你遇到任何问题,请张贴在拥抱脸话语实例上:https://discuss.huggingface.co
现在您已经在 HF Hub 上启动并运行,我们将在右上角选择您的个人资料图标,然后选择“新型号”。

作者图片
从那里,填写相关的细节,然后创建模型。你可以在这里找到我创建的基本模型:https://huggingface.co/LoganKilpatrick/BasicFluxjlModel
在您创建了基本的模型卡之后,下一步就是以 BSON 格式上传模型。如果你导航回到朱莉娅·REPL,你可以键入;然后pwd来找到你在本地保存模型的地方。我的保存在我的桌面上,以便于查找。在 HF 上,转到右侧的“文件和版本”和“添加文件”。现在你应该有一个自述文件和一个 BSON 格式的模型保存到拥抱脸中心了!接下来,如何将模型放回你的电脑或其他人的电脑?
所以现在我们有一个模型保存到 HF Hub,下一件事你可能想做的是下载别人的通量模型(像我的:https://huggingface.co/LoganKilpatrick/BasicFluxjlModel)。
HF 提供了一个 Python 库来与 Hub 交互。由于我还没有时间用 pure Julia 重新编写整个包,我们将开发出可靠的 PyCall.jl 包,它允许我们在 Julia REPL 会话或脚本中直接运行 Python 代码。
花一些时间来遵循 PyCall 安装指南:https://github.com/JuliaPy/PyCall.jl#installation,如果有任何不清楚的地方,请做开放问题。
好了,现在你已经启动并运行了,让我们使用它吧!
julia> using PyCalljulia> hf = pyimport("huggingface_hub")
PyObject <module 'huggingface_hub' from '/Users/logankilpatrick/.julia/conda/3/lib/python3.8/site-packages/huggingface_hub/__init__.py'>
注意,这里假设您已经在我们通过 PyCall 使用的同一个 Python 环境中安装了 huggingface_hub Python 包。
那么我们到底有什么呢?我们用 PyCall 导入了一个 Python 包到 Julia?是啊!现在我们已经导入了它,我们可以像平常使用 Python 包一样使用它:
julia> hf.snapshot_download(repo_id="LoganKilpatrick/BasicFluxjlModel", revision="main")
Downloading: 100%|██████████████████████████| 1.18k/1.18k [00:00<00:00, 345kB/s]
Downloading: 100%|██████████████████████████████| 711/711 [00:00<00:00, 130kB/s]
Downloading: 100%|█████████████████████████| 10.3k/10.3k [00:00<00:00, 2.07MB/s]
"/Users/logankilpatrick/.cache/huggingface/hub/LoganKilpatrick__BasicFluxjlModel.077a4b77d6175a09c156a20cf5bed0eac35c97ee"
现在我们已经将模型 repo 保存在本地,让我们再次将朱莉娅 repo 切换到命令行模式,键入;,然后将目录更改到模型文件的位置。
shell> cd "/Users/logankilpatrick/.cache/huggingface/hub/LoganKilpatrick__BasicFluxjlModel.077a4b77d6175a09c156a20cf5bed0eac35c97ee"
为了再次检查我们做得是否正确,我将运行一个 list 命令:
shell> ls -l
total 32
-rw - - - - 1 logankilpatrick staff 711 Oct 13 08:37 README.md
-rw - - - - 1 logankilpatrick staff 10260 Oct 13 08:37 mymodel.bson
太好了!我们在那里看到了模型。现在让我们把它装回通量。
julia> using BSON: [@load](http://twitter.com/load)
julia> [@load](http://twitter.com/load) "mymodel.bson" model
julia> model
Chain(Dense(10, 5, relu), Dense(5, 2), softmax)
嘣!我们做到了🎊!我们成功地用 Flux 创建了一个模型,上传到 Hub,然后下载了一个新的模型(或者是你创建的同一个)加载回 Flux / Julia。
下一步是保持训练模型并上传/分享它们。拥抱脸中心是一个与其他 ML 研究者和实践者合作的非常时髦的方式,所以我会在那里见到你!
另外,我还在 HF 上创建了一个 Julia 语言组织,所以如果你有一些令人印象深刻的模型想要分享,ping 我和我可以将你添加到该组织(https://huggingface.co/JuliaLanguage)。
~洛根·基尔帕特里克(【https://twitter.com/OfficialLoganK】T2
使用 Python 处理地理空间数据—第 1 部分
常见数据类型和使用栅格从开源数据生成树冠高度模型。

安妮·斯普拉特在 Unsplash 上的照片
实话实说吧;地理空间数据很吓人。除了关于如何使用 Python 处理地理空间数据的信息不像使用其他类型数据的信息那样容易获取之外,我不认为有什么好的理由。
这是一个系列的第一篇文章,重点关注使用 Python 处理地理空间数据的各种必须了解的方面。在接下来的几周里,我们将探讨使用地理空间数据的主要主题,包括文件格式、数据类型、坐标系和使用数据的工具。我会尽量保持这些帖子的篇幅。我希望我们在整个系列中对这个话题产生一种安慰,我们的恐惧会逐渐消失。
这篇文章的前 2/3 介绍了一些概念。在最后的 1/3 中,我们将尝试从 opentopography.org 的获取两个激光雷达栅格,并从数字表面模型和数字地形模型计算冠层高度模型。
我已经整理了文章,整理如下:
- 数据类型
- [计]元数据
- 实践栅格数据。
数据类型
地理空间数据中使用两种主要的数据类型:栅格数据和矢量数据。这可能不足为奇。如果你最近在电脑上做过任何视觉方面的工作,你可能会和两者都有过互动。下面我们将简要地看一下每一种数据。
栅格数据
栅格数据是网格中的数据。这和一张照片里的数据很像。在卫星图像的情况下,这些数据就像一张照片。这种地理空间栅格数据和照片栅格数据之间的区别在于,地理空间数据有一种将该数据与地球上特定位置相关联的方式。与只包含整数数据的图像不同,它还可以包含浮点或整数数据。

作者图片
栅格数据也可以是分类数据。例如,您可以将地面覆盖分为低、中或高。在这种情况下,我们将整数数据映射到一个类别。

作者图片
矢量数据
矢量地理空间数据也非常类似于您可能在 Adobe Illustrator 中使用过的设计数据。同样,不同之处在于地理空间矢量数据被映射到地球上的特定位置。
矢量数据有三种类型。

作者图片
每种类型的矢量数据可以表示不同种类的信息。
- 一个点可以代表像一棵树或一栋建筑这样的东西的位置。
- 一条线可以代表一条道路或者一条河流。
- 一个多边形可以代表地理单元,比如一个国家、州的边界,或者其他区域,比如国家公园。
栅格元数据
地理空间栅格使用元数据向我们传达数据的许多重要方面。以下是栅格文件元数据中的一些基本元素。
坐标参考系统
我在上面提到过,地理空间数据的本质特征是它将数据连接到地球上的特定点。建立这种联系的机制是坐标参考系统或 CRS。
有一个重要的复杂因素。地球是一个球体,我们的数据表示是二维的。像任何纸质地图一样,我们的数据被投影到二维空间。不同的 CRS 处理这个投影的方式不同。例如,一些 CRS 只关注地球上的特定区域。其他的更普遍,用于投影地球表面的大部分。有很多很多不同的 CRS。一些比另一些更常见,但出于我们的目的,我们需要知道当我们可视化或根据它们进行计算时,具有不同 CRS 的两个栅格不会对齐。我们需要有一种方法将两个栅格转换成相同的 CRS(在以后的文章中会有更多的介绍)。
NoData 值
元数据的另一个关键部分是 NoData 值。栅格的格网格式要求每个方格中都有数据。NoData值告诉我们栅格所使用的数字,以表明它对于栅格的该部分没有值。对于整型和浮点型栅格,常见的 NoData 数据分别为-9999和-3.4e+38,但可以使用任何值。
如果您想更深入地了解地理空间栅格数据中常见的其他元数据,这个 ArcGIS 帮助页面很好地展示了各种可能性。
使用栅格数据
我们都可以承认,我们绝不是地理空间数据方面的专家,但这不应该阻止我们深入研究,看看我们能完成什么。
对于这个例子,我从加利福尼亚州约塞米蒂山谷的illioulette Creek Basin LiDAR 调查中抓取了一些激光雷达数据,2018 年数据集在opentopography.org上。数据是开源的,界面也很容易使用。如果你愿意,我已经上传了我使用的与本文相关的 Github 库的确切数据。
接下来我们需要的是一个工具,它可以让我们读入栅格数据。xarray是一个 python 库,允许我们在 python 中处理多维标签数组。对于多维数据来说有点像熊猫。rioxarray是构建在xarray之上的一个包,它提供了读写栅格文件及其元数据的方法。为了加载我们的数据,我们执行以下操作。
import rioxarrayterrain_file = 'data/rasters_CA18Thompson/output_be.tif'
terrain = rioxarray.open_rasterio(terrain_file)surface_file = 'data/rasters_CA18Thompson/output_hh.tif'
surface = rioxarray.open_rasterio(surface_file)
如果我们看一下surface,我们可以看到一些关于数据的信息。

作者图片
我们可以看到这个栅格文件有一个波段,x 轴和 y 轴上的数据都是浮点 64 值。我们还可以看到与上面讨论的NoData值相同的_FillValue。为了更直接地查看元数据,我们可以检查我们的xarray的单个属性。特定于栅格的数据位于一个名为rio的子类中。
# Get no data value
surface.rio.nodata
1.70141e+38
# Show surface CRS
print(surface.rio.crs)
EPSG:6340
上面告诉我们,我们的NoData值是1.70141e+38,我们的 CRS 是 EPSG:6340。前往https://epsg.io/6340了解更多关于该 CRS 的信息。出于我们的目的,我们只需要知道这两个文件的 CRS 匹配。幸运的是,确实如此。
# Show terrain CRS
print(terrain.rio.crs)
EPSG:6340
让我们来看看如果绘制其中一个栅格会发生什么。
plt.figure(figsize=(10,8))
surface.plot()

作者图片
嗯。这没有你希望的那么令人印象深刻,不是吗?查看绘图右侧的彩色地图,我们看到 max 值相当大,与我们的NoData值非常相似。我们忘记了用np.nan替换 no data 值,这是表示缺失值的 pythonic 方式。最简单的方法是将文件通过masked=True重新导入到rasterio方法。
# reload the surface raster masking nan values
surface = rioxarray.open_rasterio(surface_file, masked=True)# plot surface raster
plt.figure(figsize=(10,8))
surface.plot()
plt.title('Digital Surface Model')

作者图片
那就好多了!现在很清楚,白色区域在数据集之外,不包含测量值。让我们来看看表面模型栅格。
# reload the terrain raster masking nan values
terrain = rioxarray.open_rasterio(terrain_file, masked=True)# plot terrain raster
plt.figure(figsize=(10,8))
terrain.plot()
plt.title('Digital Terrain Model')

作者图片
这看起来非常类似于表面模型。
最后,我们将做一点栅格数学。我们想知道树冠的高度。换句话说,我们希望看到树木和地面植被相对于地面的高度。我们有一个给我们土地上所有物体轮廓的表面模型和一个给我们土地本身轮廓的地形模型。为了得到冠层高度模型,我们从表面模型中减去地形模型。
# calculate canopy height model
canopy = surface - terrain# plot surface raster
plt.figure(figsize=(10,8))
canopy.plot()
plt.title('Canopy Height Model')

我们找到了。我们可以看到,这个地区大部分是低或没有地面覆盖,一些稀疏的树木高达 60 米。
结论
我希望这篇文章的最大收获是地理空间数据没有那么可怕。它处理一个我们在平面数据集中通常不必处理的问题:将三维地球投影到二维栅格上。在本系列的后续文章中,我们将学习如何重新投影和处理来自不同来源的数据。不过,今天我们学习了一些基本术语,并使用了一些基本的地理空间 python 包。
这只是本系列的第一篇文章。如果您想在我发布本系列的下一篇文章时得到通知,请在 medium 上关注我。
资源
- 在由 Ryan Avery 维护的 carpentries 孵化器上使用 python 介绍地理空间栅格和矢量数据
- 什么是地理数据?在 ArcGIS 帮助页面中。
- OpenTopography.org—开源高分辨率地形数据和工具。
在 OpenCV 中使用图像像素

使用 OpenCV 获取和设置要处理的图像像素
像素是计算机视觉中图像的重要属性。它们是代表图像中特定空间的光的颜色强度的数值,是图像中最小的数据单位。
图像中的像素总数是其高度、宽度和通道的乘积。
由于 OpenCV 中的图像被读取为像素值的 Numpy 数组,因此可以使用数组切片操作来获取和处理由该区域的像素表示的图像区域。
切片操作用于检索序列的子集,例如列表、元组和数组,并且同样可以用于获得图像区域的像素值以进行处理,例如编辑、格式化或裁剪。
切片操作
脚本:使用切片操作获得列表的子集。

输出(图片由作者提供)。
请注意,我使用了索引值来分割字母列表。例如,传递起始索引 1(列表中第二个字母的索引)和 4 会返回列表的一部分,从第二个值到第四个值。
由于索引值以这种方式用于检索感兴趣的子集,所以它们也用于定位和检索图像中的感兴趣区域。
以图像中的某个区域为目标的切片由图像的两个轴(水平轴(X)和垂直轴(Y))的起始值和结束值定义,格式如下:
图像【startY: endY,startx:endX】
它返回所需感兴趣区域的(图像像素的)Numpy 数组。
那么,我们如何确定感兴趣区域的 X 轴和 Y 轴的起始值和终止值呢?
这些值(startX,endX,startY,endY)是标出感兴趣区域的坐标值。当使用 OpenCV 显示时,这些值不会显示在图像旁边,但我们可以使用其他应用程序(如 Photoshop、Corel Draw、Paint e.t.c)或其他 python 可视化库(如 Matplotlib)来显示图像及其 X 和 Y 坐标值。
一如既往,这在实践中更容易理解。让我们使用matplotlib . py plot显示一幅图像,从中我们可以检索出绘制出图像中感兴趣的目标区域的坐标。
我用加纳共和国国旗的图像来证明这一点。在这里,我的目标是图像中围绕黑星的区域。
获取感兴趣区域的坐标值。
- 使用 Matplotlib 加载并显示图像。

输出:加载的图像及其 X 和 Y 坐标。(图片由作者提供)。
如您所见, plt.imshow 函数返回读取的图像以及 x 和 y 轴的坐标值。
然后,我们可以检索感兴趣区域(黑星)的起点和终点坐标值。下面是一个如何追踪星形区域的值的图。

追踪黑星区域的坐标值。(图片由作者提供)。
该图像显示了如何追踪目标黑星周围区域的坐标。
从图像中,我们可以检索坐标(startY(y1),endY(y2),startX(x1),endX(x2))。然后,我们可以定义两个轴的起点和终点坐标,并将星形裁剪为:
图像【y1: y2,x1:x2】
if we get y1, y2 = [145, 295] and x1, x2 = [245, 400]
那么画出黑星的区域应该是:
black_star = image[145:295, 245:400]
这将返回绘制感兴趣区域(在本例中为黑色星号)的像素值(在 Numpy 数组中)。
现在,我们可以使用这种技术来针对各种图像处理对图像区域进行定位和切片。
我使用一个来自尼日利亚埃努古的煤矿工人雕像的图像演示了切片操作的常见图像处理技术。
使用切片操作裁剪图像
1。加载并显示原始图像

输出:显示加载的图像(作者的图像)。
2。获取图像的空间尺寸

输出:显示图像的空间维度。(图片由作者提供)。
3。使用尺寸来裁剪图像
裁剪掉图像的左上角

输出:图片左上角(图片由作者提供)。
裁剪掉图像的右上角

输出:图片右上角(图片由作者提供)。
裁剪掉图像的左下角

输出:图片左下角(图片由作者提供)。
裁剪掉图像的右下角

输出:左下角(图片作者)。
4。使用尺寸将部分图像设置为特定颜色。

输出:设置左上角为绿色(图片由作者提供)。
概要:
图像像素是表示图像中颜色强度的数值。OpenCV 获取和设置不同图像处理的图像像素的过程是基于 Numpy 数组的切片操作。对像素值进行切片在裁剪、重置、复制或增强图像时非常有用。
在构建你的 ML 模型时,不要陷入不平衡数据的陷阱
利用这些技术带来平衡并提高性能

什么是不平衡数据
不平衡数据是机器学习应用中极其常见的一种情况。当您的数据中有许多观察值代表一种类型的类和其他小得多的类时,就会出现不平衡数据。这方面的例子可能是与合法购买相关的欺诈性信用卡交易或与合法电子邮件相关的潜在垃圾电子邮件(现在合法电子邮件也可能是少数)。
挑战是什么?
你会遇到的不平衡数据的挑战是算法如何从你的数据中学习。当您构建训练/测试数据集时,表示少数类的观察值数量将比多数类少得多。该算法没有足够的数据来真实地模拟少数阶级的样子,并最终过度偏向多数阶级。使用简单的精度指标来评估你的模型或者寻找高精度或者向少数类召回可能是特别危险的。稍后将详细介绍评估指标。
处理不平衡数据的方法
幸运的是,在您的培训和测试阶段,只要有一点额外的思考和设置,您就可以处理不平衡的数据;有许多方法可以处理不平衡的数据。以下是您可以管理它的一些(不是全部)方法:
- 算法选择
- 不平衡数据的交叉验证
- 生成合成数据
- 选择正确的绩效指标
我如何知道我是否有不平衡的数据
检查不平衡数据只需要一行简单的代码。取你的目标变量,使用下面的代码
df['Target'].value_counts()
1 17433
0 5193
通过检查项目的数量,我们可以快速而容易地看到,1类比0类多得多。
算法选择
先说最简单的方法。在这个例子中,我将参考 Scikit-Lern 的算法选择以及它们如何处理不平衡数据。他们的很多算法都支持一个可以设置为balanced的class_weight参数。例如RandomForest、LogisticRegression、Perceptron、SVM都支持此参数。根据文档:
class_weight{"balanced "," balanced_subsample"},字典或字典列表,default=None 与表单
{class_label: weight}中的类相关联的权重。如果没有给定,所有类的权重都应该是 1。对于多输出问题,可以按照与“y”列相同的顺序提供字典列表。“平衡”模式使用
y的值自动调整与输入数据中类别频率成反比的权重,如n_samples / (n_classes * np.bincount(y))
如果您发现其中一个分类器在您的模型选择阶段表现良好,您可以简单地继续使用该算法。
训练-测试分离和交叉验证
当数据不平衡时,您需要以保持类比例的方式分割数据,也称为分层分割。train_test_split自动默认通过stratify=None参数分割数据。为了对你的分割进行分层,然后使用你的目标变量stratify=y。
X_train, X_test, y_train, y_test = train_test_split(X,
y,
test_size=0.33,
random_state=53,
stratify=y)
那么分层是什么意思呢?据维基百科:
在统计学中,分层抽样是一种从可以划分为子人群的人群中抽样的方法。
换句话说,它考虑了子种群或类的大小,并在分割时考虑了这一点。
当执行交叉验证进行模型评估时,您应该使用StratifiedKFold交叉验证器。这将确保通过保留每个类别的样本百分比来进行折叠。
重要提示: 这一步很关键。如果没有这一点,您可能会在测试中以零个少数类样本结束。
重击
合成少数过采样技术(SMOTE)使用最近邻方法来生成新的少数类样本。该方法仅应用于训练数据,然后在原始的未受影响的测试分区上进行测试。这里选择的方法是首先对少数类进行过采样以使其平衡,然后对其进行欠采样以减小大小和膨胀。

作者图片
通过综合生成类似于但不等同于其他少数类观察值的少数类观察值,我们可以提高模型在少数类上的性能。
包 imblearn 包含 SMOTE 算法,可以很容易地集成到 SKLearn 管道中。
重要说明: 永远不要对你的 测试 数据集执行 SMOTE,只对 训练 分区!
实现注意: 必须导入 *imblearn* 管道而不是 sklearn 管道,否则无法工作。
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
pipeline = Pipeline([('prep',column_trans),
('over', SMOTE(random_state=42)),
('under', RandomUnderSampler(random_state=42)),
('clf', clf)])
更多:关于使用管道的更多信息,请查看我的帖子:在 Sci-kit 中使用管道学习。
选择正确的评估指标
最后,对于不平衡的数据,选择正确的评估指标至关重要。如果你依赖于准确性,你很可能不会达到你认为的结果。根据您想要的结果,您可以查看几个不同的指标,如精度、召回和F1-分数。请看我的另一篇帖子:停止用精度来评价你的分类模型。
结论
在机器学习的实际应用中,不平衡数据无处不在。我们很容易陷入忽略不平衡数据的陷阱,却发现数据并没有按照我们想象的方式运行。幸运的是,有几种方法可以处理这一点,例如选择一种处理不平衡数据的 算法,分割您的数据,并以分层的方式交叉验证您的模型,利用 SMOTE 综合生成数据,最后但并非最不重要的是,为您想要的结果选择最佳评估指标。快乐模型建筑!
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
参考文献
在 python 中使用 JSON 数据

在本文中,我想重点介绍一种叫做 JSON 的格式类型。我很确定你以前听说过 JSON。但是如果没有,让我简单地向你描述一下 JSON。
JSON 是 JavaScript 对象符号,其灵感来自于 JS 编程语言处理对象字面语法的子集。然而,它同时是语言不可知的,这意味着如果你不写 JavaScript 也没关系。您可以用任何其他编程语言处理 JSON 格式的数据。
但是为什么 JSON 对你很重要,你关心数据科学对吗?我在构建自己的 python 项目,试图获得数据科学/机器学习工程师的工作时,偶然发现了 JSON。稍后会详细介绍。【小节:“真实世界?”]因此,如果你和我走在同一条路上,并且想学习另一种有用的工具。这篇文章是给你的。即使您正处于职业生涯的不同阶段,我也相信,既然标题引起了您的注意,您一定想更新关于 JSON 格式的知识来解决当前的问题,或者您想在您的数据科学 python 库中寻找新的技能。
JSON 看起来怎么样
好的,JSON 看起来怎么样,它支持什么?
这是文件example.json
如你所见,它支持基本类型,如字符串、整数、列表和嵌套对象。它看起来像 Python。但是请注意,我提供了一个读转换和写转换表,以便您了解 JSON 和 python 之间的区别。
为什么使用 JSON 模块在 Python 中存储数据?
- JSON 格式使程序员能够将简单的数据结构转储到一个文件中,并在需要时将它们加载回程序中
- 使用 JSON 格式的文件可以在其他语言的程序之间共享数据。
由于 JSON 格式的优势来自于存储和获取数据,所以本文将与您分享一些函数和示例,以便下次您想要处理数据时,JSON 格式技巧就在您的掌握之中。
如果你想要更多关于这个话题的文章,你有必要了解一些词汇。将数据编码成 JSON 称为序列化(数据存储为一系列字节)。从 JSON 格式的文件中获取数据的反向过程称为反序列化。但是我觉得对于基本任务来说没必要知道这些。所以现在开始做一些体验吧。
数据到 json —序列化
假设您构建了一个创建数据的程序,但是您还想与其他用户/程序共享数据和底层信息。这就是为什么您想要获取数据并将其存储在 JSON 格式的文件中。但是数据是如何翻译的呢?我给你提供一个 Python 的换算表。

按作者分类的表格
好了,现在我们知道了在存储过程中我们可以从转换中得到什么,我们可以看看内置的 json 包中的函数。
import json
json 库中有两个函数叫做json.dump()和json.dumps(),它们可能用于序列化。
我们一次看一个。
json.dump()将数据对象作为 json 字符串存储在。json 文件
json.dump(data, file_object)函数接受两个参数:
- 需要写入 JSON 文件的数据。
- 可用于保存数据的文件对象
example.json 如下所示
json.dumps()将 python 对象转换成 json 字符串
这很简单。
JSON 到数据—反序列化
同样,反序列化是将 JSON 数据转换成本地数据类型的过程。这里,我们将 JSON 数据转换回 Python 中的字典。但是我们必须再次考虑 Python 的转换表,以便意识到可能发生的错误。

按作者分类的表格
看,如果我们编码一个元组,它变成一个数组,数组变成一个列表。请记住这个。
接下来,我们想要检查用于反序列化的两个函数。
json.load()将 json 文件读入 python 类型
json.load(filename)
数据来自example.json文件,并存储在名为 data 的字典中。
json.loads()将 json 字符串转换成 python 类型
真实世界?
我在介绍中已经提到,我在构建自己的 python 项目试图获得数据科学/机器学习工程师的工作时偶然发现了 JSON。我的目标是将我从 API 收集的数据存储到一个文件中,这样我可以在以后的测试中使用它。从 API 接收 json 字符串形式的数据是很常见的。通过名为requests的库,你可以从一个 API 获取数据。然后,您需要将数据转换成 python 对象,这可以很容易地完成,只需使用response.json()而无需任何参数。
现在,假设您进行了一些争论,并决定获取数据的一个子集,并希望将其存储起来以备后用。您了解到您可以使用json.dump(data, filename)并在以后使用它。耶!
结论
很高兴您学会了如何以一种在许多其他语言中都可以管理的方式存储数据。您还知道在哪里查找换算表,并且您看到了一些无需太多努力就可以进行更改的示例,这些示例对您自己的需求非常有用。我将关键的工作流程步骤总结为以下 4 点。
- 导入
json包 - 用
json.load(JSON_string)或json.loads(filename)读取数据 - 处理数据
- 用
dump(data, filename)或dumps(data)写入更改的数据
谢谢你坚持到最后。我欢迎你阅读我的另一篇文章。
使用多索引熊猫数据框架
了解如何轻松使用多索引数据框架

由 Unsplash 上的absolute vision拍摄
大多数熊猫数据帧的学习者都熟悉数据帧的样子,以及如何使用 loc[] 和 iloc[] 索引器方法提取行和列。然而,当涉及到多索引数据框架时,事情会变得非常棘手。多索引(也称为层次索引)数据帧使用多个列作为数据帧的索引。一个多索引的数据框架允许你以多维的格式存储你的数据,并且打开了许多令人兴奋的来表示你的数据。
在本文中,我将向您介绍如何操作多索引数据帧,以及您可能会遇到的一些陷阱。也就是说,系好安全带——这将是一次过山车之旅!开个玩笑,会很有趣的!
加载数据集
对于本文,我将使用下面的数据框架进行说明:
import pandas as pdscores = {'Zone': ['North','South','South',
'East','East','West','West','West','West'],
'School': ['Rushmore','Bayside','Rydell',
'Shermer','Shermer','Ridgemont',
'Hogwarts','Hogwarts','North Shore'],
'Name': ['Jonny','Joe','Jakob',
'Jimmy','Erik','Lam','Yip','Chen','Jim'],
'Math': [78,76,56,67,89,100,55,76,79],
'Science': [70,68,90,45,66,89,32,98,70]}df = pd.DataFrame(scores, columns =
['Zone', 'School', 'Name',
'Science', 'Math'])
df

获取多索引数据帧
当您在 df 数据帧上应用聚合函数时,您将获得一个多索引数据帧:
**df_result_zone_school** = df.groupby(['Zone','School'])**.agg(
{
'Science':['mean','min','max'],
'Math':['mean','min','max']
})**
df_result_zone_school
结果看起来像这样:

您拥有的是一个具有多列的数据帧,这些列充当数据帧的索引(通常称为多索引数据帧)。此外,列标题还包含多个级别。
在下面几节中,我将向您展示如何从多索引数据帧中提取行和列。
使用列
让我们先从柱子开始。您可以使用列属性来获取列标题列表:
df_result_zone_school**.columns**
您将看到以下内容:
MultiIndex(levels=[['Science', 'Math'], ['mean', 'min', 'max']],
codes=[[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]])
标题按级别分组,第一列属于 0 级,第一列属于下一级:

您可以使用列获得每个级别的值。get_level_values() 函数:
print(df_result_zone_school.columns.get_level_values(0))
print(df_result_zone_school.columns.get_level_values(1))
这将打印以下内容:
Index(['Science', 'Science', 'Science', 'Math', 'Math', 'Math'], dtype='object')Index(['mean', 'min', 'max', 'mean', 'min', 'max'], dtype='object')
提取具有 0 级标题的列
如果您想要提取一个单独的列,比如说科学列,您可以简单地指定 0 级列名:
df_result_zone_school[**'Science'**]
这将产生以下输出:

请注意,结果中不包括科学列标题。要包含它,请用列表( [] )将列名括起来:
df_result_zone_school[**['Science']**]
科学列标题现在包含在结果中:

如果需要其他列,只需将列名添加到列表中:
df_result_zone_school[['Science'**, 'Math'**]]
科学和数学列现在都会出现在结果中:

提取具有 0 级和 1 级标题的列
如果你只想得到理科,意思是列呢?在这种情况下,可以通过科学和意思作为元组:
df_result_zone_school[**('Science','mean')**]
这将返回一个系列(即一个多索引系列):
Zone School
East Shermer 55.5
North Rushmore 70.0
South Bayside 68.0
Rydell 90.0
West Hogwarts 65.0
North Shore 70.0
Ridgemont 89.0
Name: (Science, mean), dtype: float64
希望结果是数据帧吗?使用列表( [] )包装元组:
df_result_zone_school[**[('Science','mean')]**]

在科学下的和都是指和最小栏呢?您可能会尝试这样做:
df_result_zone_school[('Science',**['mean','min']**)]
不幸的是,这不起作用。相反,您必须使用 loc[] 索引器:
df_result_zone_school**.loc**[**:**, **('Science',['mean','min'])**]

上面也可以写成:
df_result_zone_school.loc[:,**[('Science','mean'),
('Science','min')]**]
使用这种技术,您现在可以检索基于 0 级和 1 级标题的附加列:
df_result_zone_school.loc[:,[('Science','mean'),
('Science','min'),
**('Math','mean')**]]

务必注意,下面的将而不是将工作:
df_result_zone_school.loc[:, [('Science',['mean','min']),
('Math','mean')]]
分割列
您可以对 0 级列标题执行切片:
df_result_zone_school.loc[:,**'Science':'Math'**]

在一级标题上切片怎么样?像下面这样的?
df_result_zone_school.loc[:,('Science','mean':'max')]
不,这不行。要对 1 级头进行切片,您需要将开始和结束指定为元组:
df_result_zone_school.loc[:,**('Science','mean'):('Science','max')**]

下面是另一个跨 0 级和 1 级头切片的例子:
df_result_zone_school.loc[:,**('Science','mean'):('Math','min')**]

使用行
现在您已经查看了列,是时候查看行了。首先要做的是检查数据帧的索引:
df_result_zone_school**.index**
您应该看到以下内容:
MultiIndex([( 'East', 'Shermer'),
('North', 'Rushmore'),
('South', 'Bayside'),
('South', 'Rydell'),
( 'West', 'Hogwarts'),
( 'West', 'North Shore'),
( 'West', 'Ridgemont')],
names=['Zone', 'School'])
索引中的每一项都是包含 0 级(区)和 1 级(学校)索引的元组。

与列标题一样,您可以使用 get_level_values() 函数来获取各个级别的索引:
df_result_zone_school.index**.get_level_values(0)** # level-0
与列标题不同,索引的每个级别都有一个名称。除了通过级别号获取索引,您还可以直接使用其名称:
df_result_zone_school.index.get_level_values('**Zone**')
上述两个语句的结果是:
Index(['East', 'North', 'South', 'South', 'West', 'West', 'West'], dtype='object', name='Zone')
要获得 1 级指数:
df_result_zone_school.index.get_level_values(1) # level-1
*# OR*
df_result_zone_school.index.get_level_values('**School**')
结果是:
Index(['Shermer', 'Rushmore', 'Bayside', 'Rydell', 'Hogwarts', 'North Shore', 'Ridgemont'],
dtype='object', name='School')
提取索引级别为 0 的行
现在让我们尝试获取属于南部区域的所有行:
df_result_zone_school.loc[**'South'**]

请注意,结果中未显示索引 South 。为了显示这一点,将它放在一个列表中( [] ):
df_result_zone_school.loc[**['South']**]

如果你想在南区和西区都有学校:
df_result_zone_school.loc[**['South','West']**]

提取具有 0 级和 1 级索引的行
如果您想让湾畔学校位于南部区域,请将这两个值包含在一个元组中:
df_result_zone_school.loc[**('South','Bayside')**]
结果将是一个系列(多指标系列):
Science mean 68.0
min 68.0
max 68.0
Math mean 76.0
min 76.0
max 76.0
Name: (South, Bayside), dtype: float64df_result_zone_school.loc[[**('South','Bayside')**]]
要获得 dataframe 形式的结果,请使用 list( [] )包装元组:
df_result_zone_school.loc[**[('South','Bayside')]**]

如果您想要分别来自南部和西部区域的 Bayside 和 Ridgemont 学校,请将它们作为元组传递:
df_result_zone_school.loc[
**[('South','Bayside'),
('West','Ridgemont')]**
]

把霍格沃茨和里奇蒙从西区区弄来怎么样?下面这个说法能行得通吗?
*# this wouldn't work*
df_result_zone_school.loc[('West',['Hogwarts','Ridgemont'])]
不幸的是,上面的方法行不通。要使它工作,您需要在元组的后面添加一个逗号(,) :
*# note the comma at the end*
df_result_zone_school.loc[('West',['Hogwarts','Ridgemont'])**,**]*# this will also work*
df_result_zone_school.loc[('West',['Hogwarts','Ridgemont'])**,:**]

解决上述问题的另一种方法是指定两个元组:
df_result_zone_school.loc[
[**('West','Hogwarts'),
('West','Ridgemont')**]
]
使用这种方法,您可以检索任何想要的行:
df_result_zone_school.loc[
[
** ('West','Hogwarts'),
('West','Ridgemont'),
('South','Bayside')**
]
]

切片行
当然,您可以根据索引对行进行切片。假设您想要从北部到西部区域的所有学校:
df_result_zone_school.loc[**'North':'West'**]

从南面的,海湾边的到西面的,霍格沃茨的所有学校怎么样?没问题:
df_result_zone_school.loc[
**('South','Bayside'):('West','Hogwarts')**
]

把霍格沃茨带到西区的里奇蒙怎么样?既然都属于西区,下面还能工作吗?
*# this wouldn't work*
df_result_zone_school.loc[
('West','Hogwarts':'Ridgemont')
]
相反,您应该这样做:
df_result_zone_school.loc[
**('West','Hogwarts'):('West','Ridgemont')**
]

按位置获取行和列
通过位置获取行和列比通过值获取更直接。以下是一些使用 iloc[] 索引器的示例:
df_result_zone_school.iloc[**[0],[0]**]

df_result_zone_school.iloc[**[0],[0,1]**]

df_result_zone_school.iloc[**[1,2,3]**]

df_result_zone_school.iloc[**2:5, 3:**]

基于索引值搜索行
有时基于数组索引提取行更容易。例如,假设您想要获取属于北和南区域的所有行。你可以得到 0 级索引,然后使用 isin() 函数,就像这样:
condition = df_result_zone_school.index.**get_level_values(
'Zone').isin(['North','South'])**
df_result_zone_school[condition]

如果你想得到所有以' th 结尾的区域,你可以使用 endswith() 函数:
condition = df_result_zone_school.index.**get_level_values(
'Zone').str.endswith('th')**
df_result_zone_school[condition]

只上赖德尔和谢默学校怎么样?检查过了!
condition = df_result_zone_school.index**.get_level_values(
'School').isin(['Rydell','Shermer'])**
df_result_zone_school[condition]

最后,如果你想得到 Rydell 和 Shermer 学校,以及 West 区的所有学校呢?
condition1 = df_result_zone_school.index**.get_level_values(
'School').isin(['Rydell','Shermer'])**
condition2 = df_result_zone_school**.index.get_level_values(
'Zone').isin(['West'])**
df_result_zone_school[ **condition1 | condition2** ]

重置索引
如果你到现在还在关注,那就太好了!还记得我们的多索引数据框架吗?
df_result_zone_school

有时,您希望将索引扁平化,以便更容易处理数据帧。您可以通过 reset_index() 函数来实现:
df_result = df_result_zone_school**.reset_index()**
df_result

reset_index() 函数具有级参数,允许您指定要移除的索引。默认情况下,它会删除所有级别的索引。所以上面的语句本质上是这样的:
df_result = df_result_zone_school.reset_index(
**level=['Zone','School']**)
您可以使用索引的级别号或索引名来指定要删除的索引:
df_result_school = df_result_zone_school.reset_index(level=[0])
# same as
df_result_school = df_result_zone_school.reset_index(level=
['Zone'])
df_result_school
上述语句从索引中删除了区域:

以下语句将学校从索引中删除:
df_result_zone = df_result_zone_school.reset_index(level=[1])
# same as
df_result_zone = df_result_zone_school.reset_index(level=['School'])
df_result_zone

您可以使用 set_index() 函数指定一列作为索引,而不是使用 reset_index() 函数从索引中删除一列。调用 df_result 数据帧:

以下语句将区域设置为索引:
df_result.**set_index('Zone')**

最后,下面的语句将区域和学校都设置为索引:
df_result.set_index(**['Zone','School']**)

结论
我希望这篇文章使处理多索引数据框架不像最初看起来那么可怕。记住我在本文中讨论的技术的最好方法是自己尝试代码示例。如果你有进一步的问题,请留下你的评论,我会尽量在这里回答。玩得开心!
https://weimenglee.medium.com/membership
使用对象存储和 Jupyter 笔记本
理解对象存储和 Jupyter 笔记本的概念。了解在 LakeFS 中集成它们的最佳平台。
面向通用用途的对象存储和 Jupyter 笔记本正在兴起。对大数据和数据元素的需求不断增加。虽然需要对象存储来存储持续增长的大量数据元素,但 Jupyter 笔记本电脑可以计算和分析这些数据集和数据。
在本文中,我们将介绍对象存储的概念,并承认它的好处。然后,我们将了解 Jupyter 笔记本电脑及其在现代计算中的应用范围。最后,我们将了解 LakeFS,它是集成对象存储和 Jupyter 笔记本的最佳平台之一。
了解对象存储:

数据是一种宝贵的资源,互联网上有大量的数据(通常是非结构化的)和数据集。以高度可伸缩的方式存储这些数据元素的最佳方式是使用对象存储。它具有很高的耐用性,因为存储的信息是安全的,即使某个磁盘出现故障,数据也不会丢失。
对象存储将体系结构中的数据元素作为对象进行管理。它不同于其他存储体系结构,如文件系统或块存储系统。前者利用文件存储数据,而后者使用扇区和磁道内的块。
因此,它是安全、灵活和可扩展的。这是数据恢复或备份系统的最佳选择之一。对象存储的功能允许其用户存储大量数据,这在人工智能、分析和云计算中尤其有用。当与 Jupyter 笔记本结合使用时,它会非常有价值。
大公司和科技巨头利用对象存储来保留大量数据。对象存储在特定的公司中有其独特的使用案例,如在脸书上存储照片、图片和图像,在网飞上收集电影,在 Spotify 上收集歌曲。在线协作服务 Dropbox 利用对象存储来存储多个文件。
使用 Jupyter 笔记本:

照片由 Ashley West Edwards 在 Unsplash 拍摄
Project Jupyter 对程序员来说是一个强大的开源环境,因为它具有高度的交互性,并支持动态范围的编程语言,主要是 Python、R 和 Julia。
Jupyter 项目最重要的特征之一是 Jupyter 笔记本。这些笔记本可以让你开发各种编程项目,分析数据,创建许多机器学习和深度学习模型,等等。
Jupyter Book 允许用户从计算材料中构建书籍、内容和文档。Jupyter 笔记本最好的部分是它为用户提供的众多选项和独特性。您可以混合使用降价来构建代码块,以解释您的代码并使其更具代表性。您还可以在 Jupyter 笔记本上添加额外的数学公式,以及重组文本。最后,您可以下载各种格式的笔记本,包括 PDF 文件、HTML 网页、Python 文件或笔记本文件等输出格式。
如果你不想在你的系统上安装 Jupyter 笔记本的版本,你也可以使用这些笔记本的免费版本,可以从谷歌联合实验室获得。Colaboratory(也称为 Colab)允许用户在免费 Jupyter 笔记本的帮助下开发 Python、数据科学和可视化项目。这些笔记本运行在云上,有免费的资源,如 GPU 和 TPU 可用。你可以将这些笔记本保存在谷歌硬盘上。
将 Jupyter 和对象存储与 LakeFS 集成:

对象存储与 Jupyter 笔记本电脑相结合产生了极佳的效果。对象存储和 Jupyter Notebook 可以具有显著的互连性,以实现更好、更高效的结果。
由于对象存储中的数据是按照对象来管理的,因此它们可以存储大量的信息,这些信息可以在 Jupyter 笔记本中使用,以创建建设性的高质量项目。但是,集成 Jupyter 笔记本和对象存储组件的最佳平台是什么?
LakeFS公司提供了一个最好的平台来整合对象存储的质量,并将其与 Jupyter 笔记本结合使用。
LakeFS 是一个开源平台,可以创建一个定制系统来有效地管理您的数据元素。它提供对象存储的功能,并允许您访问各种资源,以利用其功能来执行复杂的任务,包括数据科学和分析。下图是其示意图。

LakeFS 旨在解决 GitHub 的一些与更广泛的数据存储相关的问题。比起 GitHub 这样的网站,你可以使用云来存储更多的对象。支持的云平台主要有两个,分别是 Amazon Web Services (AWS)和 Google 对象存储云平台,这些依次集成到 Spark、ML flow、Jupyter notebook 等。
借助这种架构,您可以在 Jupyter 笔记本电脑上利用对象存储的属性。这些有各种好处,包括为各种计算问题存储更大的数据集和文档,包括大数据、机器学习、深度学习、计算机视觉、自然语言处理等等。
由于 Jupyter 笔记本支持 Python,我们将研究 LakeFS 的一些 Python 代码。要开始安装过程,您可以通过在命令提示符下键入以下行来安装必要的依赖项。
pip install bravado==10.6.2
一旦安装了所有必需的依赖项,就可以开始处理各种任务了。借助 Bravado 包,您可以在运行时生成特定的动态客户端。这种生成是由 OpenAPI 定义通过 lakeFS 服务器完成的,它为您提供了指定 URL 的 JSON 扩展。如果您选择在您的系统上本地运行该平台,您可以在http://localhost:8000/swagger . JSON上这样做。
让我们看几个特定于任务的代码块来理解它们的用法。首先,让我们试着用 Bravado 生成一个 Python 客户机。随着 Bravado 包的安装,唯一的其他要求是从 OpenAPI 获得一个 URL。在我们达到这两个要求之后,您可以使用下面的代码块来生成一个客户端:
from bravado.requests_client import RequestsClientfrom bravado.client import SwaggerClienthttp_client = RequestsClient()http_client.set_basic_auth('localhost:8000', 'AKIAIOSFODNN7EXAMPLE', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY')client = SwaggerClient.from_url('http://localhost:8000/swagger.json', http_client=http_client, config={"validate_swagger_spec": False})
一旦准备好了客户机对象,就可以用它来与 API 进行交互。仅举一个创建清单和存储库的简单例子,您可以使用下面的代码块。
client.repositories.createRepository(repository={ 'id': 'example-repo', 'storage_namespace': 's3://storage-bucket/repos/example-repo', 'default_branch':'main' }).result()# output:# repository(creation_date=1599560048, default_branch='main', id='example-repo', storage_namespace='s3://storage- bucket/repos/example-repo')
在本文中,我们已经讨论了一些通用代码。为了更详细地了解如何在 LakeFS 中使用 Jupyter 笔记本上支持的 Python API,我强烈推荐查看下面的网站。
结论:
在本文中,我们理解了对象存储和 Jupyter 笔记本的概念。然后,我们研究了将这两个方面集成到一个组件中的最佳方法之一,以便在 LakeFS 的帮助下实现更好的功能和更快的计算。
我强烈推荐查看下面的网站指南,通过他们的文档了解更多关于 LakeFS 的理论和技术方面的内容。对于进一步解释使用 LakeFS 平台可以实现的目标,这应该是一个很好的起点。
如果你对这篇文章中提到的各点有任何疑问,请在下面的评论中告诉我。我会尽快给你回复。
看看我的其他一些文章,你可能会喜欢读!
</5-reasons-why-you-should-code-daily-as-a-data-scientist-fa7fc6dc92c4> </10-best-free-websites-to-learn-more-about-data-science-and-machine-learning-f2c6d7387b8d>
谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!
使用 OpenStreetMap 数据
如何使用开放数据进行商业洞察

乔伊·孙约在 Unsplash 上的照片
在本演练中,我将使用 BigQuery 中的 OpenStreetMap 数据。谷歌在他们的公共数据集中免费发布,这太棒了。所以你可以用 SQL [1]轻松查询地理信息。谷歌上传了一次他们的数据集,但没有任何更新。如果您需要更新的数据,那么 OpenStreetMap API 结合一些简单的 python 代码可能是您的问题的一个可能的解决方案。
OpenStreetMap 的用例
OpenStreetMap.org 是一个国际项目,成立于 2004 年,目标是创建一个免费的世界地图。为此,我们收集了世界范围内的公路、铁路、河流、森林、房屋等数据。
虽然来自 OpenStreetMap 的数据是免费的,但你可以很好地得出一定的商业优势。我帮助开发或至少参与开发的一些示例如下:
- 地理分析(我的客户在哪里?)
- 市场分析(我的竞争对手在哪里有门店?)
- 旅游优化(如何优化物流路线?)
- 欺诈检测(运输发票是否合理?)
- 还有更多
什么是嵌套数据?
从 BigQuery 数据集中查询数据将返回嵌套的数据字段。BigQuery 支持从支持基于对象的模式(例如 JSON)的源格式中加载和查询嵌套和循环数据。下面是嵌套数据的简短说明:

嵌套和重复数据的图示-按作者分类的图像
地址列包含一个值数组。数组中的不同地址是重复出现的数据。每个地址中的不同字段是嵌套数据。
真实的例子
对于使用 BigQuery 的示例演练,我在 geo_openstreetmap 数据集中使用了开放数据集 planet_features 。在这里,数据是以嵌套格式存储的,所以我们来看看:
SELECT * FROM `bigquery-public-data.geo_openstreetmap.planet_features` LIMIT 1000
这是以下输出中的结果:

输出—按作者分类的图像
结果乍一看还不错。与传统的关系数据库不同,我可以使用数组并保存额外的列。此外,整件事的效果非常好。现在,我想在德国找一家超市,应该使用简单的 where 子句,对吗?差不多了。在这里,你需要 Unnest、 的魔力
SELECT *
FROM `bigquery-PUBLIC- data.geo_openstreetmap.planet_features`
WHERE 'Netto' IN
(
SELECT value
FROM unnest(all_tags))
AND (
'addr:country', 'DE') IN
(
SELECT (KEY, value)
FROM unnest(all_tags))
AND (
'addr:city', 'Hamburg') IN
(
SELECT (KEY, value)
FROM unnest (all_tags));
这导致了期望的输出:

输出—按作者分类的图像
利用 功能,您还能够 展平数据并由此输出查询结果:
SELECT osm_id,tags
FROM bigquery-public-data.geo_openstreetmap.planet_features,
UNNEST(all_tags) as tags limit 100

拼合数据—按作者排列的图像
当在 BigQuery 中处理嵌套数据并想要查询一些数据时,这可能是您必须知道的最重要的事情。但是,如果您想将数据用于进一步的 ETL 过程,将数据存储在关系数据库中,或者需要键值对作为分类器的属性,该怎么办呢?这里,您不希望数据变平,因为这将导致重复的行,这里您希望数组中的键值对作为新列:
SELECT
(
SELECT osm_id) osmid,
(
SELECT value
FROM unnest(all_tags)
WHERE KEY = "Address") AS address,
(
SELECT value
FROM unnest(all_tags)
WHERE KEY = "name") AS name,
(
SELECT value
FROM unnest(all_tags)
WHERE KEY = "opening_hours") AS opening_hours,
(
SELECT value
FROM unnest(all_tags)
WHERE KEY = "organic") AS organic,
(
SELECT geometry) AS geometry,
FROM bigquery-public-data.geo_openstreetmap.planet_features
WHERE (
'Edeka' IN
(
SELECT value
FROM unnest(all_tags))
OR 'Rewe' IN
(
SELECT value
FROM unnest(all_tags))
OR 'Netto' IN
(
SELECT value
FROM unnest(all_tags)))
AND (
'addr:country', 'DE') IN
(
SELECT (KEY, value)
FROM unnest(all_tags)) -
AND (
'addr:city', 'Hamburg') IN
(
SELECT (KEY, value)
FROM unnest(all_tags));
这给了我们输出:

嵌套和重复出现的列数据—按作者分类的图像
最后,您可以通过 Google Data Studio 等商业智能工具来可视化数据——尤其是在使用 BigQuery 时。

用于可视化地理数据的 Google Data Studio 按作者分类的图片
在相关的用例中,数据也显示在地图上,以了解商店的分布情况。在仪表板中,一个底层表格也提供了更接近的见解。为了进行更深入的分析,这些数据还被用于即将到来的客户细分场景。
结论
除了 OpenStreetMap API 和 Googles 的公共数据集之外,你还可以找到更多的数据集用于你的分析。在这里,我推荐谷歌数据集搜索网站:https://www.google.com/publicdata/directory
BigQuery 等新系统通过基于列的数据库提供了极高的计算能力和快速的结果。通过上面的例子,您应该能够很好地查询和处理大多数嵌套数据用例。有了免费数据和计算能力,您可以实现许多有利可图的用例。作为第一步,我建议简单地处理数据。使用 Google Cloud 免费层,你可以相对容易地做到这一点。
资料来源和进一步阅读
[1]谷歌, OpenStreetMap 公共数据集
[2] OpenStreetMap,【https://www.openstreetmap.de/ (2021)
[3]谷歌,在谷歌云上解决真正的商业挑战 (2021 年)
使用 Python 和 JSON
每个数据爱好者都必须了解的基础知识

在 Unsplash 上由 Vadym Lebedych 拍摄的照片
作为数据分析师、数据工程师或数据科学家,你经常会接触到 JSON 格式的数据。而且对数据有兴趣的经理或来自不同部门的员工也经常有机会处理 API 并为仪表板或类似的东西提供数据。在这里,您经常使用 JSON 格式,因此了解它的最基本的知识对于快速、轻松地将数据放入 Python DataFrame 是很有用的。
JSON 是什么来着?
JSON (JavaScript 对象符号)是一种数据交换格式。用户可以轻松读写这种格式,机器也可以轻松解析生成。
JSON 建立在两种结构之上:
- 名称/值对的集合:在各种语言中,这被实现为一个对象、记录、结构、字典、散列表、键列表或关联数组。
- 值的有序列表:在大多数语言中,这被实现为一个数组、向量、列表或序列【1】。
示例
{
"company": "Google",
"companycontacts": {
"phone": **12345678**,
},
"employees": [
{
"name": "Lukas",
"id": 19,
},
{
"name": "Lisa",
"id": 14,
}
]
}
在下图中,您可以看到一个典型的 JSON 示例。JSON 对象以“{”开头,后跟一个名称(“公司”)和值(“Google”)。通常,您还会在对象中找到对象,如公司联系人。元素“employee”是一个经典的数组示例,其中也可以包含另一个数组。
四个主要使用案例
首先让我们获取一些数据——在这里,我使用了 finnhub API,这是一个非常好的 API,可以为您提供非常智能的金融数据,如股票、货币和密码[2]。

获取 API 数据—按作者排序的图像
第一种情况:对象中的对象:通常你会需要数据帧中来自 API 的数据——所以你需要平铺对象中的对象。将数据放在数据框中便于将数据用于数据科学任务或将其加载到数据库中。

在对象中展平对象—按作者排列的图像
第二种情况:从 JSON 数组中读取:
现在让我们用 JSON 中的数组获取一些数据——在这个例子中,我请求了一些股票数据,包括买入、持有等。信息:

获取 API 数据—按作者排序的图像
从对象的数组中获取数据有点困难,但从数组本身的对象中获取数据也有点困难,就像您在这里看到的:

拼合对象中的数组—按作者排列的图像
这个想法是在一个关系表结构中获得每只股票的基于周期的数据。这里,我们需要一些循环:
第三种情况:显式键和值
上面的例子,但以获取某些键和值为目标可以通过:

获取显式的键和值—按作者排序的图像
例如,我从数组中取出第二个项目(period 月)和相应的 buy=18 值。
第四种情况:将 DataFrame 写成 JSON 格式
如果您需要其他方式,只需通过以下方式即可:
out = df.to_json(orient=’records’)[1:-1].replace(‘},{‘, ‘} {‘)
如果您想将数据写回到像数据仓库或数据湖这样的系统中,这通常是需要的。像 Amazon 的 Redshift 或 Google 的 Big Query 这样的现代数据仓库技术也经常支持 JSON 和嵌套数据结构[3]。
结论
当处理数据时,您无法避免 JSON,尤其是在处理 API 和免费数据源时。对于数据管理系统的数据集成,使用 JSON 格式也是一个好主意。许多现代技术都支持 JSON 格式。作为一名数据分析师或工程师,你可能无法避开 JSON,但即使作为一名经常处理数据的普通雇主,了解一些基础知识以及如何使用 Python 将数据转换成数据帧以供进一步处理也是有意义的。我希望这篇文章能为您提供关于 JSON 的最重要的信息,以及一些实际上如何开始处理这类数据的例子。
资料来源和进一步阅读
[1]JSON.org,介绍 JSON (2021)
[2]芬兰枢纽,https://finnhub.io/(2021)
[3] Google,从云存储中加载 JSON 数据 (2021)
使用 Python 字典:备忘单

访问、编辑和循环浏览字典项目
一些基础知识
Python 中的字典是键值对的集合,这意味着字典中的每个条目都有一个键和一个关联值。
如果我们想写下杂货店里一些商品的价格,通常我们会把它们记在一张纸上,就像这样:
eggs - 4.99
banana - 1.49
cheese- 4.5
eggplant - 2.5
bread - 3.99
在 Python 字典行话中,每件商品的名称是“key”,相关的价格是“value”,它们成对出现。我们可以在 Python 字典数据结构中表示相同的内容,如下所示:
{"eggs": 4.99,
"banana": 1.49,
"cheese": 4.5,
"eggplant": 2.5,
"bread": 3.99}
注意不同之处。在字典里
- 每个键都在引号内,因为它们是字符串
- 相关值没有被引用,因为它们是数字
- 键和值由冒号(:)分隔
- 这些项目用逗号分隔
现在,在第一项中,我们有{"eggs”: 4.99},但实际上,鸡蛋可以有几个值——一个是褐色鸡蛋,一个是白色鸡蛋,一个是有机鸡蛋 python 字典允许在字典中添加多个值,这样{“eggs”: [3.99, 4.99, 5.50]}。
让我们给这本字典起个名字,玩玩它:
grocery_items = {"eggs": [3.99, 4.99, 5.50],
"banana": 1.49,
"cheese": 4.5,
"eggplant": 2.5,
"bread": 3.99}
您可以打开 IDE/text 编辑器,按照下面的代码使用我们刚刚创建的字典。
添加和删除项目
我们已经建立了包含 5 个条目的初始字典,但是我们可以通过添加更多条目来扩展它:
grocery_items["onion"] = 3.50 Out: {"eggs": [3.99, 4.99, 5.50], "banana": 1.49, "cheese": 4.5, "eggplant": 2.5, "bread": 3.99, "onion": 3.50}
您可以用类似的方式添加多个项目。
我们也可以反过来做——从列表中删除一个或多个项目。有几种方法可以做到这一点:
使用popitem()方法移除最后一个项目:
grocery_items.popitem()Out: {"eggs": [3.99, 4.99, 5.50], "banana": 1.49, "cheese": 4.5, "eggplant": 2.5, "bread": 3.99}
或者只使用pop()方法通过项目的键名删除项目:
grocery_items.pop("banana")Out: {"eggs": [3.99, 4.99, 5.50], "cheese": 4.5, "eggplant": 2.5, "bread": 3.99}
最后,你可以用clear()方法清空整个字典(也就是清空它),用del关键字删除整个字典(如果你不得不使用它的话,一定要小心)。
您可能想知道有人在哪里可以找到所有这些方法?只要按键盘上的“tab”键,所有的方法都会弹出来。

在 Python 中访问字典方法
访问项目
通常有必要在字典中获得一个键或值的列表,以供进一步分析(我们将在后面看到原因)。
要访问所有键:
grocery_items.keys()Out: dict_keys(['eggs', 'banana', 'cheese', 'eggplant', 'bread'])
要改为访问所有值:
grocery_items.values()Out: dict_values([[3.99, 4.99, 5.5], 1.49, 4.5, 2.5, 3.99])
我们还可以查询字典来访问特定键的值(例如香蕉的价格)。有几种方法可以做到这一点:
# access values by key
grocery_items["banana"]# or
grocery_items.get("banana")Out: 1.49
但是鸡蛋呢?我们有三种不同的鸡蛋价格。我们可以选择访问所有价格:
grocery_items.get("eggs")Out: [3.99, 4.99, 5.5]
我们也可以选择按索引位置访问列表中的第一个:
grocery_items.get("eggs")[0]Out: 3.99
事实上,我们可以以类似的方式访问更复杂的嵌套字典项/键/值。
更多的方法和功能
除了上述最常用于字典的方法之外,还有一些方法和函数可以在数据分析中派上用场:
要获得字典中的条目数:
len(grocery_items)Out: 5
要更改键值,请执行以下操作:
grocery_items.update({"banana": 0.85})
要将密钥转换为列表:
list(grocery_items)Out: ['eggs', 'banana', 'cheese', 'eggplant', 'bread']
如果字典非常大,我们可以检查一个键是否存在:
"potato" in grocery_itemsOut: False
最后,为了返回字典的字符串表示:
str(grocery_items)Out: "{'apple': 2.5, 'orange': 4.99, 'banana': 0.59}"
在字典中循环
经常需要根据某些条件更改字典元素或提取特定的值或键。因为字典是可迭代的对象,循环经常被用于这样的操作。这里有几个例子:
打印所有密钥的简单循环:
for i in grocery_items:
print(i)Out:
eggs
banana
cheese
eggplant
bread
或者用于字典键的条件过滤的for循环:
for i in grocery_items.keys():
if i.startswith("e"):
print(i)Out:
eggs
eggplant
词典释义
像列表理解一样,Python 也支持字典理解来执行不同种类的操作。我们上面创建的for循环也可以用字典理解语法来表达。
例如,要打印字典的值:
[x for x in grocery_items.values()]Out:
[[3.99, 4.99, 5.5], 1.49, 4.5, 2.5, 3.99]
摘要
概括地说,我们在本文中介绍了:
- Python 字典是具有键值对的可迭代对象
- 可以编辑字典来添加、删除和更新值
- 字典元素可以通过
.keys()、.values()、.items()等方法访问 - 字典是可迭代的对象,所以 Python 允许
for循环或字典理解。
我省略了许多其他的方法和功能,但是我希望这为构建一个使用 Python 字典对象的备忘单提供了一个良好的开端。
如果你有意见,请随意写在下面,或者通过媒体、推特或 LinkedIn 与我联系。
使用 Python 列表:备忘单

Python 列表的方法、功能和用例
写了几篇关于计量经济学、逻辑回归和正规化的文章之后——我又回到了基础!
许多复杂的数据科学算法都是用简单的构件构建的。你提升技能的速度很大程度上取决于你的基础有多强。在接下来的几篇文章中,我将触及一些这样的基础话题。希望学习这些话题能让你的旅程变得愉快有趣。
今天的话题是 Python 列表。
大多数人会先学习工具,然后用几个例子来实践它们。我走的是相反的路线——首先关注问题,在这个过程中学习解决问题的工具、方法和功能。因此,今天的文章分为以下几个小部分:
- 什么是列表以及如何创建列表
- 如何添加/删除元素
- 如何访问列表项,最后
- 对它们执行什么操作
什么是列表?
让我们从一个熟悉的例子开始——购物清单。
苹果、香蕉、牛奶、糖、盐——这是我可能会写在一张纸上或手机记事本上的购物清单。
现在你有了一个购物清单,你能用它做什么呢?事实上有几件事:
- 加上“鸡蛋”因为你忘了
- 购买时去掉“苹果”
- 如果你在水果区,进入列表的下一个项目
不管出于什么原因,如果您想将这个列表存储/导入到 Python 程序中,您需要以特定的方式对其进行格式化。以下是 Python 存储杂货的方式:
grocery_list = ["apple", "banana", "milk", "sugar", "salt"]
因此,列表只不过是以特定格式存储的信息,程序可以理解并处理这些信息。
有一些内置的方法和函数可以完成这些任务,我们将在本文中看到如何使用其中的一些方法和函数:
列表方法: append(), extend(), insert(), remove(), pop(), clear(), index(), count(), sort(), reverse(), copy()
内置函数: sum(), min(), max(), len(), enumerate(), map(), filter(), lambda(), set(), sorted(), zip()
让我们创建一个列表
当你在做一个项目时,你很可能会使用别人已经创建的列表。但是,由于我们是从零开始,所以让我们用任意数字构建一个新的:
# manually creating a list
mylist = [10, 12, 8, 6, 14]
有时候人们用 Python 内置的range()函数生成人工列表。
# generating a list
list(range(0, 5)) #start 0, stop 5>> [0,1,2,3,4]
现在我们有了一个列表,让我们来玩它。
添加到列表中
有几种方法可以将新项目添加到列表中。用append()方法,你可以在列表末尾添加一个条目,99:
mylist = [10, 12, 8, 6, 14]
mylist.append(99)>> [10, 12, 8, 6, 14, 99]
或者使用insert()方法,您可以在您想要的位置添加 99——比如说在位置 0。
mylist = [10, 12, 8, 6, 14]
mylist.insert(0, 99)>> [99, 10, 12, 8, 6, 14]
如果您想将另一个列表[99,100]而不是单个元素添加到现有列表中,该怎么办?
mylist = [10, 12, 8, 6, 14]
mylist.extend([99, 100])>> [10, 12, 8, 6, 14, 99, 100]
总结一下,我们刚刚学会使用三种方法:append(), insert(), extend()以便在现有列表的不同位置添加新元素。
从列表中删除
学会了如何添加新物品,自然接下来的话题就是如何移除物品了。我们将使用两种方法— pop()和remove()。
pop()方法根据位置移除项目。例如,如果我们要移除位置为 0 的项目:
mylist = [10, 12, 8, 6, 14]
mylist.pop(0)>> [12, 8, 6, 14]
类似地,要移除位置 2 处的项目:
mylist = [10, 12, 8, 6, 14]
mylist.pop(2)>> [10, 12, 6, 14]
也可以从对面选择位置。比方说,要删除倒数第二个位置的元素:
mylist = [10, 12, 8, 6, 14]
mylist.pop(-2)>> [10, 12, 8, 6, 14]
另一种移除物品的方法叫做remove()。它不是根据位置而是根据值来移除项目。它是这样工作的:
mylist = [10, 12, 8, 6, 14]
mylist.remove(8)>> [10, 12, 6, 14]
还有一件事,你也可以用一个新的值替换(即改变)列表中现有项的值。假设用 99 代替 10:
mylist = [10, 12, 8, 6, 14]
mylist[0] = 99>> [99, 12, 8, 6, 14]
最后,不管出于什么原因,如果你决定删除列表,也有一个方法可以实现这个目的——del():
del mylist
访问列表元素
既然您已经知道了如何创建和编辑列表,下一步自然是如何处理它们。但是在执行任何操作之前,您需要先访问这些项目,对吗?到目前为止,我们使用的 5 个项目的列表很容易可视化,但是如果有数千甚至数百万个项目呢?如果你不知道你到底在找什么呢?
下面我们将看到一些如何使用索引位置来访问列表项的例子。
# access the first item
mylist[1]# grab the last item
mylist[-1]
如果它是一个嵌套列表(列表中的列表),并且您需要从内部访问一个项目,该怎么办?
# to access number 33 from the list above
mylist = [0,1,[33,34],9,10]
mylist[2][0]>> 33
这里发生的事情很简单,你正在采取一种逐步的方法——首先访问恰好在位置 2 的[33,34],然后从这个列表中的位置 0 访问 33。
切片: 除了访问单个元素之外,您可能还希望访问一系列项目——这个过程称为切片。切片的一般格式如下:
# start index : stop index : steps
mylist[::]
让我们使用这种格式以各种方式分割列表:
mylist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]# slice items from index 0 to index 10 with 2 stepsmylist[0:10:2]>> [0, 2, 4, 6, 8]
类似地:
# slice items from index 1 to end
mylist[1:]# items from the beginning to index 10
mylist[:10]# items from index 5 to 10
mylist[5:10]
方法和功能
到目前为止,我们已经完成了一些基础工作——创建、编辑和访问列表元素。但是这并没有解决任何问题,也没有为用户提供多少信息。
为了生成信息,我们需要对列表应用方法和函数(顺便问一下:你知道方法和函数的区别吗?如果没有,请查看这个堆栈溢出帖子。
在开始时已经提到过,但这里列出了列表对象的所有方法:
列表方法: append(), extend(), insert(), remove(), pop(), clear(), index(), count(), sort(), reverse(), copy()
我们已经应用了上面的一些方法,剩下的你可以自己尝试看看它们是如何工作的。
让我们来看看 Python 的内置函数,它们在您的数据科学和分析之旅的每一步都非常有用。
内置函数: sum(), min(), max(), len(), set(), sorted(), enumerate(), map(), filter(), lambda(), zip()
上面的前 4 个函数是不言自明的,可以通过以下方式应用:
# get the sum of all elements
sum(mylist)# minimum/maximum value
min(mylist)
max(mylist)# total number of elements
len(mylist)
接下来的两种方法——set(), sorted()——也很有用。
set()只返回列表中唯一的值,所以如果有重复的值,它将只返回其中的一个。
mylist = [1,1,2,2,3,3,4,4]
set(mylist)>> [1,2,3]
sorted()另一方面将返回一个排序列表(从低到高或反之亦然等)。).
mylist = [2,1,3]
sorted(mylist)>> [1,2,3]
特殊功能
Python 标准库还附带了一些特殊的函数。我们将检查其中的 4 个— enumerate(), map(), filter(), lambda()。
枚举()
给定一个列表mylist = [‘hello’, ‘world’],enumerate()函数将找出每个项目的索引位置:
mylist = ['hello', 'world']
list(enumerate(mylist))>> [(0, 'hello'), (1, 'world')]
地图()
map() function 接受两个输入——一个函数和一个可迭代对象——然后对对象执行函数,并将输出作为可迭代对象返回。这是它的一般公式:
map(function, iterable_object)
让我们来看看如何一步一步地使用它:
# define a function
def squared(num):
return num**2# input data
mylist = [1,2,3,4]# execute map() function
mymap = map(squared, mylist)# get the output
list(mymap)>> [1, 4, 9, 16]
过滤器()
类似于map()函数,filter()也接受一个函数和一个 iterable 对象。然而,它接受的函数只返回布尔值—真或假。然后,该函数检查 iterable 中的每一项,看它们是真还是假,最后返回一个过滤后的对象。
因此,例如,如果我们想要过滤一个列表的奇数[1,2,3,4,5],那么filter()函数将返回[1,3,5]。这是它如何在引擎盖下一步一步地工作:
# create a boolean function
def is_odd(num):
return num%2 != 0# input data
mylist = [1,2,3,4,5]# filter odd numbers
my_filter = filter(is_odd, mylist)# get the output
list(my_filter)>> [1, 3, 5]
希腊字母的第 11 个
您在编程中经常遇到的最后一个函数是lambda函数。通常,当我们编写一个函数时,我们给它一个名字,并在各种场合重复使用它。一个返回数字平方的普通函数如下所示:
def my_squar_func(num):
return num ** 2
相比之下,lambda 函数没有名称,是在操作中使用的一次性函数。下面是我们如何将上面的my_square_func()转换成λ表达式:
lambda num: num**2
它有哪些用例?
在上面的map()和filter()函数中,我们必须遵循许多步骤才能得到最终的输出,因为我们必须首先创建函数。lambda()在这两种情况下都会派上用场。这里有两个使用案例:
# map() + lambda
mylist = [1,2,3,4]
mymap = map(lambda num: num**2, mylist)
list(mymap)>> [1, 4, 9, 16]# filter() + lambda
mylist = [1,2,3,4,5]
my_filter = filter(lambda n: n%2 != 0, mylist)
list(my_filter)>> [1, 3, 5]
摘要
总而言之,在本文中,我们已经讨论了相当多的内容:
- 定义和创建列表;
- 用
append(), insert(), extent()方法添加新元素; - 使用
pop(), remove()方法移除元素; - 通过索引和切片访问列表项;
- 应用 Python 的内置函数
enumerate(), map(), filter()和lambda函数对列表进行操作。
在本文中,我遗漏了许多其他方法和函数,但是我们在这里使用的是经常使用的方法和函数。我希望这篇文章是有用的。如果你有意见,请随意写在下面。也可以在 Medium 、 Twitter 或 LinkedIn 上与我联系。
使用 SQL 与熊猫(第 1 部分)以及练习题
选择、过滤和排序数据

介绍
Python Pandas 库和 SQL 都是操作数据的流行工具,在功能上有很多重叠。那么,有什么更好的方法来提高您在这两方面的技能,而不是通过为 Pandas 和 SQL 解决相同的编码问题来让它们短兵相接呢?
这是我将撰写的帮助您直接比较 Pandas 和 SQL 的系列文章的第一篇。我的目标是帮助你:
- 了解和比较功能
- 如果你已经知道一个,那就去学习另一个
- 了解各自的优势和劣势
- 拥有处理数据的多种工具
对于这里的例子,我将使用臭名昭著的泰坦尼克号数据集,它可以在 Kaggle 上找到。我鼓励你在阅读这篇文章的时候看看它,这样你就能跟上了。最后,我将根据本文中讨论的特定功能,提出一些练习问题供您自己解决。
选择列
让我们从如何选择列并以特定顺序显示它们的最基本功能开始。在我们的练习中,假设我们很想依次看到数据集的 pclass、fare、age 和 survived 列。
结构化查询语言
在 SQL 中,SELECT语句是您列出想要显示的列以及显示顺序的地方。在 titanic 数据集作为一个表输入到我们的数据库中之后,我们从它的数据集中选择列。在 SQL 中,如果您想要查看一个表的所有列,您可以使用SELECT *,但是我们将命名想要显示的列。
SELECT pclass, fare, age, survived
FROM titanic
熊猫
在 Pandas 中,titanic 数据集将被读入 Pandas 并存储为 dataframe 对象。从这里,我们将切出所需的列。有多种方法可以达到同样的效果。
col_list = ['pclass', 'fare', 'age', 'survived']
titanic[col_list]
您也可以将列列表直接放入括号中,而不创建单独的变量。
titanic[['pclass', 'fare', 'age', 'survived']]
另一种方法是使用loc[]。
titanic.loc[:, ['pclass', 'fare', 'age', 'survived']]
冒号表示我们想要选择所有的行,然后在逗号后面指定我们想要的列和顺序。同样,您可以创建一个列列表作为变量,并将其放在逗号之后。通常我更喜欢创建一个变量,因为在一行中,括号上的括号越少,看起来越清晰。使用列表变量也更容易遵守 PEP-8 指南,因为这样可以避免代码行过长。接下来,我将继续使用loc[]并创建单独的变量。
这里还值得注意的是,Pandas 的loc[]选项提供了基于索引选择一系列行的功能。如果我们希望只查看 Titanic 数据集中从 50 到 100 的索引值,那么我们可以将50:100作为第一项,这样包含的行范围将是唯一返回的行。我们也可以在逗号后面使用普通的冒号来选择所有的列,或者我们可以使用它来选择一系列的列,就像对待行一样。
基于列值的筛选
现在,让我们假设您想要基于特定条件的结果。让我们继续上面的同一个示例,假设我们希望再次看到 pclass、fare、age 和 survived 列,但是现在我们只希望看到成年男性的条目。
结构化查询语言
在 SQL 中,WHERE子句是根据列中的特定值进行过滤的方式。WHERE条款在FROM条款之后。由于我们的问题包括需要在两个条件上进行过滤,我们将在两个条件之间列出 AND。如果希望返回满足任一条件的行,也可以在两个条件之间使用 OR。
SELECT pclass, fare, age, survived
FROM titanic
WHERE sex = 'male' AND age >= 18
熊猫
对于熊猫,我们将创建包含我们想要过滤的两个条件的变量。然后我们将再次使用loc[]选择所需的行和列。
male_mask = titanic.sex == 'male'
age_mask = titanic.age >= 18
col_list = ['pclass', 'fare', 'age', 'survived']titanic.loc[male_mask & age_mask, col_list]
这两个掩码变量是布尔值列表,用于判断数据帧的每一行是否满足该条件。还记得以前当我们使用loc[]时,我们如何使用冒号来指定我们想要的所有行,然后在逗号后指定我们想要的列吗?这里我们使用相同的功能,首先指定我们想要的行,然后在逗号后面指定我们想要显示的列。就像以前一样,您可以直接输入掩码标准而不创建变量,但是创建变量会使事情看起来更整洁。使用掩码变量之间的&,将只返回两个条件都为真的行。如果你想做“或”逻辑,你可以用管道符号|来代替。
Pandas 的一个巧妙的小功能是,您还可以在掩码变量前使用波形符~来翻转布尔值。例如,如果我们使用~male_mask来过滤,它将翻转所有的真为假,反之亦然。在这种情况下,它将与针对女性的过滤相同,但有时该功能会派上用场,能够使用相同的变量,但会颠倒逻辑。
按列值对结果排序
让我们继续我们的问题,假设我们现在希望结果首先按票价排序,然后按年龄排序。我们希望票价值按降序排序,年龄值按升序排序。这意味着任何具有相同票价值的条目将首先按照最年轻的个体对这些条目进行排序。
结构化查询语言
在 SQL 中,我们将使用ORDER BY子句。在查询中,ORDER BY子句将出现在WHERE子句之后,默认的排序方法是升序。为了将最大值排在最上面,我们将使用DESC关键字。
SELECT pclass, fare, age, survived
FROM titanic
WHERE sex = 'male' AND age >= 18
ORDER BY fare DESC, age
熊猫
对于熊猫,我们将使用sort_values()进行分类。我们可以使用列的列表进行排序,排序层次结构是按照列的顺序排列的。有一个上升参数,默认设置为真。我们可以向该参数传递一个布尔值列表,这些值按照它们被列出的顺序与相应的列相匹配。
male_mask = titanic.sex == 'male'
age_mask = titanic.age >= 18
col_list = ['pclass', 'fare', 'survived']df_slice = titanic.loc[male_mask & age_mask, col_list]
df_slice.sort_values(['fare', 'age'], ascending=[False, True])
我想在这里提出的一个重要注意事项是,如果您计划更改 dataframe 切片的任何值,最好的做法是在loc[]之后加上一个 **.copy()**。这样,您可以确保不会意外更改原始数据帧。即使对于那些在 Pandas 中有经验的人来说,当在切片上进行更改时,知道原始数据帧何时被更改或不被更改的规则也是非常令人困惑的。
限制顶级结果
我们将在练习题中添加的最后一项内容是将结果限制在前 10 名。
结构化查询语言
这个功能对于 SQL 来说非常简单;我们只需添加LIMIT子句,并将我们想要限制的条目数量限制在——在本例中是 10 个。
SELECT pclass, fare, age, survived
FROM titanic
WHERE sex = 'male' AND age >= 18
ORDER BY fare DESC, age
LIMIT 10
熊猫
对于熊猫,我们可以通过在末端加上一个head()来获取最高值。您可以在括号中传递想要显示的条目数——在我们的例子中是 10。
male_mask = titanic.sex == 'male'
age_mask = titanic.age >= 18
col_list = ['pclass', 'fare', 'survived']df_slice = titanic.loc[male_mask & age_mask, col_list]
df_slice.sort_values(['fare', 'age'], ascending=[False, True]).head(10)
我想在这里指出,Pandas 也有nlargest()和nsmallest()方法,可以方便地对列进行排序,并限制在一个简单的方法中返回的结果。然而,在我们的问题中,我们希望按降序过滤一列,按升序过滤另一列,所以nlargest()和nsmallest()方法并不真正符合我们的需要。然而,假设我们实际上想按升序过滤票价和年龄。这将是nsmallest()的一个有效用例。
male_mask = titanic.sex == 'male'
age_mask = titanic.age >= 18
col_list = ['pclass', 'fare', 'survived']df_slice = titanic.loc[male_mask & age_mask, col_list]
df_slice.nsmallest(10, ['fare', 'age'])
练习题
- 为没有父母陪同的 16 岁以下儿童显示“年龄”、“性别”和“幸存”列(parch 列中的值为零)。按年龄对结果进行升序排序。
- 显示 2 级或 3 级女性的级别、年龄和票价。按年龄和票价降序排列结果,并限制前 20 个条目。
- 显示第三班幸存儿童(18 岁以下)的所有列。将结果按年龄升序排序,然后按票价降序排序。幸存列包含 1 表示是,0 表示否。
停止—下面的答案

约翰·马特丘克在 Unsplash 上拍摄的照片
我希望你在偷看的时候没有至少在脑子里试一试每个问题!有些问题有多种解决方法,所以预计你的答案会与我的略有不同。
1。为没有父母陪同的 16 岁以下儿童显示“年龄”、“性别”和“幸存”列(parch 列中的值为零)。按年龄对结果进行升序排序。
SQL:
SELECT age, sex, survived
FROM titanic
WHERE age < 16 AND parch == 0
ORDER BY age
熊猫:
children = titanic.age < 16
parents = titanic.parch == 0
col_list = ['age', 'sex', 'survived']titanic.loc[children & parents, col_list].sort_values('age')
2。显示 2 级或 3 级女性的级别、年龄和票价。按年龄和票价降序排列结果,只显示前 20 个条目。
SQL:
SELECT pclass, age, fare
FROM titanic
WHERE sex = 'female' AND class != 1
ORDER BY age DESC, fare DESC
LIMIT 20
熊猫:
females = titanic.sex == 'female'
lower_class = titanic.pclass != 1
col_list = ['pclass', 'age', 'fare']df_slice = titanic.loc[females & lower_class, col_list]
df_slice.nlargest(20, ['age', 'fare'])
3。显示第三班幸存儿童(18 岁以下)的所有列。将结果按年龄升序排序,然后按票价降序排序。幸存列包含 1 表示是,0 表示否
SQL:
SELECT *
FROM titanic
WHERE age < 18 AND pclass = 3 AND survived = 1
ORDER BY age, fare DESC
熊猫:
children = titanic.age < 18
low_class = titanic.pclass == 3
survival = titanic.survived == 1df_slice = titanic.loc[children & low_class & survival, :]
df_slice.sort_values(['age', 'fare'], ascending=[True, False])
结论
如您所见,SQL 和 Pandas 非常不同,但仍然可以完成相同的任务。在写这篇文章的时候,我很喜欢在 Pandas 和 SQL 之间切换,所以我希望你也喜欢。请关注本系列的后续部分!
第二部分可以在这里找到:
[## 使用 SQL 与熊猫(第 2 部分)以及练习题
towardsdatascience.com](/working-with-sql-versus-pandas-part-2-plus-practice-problems-ae1c19aab114)
使用 SQL 与熊猫(第 2 部分)以及练习题
聚合和分组数据

塞尔吉奥·卡普齐马蒂在 Unsplash 上拍摄的照片
这是系列文章的第二部分。你可以在这里找到第一部分:
介绍
在本系列中,我将 Python Pandas 与 SQL 进行比较。这两个都是流行的数据操作工具,在功能上有很多重叠。在本文中,我们将讨论数据的聚合和分组。对于本文中的示例,我们将再次使用可以在 Kaggle 上找到的 Titanic 数据集。我鼓励你下载它,并遵循最大保留。另外,不要忘记在最后尝试一下练习题。好吧,我们开始吧!
聚合函数
假设我们对找出一些数据的统计感兴趣,比如支付的最高和最低票价,乘客的平均年龄,以及有多少人幸存。
结构化查询语言
要在 SQL 中执行聚合函数,您可以将AVG()、COUNT()、MIN()、MAX()和SUM()用于您想要在括号中应用函数的列。需要注意的是,这些聚合函数会忽略空值。这是我们设置的问题的样子。
SELECT MAX(fare), MIN(fare), AVG(age), SUM(survived)
FROM titanic
请记住,幸存的列用 1 表示幸存的列,用 0 表示未幸存的列。这就是为什么SUM()函数能够给出幸存者的总数。
对于这些聚合函数,您可能会发现使用ROUND()可以获得更漂亮的结果。例如,如果我们想将平均年龄列的结果四舍五入到小数点后一位,结果应该是这样的:ROUND(AVG(age), 1)。如果我们想要四舍五入到最接近的整数,那么它将是这样的:ROUND(AVG(age))。
SQL 的一个缺点是前面列出的聚合函数是唯一的标准函数。没有如此简单、直接的方法来计算一列的中值或标准差。我相信 SQL 的某些特定版本可能有更多的聚合函数选项,所以我建议对您正在使用的版本做一些研究。
熊猫
pandas 中有几个集合函数选项。Pandas 默认忽略空值,但是如果你真的想的话,有一些参数可以包含它们。为了在某些列上完成特定的聚合函数,我们可以单独计算每个函数。这就是它的样子。
titanic.fare.agg(['max','min'])
titanic.age.mean()
titanic.survived.sum()
这里让我们列出一个清单。因为我们想要找到 fare 列的最大值和最小值,所以我们可以使用agg()方法将其合并到一行代码中。
正如在 SQL 示例中提到的,您可能想要舍入以获得更好的结果。你可以在一个round()上链这样做。第一个参数是小数位数,默认值为 0。例如,平均年龄四舍五入到小数点后一位就是titanic.age.mean().round(1)。
Pandas 真正了不起的地方是describe()方法,它返回数字列的汇总表。代码titanic.describe()返回下表。

作者创造的形象
这比技术上要求的信息要多,但是仍然非常有用,并且只需要一行非常简单的代码。您还可以通过titanic.describe(include=”O”)获得一些关于“object”数据类型列的信息。下面是返回的内容。

作者创造的形象
Pandas 拥有比 SQL 更多的聚合函数,如 median ( median())、standard deviation ( std())和 variance ( var())。agg()方法还允许更大的灵活性,如果你愿意,你可以基于一个定制的函数进行聚合。
分组依据
比方说,我们希望找到每个班级的男性和女性的一些汇总数据。让我们找出每个班级中男性和女性的数量、平均年龄和存活人数。
结构化查询语言
在 SQL 中,GROUP BY子句用于此目的。这是它看起来的样子。
SELECT sex, pclass, COUNT(*), SUM(survived), AVG(age)
FROM titanic
GROUP BY sex, pclass
在本例中,由于我们返回的列不是聚合值(sex,pclass),如果没有 GROUP BY 子句,代码将无法运行,因为 SQL 不知道如何处理没有 GROUP BY 子句的这些列。在按性别和阶级分组时,将为性别和阶级的每个组合单独计算汇总列(女性在一等,男性在一等,女性在二等,等等)。)
熊猫
在熊猫身上,我们有获得我们想要的结果的方法。和以前一样,我们可以运行代码来获得我们想要的每一个片段,或者我们可以运行包含比我们需要的更多信息的代码。这是把信息分成几部分的样子。
titanic.groupby(['sex', 'pclass']).survived.agg(['count', 'sum'])
titanic.groupby(['sex', 'pclass']).age.mean()
我们也可以生成一个包含我们想要的信息的表,但是它包含的信息比我们要求的要多。这就是它的样子。
new_df = titanic.groupby(['sex', 'pclass'])[['survived', 'age']]
new_df.agg(['count', 'sum', 'mean'])
在这个例子中,我创建了一个新的变量来拆分代码行,这样更容易查看,但是您可以在一行中完成所有这些。首先我们应用 groupby,然后对我们需要的列进行切片,然后对这些列应用聚合函数。下面是生成的表格。

作者创造的形象
在某些情况下,这些额外的信息可能是额外的兴趣,在其他情况下,它可能只会引起更多的混乱。例如,“存活”和“年龄”列的计数不同。这是因为 age 列中有空值,所以 survived 列中的计数是我们关心的真实计数,因为没有空值。但是这会导致不必要的混乱。另一方面,存活列中的平均值告诉我们每个组中存活者的百分比,这是增加分析价值的有趣信息。
练习题
- 查找平均票价和按上船港口和舱位分类的乘客人数。
- 找出按等级和存活率分组的乘客的最小、最大和平均年龄。
- 查找按生存分组的乘客的平均票价、最小年龄和最大年龄。
停止—下面的答案

约翰·马特丘克在 Unsplash拍摄的照片
在偷看答案之前,你有没有尝试过每个问题?请记住,您的答案可能会与我的略有不同,因为通常有多种解决方法。
- 查找平均票价和按上船港口和舱位分组的乘客人数。
SQL:
SELECT embarked, pclass, COUNT(*), AVG(fare)
FROM titanic
GROUP BY embarked, pclass
熊猫:
titanic.groupby(['embarked', 'pclass']).fare.mean()
titanic.groupby(['embarked', 'pclass']).embarked.count()
2。找出按等级和存活率分组的乘客的最小、最大和平均年龄。
SQL:
SELECT pclass, survived, MIN(age), MAX(age), AVG(age)
FROM titanic
GROUP BY pclass, survived
熊猫:
titanic.groupby(['pclass','survived']).age.agg(['min','max','mean'])
3。查找按生存分组的乘客的平均票价、最小年龄和最大年龄。
SQL:
SELECT survived, AVG(fare), MIN(age), MAX(age)
FROM titanic
GROUP BY survived
熊猫:
titanic.groupby('survived').age.agg(['min', 'max'])
titanic.groupby('survived').fare.mean()
结论
我希望您喜欢关于聚合函数和分组的讨论。我想让这些文章足够短,容易消化,这也是我决定就此打住的原因。我期待着与您一起继续这篇文章系列的旅程,所以请务必参加我的下一部分!
在 Python 中使用字符串:备忘单

包含 f 字符串、计数器和正则表达式
也许你不想成为 NLP 专家。但是作为一名数据科学家,你肯定要处理字符串对象;无一例外。它可能包括命名和重命名列、遍历列名或编辑数据集中存在的其他类型的字符串对象等简单操作。
或者也许你正在考虑成为一名 NLP 专家。无论哪种方式,学习一些基本的字符串操作技术都将使您在很大程度上提高技能,并使您能够轻松地处理文本数据。
Python 标准库中有一些有用的工具,其他包也非常强大。在 I 中使用内置工具和外部库将在本文中涉及以下内容:
- 内置字符串方法
- 计数器模块
- 访问元素
- f 字符串文字
- 正则表达式
让我们开始吧。
预热
我先做个热身。
您可能应用了内置的len()函数来计算列表、集合、元组或字典中的项目。你可以用同样的方法来检查一个字符串的总长度。
len('hello')>> 5
还可以通过使用list()构造函数将一个字符串转换成一个列表。
list('hello!')>> ['h', 'e', 'l', 'l', 'o', '!']
连接两个字符串的工作方式就像数学函数中普通的数字加法一样。
first_string = 'hello'
second_string = 'world'
new_string = first_string + second_string>> 'hello world'
同样,你可以通过多次相乘得到一个字符串的副本!
string = 'hello'
(string + ' ') *5>> 'hello hello hello hello hello '
字符串方法
和其他东西一样,字符串是 Python 中的对象。自然,它带有内置的方法,你可以通过点击来发现。字符串后的 tab 键:

Python 中的字符串方法
那么你到底能用字符串方法做什么呢?一些基本的功能包括将字符串转换成大写或小写,大写等。下面是一些例子:
# make uppercase
'hello'.upper()
>> 'HELLO'# make lowercase
'HELLO'.lower()
>> 'hello'# capitalize
'hello'.capitalize()
>> 'Hello'# title
'hello world'.title()
>> 'Hello World'
除了基本方法,还可以调用几个非常有用的方法。例如,您可以使用自己选择的分隔符(如字母)来拆分字符串(比如一个句子)。
# split a word
'hello'.split(sep = 'e')>> ['h', 'llo']
最有用的方法是用空格(“”)分隔符将一个句子分成一系列单词。
sent = 'hello world'
sent.split(sep = ' ')>> ['hello', 'world']
还有一种方法可以将字符串中的元素逆序排列。
# reverse a string
s = 'hello'
s[::-1]>> 'olleh'
如果你发现反转一个字符串对你的工作没什么用,去掉字符串中不需要的东西当然有用。你可以从字符串中删除任何你想删除的东西。
# strip part of a string
'hi there'.strip('hi ')>> 'there'
替换单词或字符也可以派上用场。
# replace a character
'hello'.replace('l', 'L')
>> 'heLLo'# replace a word
'hello world'.replace('world', 'WORLD')
>> 'hello WORLD'
通过使用join()方法,你可以将一列字符或单词连接成一个更长的字符串对象。
# join a list of letters
s = ['h', 'e', 'l', 'l', 'o']
''.join(s)>> 'hello'# join a list of words
w = ['hello', 'world']'-'.join(w)>> 'hello-world'
在结束字符串方法之前,我只想强调,如果你使用 Python pandas库,你会发现内置的字符串方法同样有用。如果你只是在一个熊猫对象(比如字符串)上使用.str.,你可以访问所有这些方法。

在 Pandas 对象中使用内置字符串方法
包括…在内
下一个重要的话题是计数——从计算字符到计算单词等等。
用于计数的内置方法是.count(),它应用于一个对象,您可以指定想要计数的对象作为参数。请记住它是区分大小写的。
# counting characters
sent = 'His name is Abdul. Abdul is a good boy. Boy o boy'
sent.count('a')>> 2# counting the occurance of an word
sent.count('name')>> 1
Python 还附带了一个带有collections库的Counter模块。它可以计算字符串中每个元素的频率,并返回一个 dictionary 对象。
# count ALL characters in a string
from collections import Counter
Counter('aaaaabbbbbbxxxxxx')>> Counter({'a': 5, 'b': 6, 'x': 6})
通过增加一行代码,Counter还可以拆分单词并显示词频。
# counting ALL words in a sentence
sent = 'His name is Abdul. Abdul is a good boy, boy o boy'
word_lis= sent.split(' ')
Counter(word_list)>> Counter({'His': 1,
'name': 1,
'is': 2,
'Abdul.': 1,
'Abdul': 1,
'a': 1,
'good': 1,
'boy,': 1,
'boy': 2,
'o': 1})
作为一个最佳实践,您应该删除字符串中的任何标点符号,否则,它可能会造成一些麻烦。
关联的.most_common()方法返回出现频率最高的项目元组。
Counter(word_list).most_common(3)>> [('is', 2), ('boy', 2), ('His', 1)]
接近
通过索引访问单个项目或通过项目访问索引非常有用。
要访问字符/单词的索引:
'hello how are you'.find('how')>> 6
或者通过索引访问字符/单词:
s = 'hi there'
s[3]>> 't'
分割字符串的方式与处理列表对象的方式类似。
s = 'hi there'
s[0:4]>> 'hi t'
f 弦
在 Python 中,可以用多种方式格式化字符串。在 Python 3.6 之前,大多数人会使用% formatting 或.format(),但是 f-string 让它变得更加简单——易于使用和阅读。这里有一个例子:
name = 'Anna'
age = 23
print(f"my name is {name} and I am {age} years old")>> my name is Anna and I am 23 years old
对于数值变量,您可以在格式-> value:width.precision 中指定值的精度
# f string numbers
div = 11/3
print(f'the result of the division is {div:2.3f}') >> the result of the division is 3.667
正则表达式
正则表达式re是 Python 中一个非常强大的库,你可以用它做很多事情,尤其是在数据集中搜索模式。下面是其功能的一个小例子。
如果您希望搜索匹配的文本模式:
import re
text = 'hello my phone number is (000)-111-2222'
pattern = 'phone're.search(pattern, text)>> <re.Match object; span=(9, 14), match='phone'>
在输出中,span 告诉匹配的索引位置(在本例中从索引 6 开始,到索引 8 结束)。
您还可以搜索您认为最有可能出现的特定格式的数字模式。
text = 'my zipcode is 22202'
pattern = r'\d\d\d\d\d'
re.search(pattern, text)>> <re.Match object; span=(14, 19), match='22202'>
最后,还有所谓的“通配符”,用。(点)元字符。
# input text
text = 'the dog and the hog sat in the fog'# regex search
re.findall(r'.og', text)>> ['dog', 'hog', 'fog']
摘要
总结一下我在本文中介绍的所有内容:
- 基本功能的内置字符串方法,如改变大小写、拆分、连接、替换等。
- 使用内置
.count()方法或使用Counter模块计数字符串元素 - 访问字符串模式和索引值
- 使用 f 字符串文字进行字符串格式化
- 应用正则表达式
re模块进行模式搜索。
我留下了许多其他方法和功能,但我希望这是构建一个使用字符串对象的备忘单的良好开端。
如果你有意见,请随意写在下面,或者通过媒体、推特或 LinkedIn 与我联系。
使用 SQL 处理时间序列
使用 SQL 操作时间序列数据

来源:图片来自 Pixabay
Python 或 R 等工具最常用于进行深度时间序列分析。
但是,了解如何使用 SQL 处理时间序列数据是非常重要的,尤其是在处理非常大的数据集或不断更新的数据时。
下面是一些有用的命令,可以在 SQL 中调用它们来更好地处理数据表本身中的时间序列数据。
背景
在本例中,我们将使用在不同时间和地点收集的天气数据。
PostgreSQL 数据库表中的数据类型如下:
weather=# SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'weatherdata';
column_name | data_type
-------------+-----------------------------
date | timestamp without time zone
mbhpa | integer
temp | numeric
humidity | integer
place | character varying
country | character varying
realfeel | integer
timezone | integer
(8 rows)
正如您所看到的, date 被定义为不带时区的时间戳数据类型(我们也将在本文中看到这一点)。
感兴趣的变量是 temp(温度)—我们将研究使用 SQL 更直观地分析该变量的方法。
计算移动平均值
下面是数据表中一些列的片段:
date | mbhpa | temp | humidity
---------------------+-------+-------+----------
2020-10-12 18:33:24 | 1010 | 13.30 | 74
2020-10-15 02:12:54 | 1017 | 7.70 | 75
2020-10-14 23:53:42 | 1016 | 8.80 | 75
2020-10-15 11:03:25 | 1016 | 9.70 | 75
2020-10-15 13:05:23 | 1017 | 12.30 | 74
2020-10-15 18:47:25 | 1015 | 12.10 | 74
2020-10-16 23:23:23 | 1011 | 9.10 | 75
2020-10-20 10:25:15 | 967 | 13.80 | 83
2020-10-27 16:30:30 | 980 | 12.00 | 75
2020-10-29 15:12:07 | 988 | 11.70 | 75
2020-10-28 18:42:52 | 990 | 8.80 | 77
假设我们希望计算不同时间段内温度的移动平均值。
要做到这一点,我们首先需要确保数据是按日期排序的,并决定在平均窗口中应该包括多少个周期。
首先,使用 7 周期移动平均值,所有温度值按日期排序。
>>> select date, avg(temp) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) FROM weatherdata where place='Place Name'; date | avg
---------------------+---------------------
2020-11-12 16:36:40 | 8.8285714285714286
2020-11-14 15:45:08 | 9.8142857142857143
2020-11-15 08:53:26 | 10.3857142857142857
2020-11-17 10:50:32 | 11.2285714285714286
2020-11-18 14:18:58 | 11.8000000000000000
2020-11-25 14:54:11 | 11.6285714285714286
2020-11-25 19:00:21 | 10.9142857142857143
2020-11-25 19:05:31 | 10.2000000000000000
2020-11-25 23:41:34 | 9.2857142857142857
2020-11-26 15:03:10 | 9.4857142857142857
2020-11-26 17:18:33 | 8.3428571428571429
2020-11-26 21:30:39 | 7.9142857142857143
2020-11-26 22:29:17 | 7.6142857142857143
现在,让我们添加一个 30 和 60 周期移动平均线。我们将这些平均值与 7 期移动平均值一起存储在一个表中。
>>> select date, temp, avg(temp) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), avg(temp) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), avg(temp) OVER (ORDER BY date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW), avg(temp) OVER (ORDER BY date ROWS BETWEEN 59 PRECEDING AND CURRENT ROW) FROM weatherdata where place='Place Name';

来源:作者创作的作品
关于如何在 SQL 中计算移动平均值的更多信息可以在以下资源中找到,作者是sqltrainingonline.com。
使用时区
您会注意到时间戳包含日期和时间。虽然在表中只存储一个位置时这很好,但是当处理跨多个时区的位置时,事情可能会变得非常棘手。
注意,在表中创建了一个名为时区的整数变量。
假设我们正在分析输入时间之前一系列时区的不同地方的天气模式,在这种情况下,所有数据点都是在 GMT 时间输入的。
date | timezone
---------------------+----------
2020-05-09 15:29:00 | 11
2020-05-09 17:05:00 | 11
2020-05-09 17:24:00 | 11
2020-05-10 13:02:00 | 11
2020-05-13 19:13:00 | 11
2020-05-10 13:04:00 | 11
2020-05-10 15:47:00 | 11
2020-05-13 19:10:00 | 11
2020-05-14 17:17:00 | 11
2020-05-09 15:20:00 | 5
2020-05-09 17:04:00 | 5
2020-05-09 17:25:00 | 5
2020-05-09 18:12:00 | 5
2020-05-10 13:02:00 | 5
2020-05-10 15:50:00 | 5
2020-05-10 20:32:00 | 5
2020-05-11 17:31:00 | 5
2020-05-13 19:11:00 | 5
2020-05-17 21:41:00 | 11
2020-05-15 14:08:00 | 11
2020-05-14 16:55:00 | 5
2020-05-15 14:10:00 | 5
(22 rows)
新时间可以按如下方式计算:
weather=# select date + interval '1h' * timezone from weatherdata;
?column?
---------------------
2020-05-10 02:29:00
2020-05-10 04:05:00
2020-05-10 04:24:00
2020-05-11 00:02:00
2020-05-14 06:13:00
2020-05-11 00:04:00
2020-05-11 02:47:00
2020-05-14 06:10:00
2020-05-15 04:17:00
2020-05-09 20:20:00
2020-05-09 22:04:00
2020-05-09 22:25:00
2020-05-09 23:12:00
2020-05-10 18:02:00
2020-05-10 20:50:00
2020-05-11 01:32:00
2020-05-11 22:31:00
2020-05-14 00:11:00
2020-05-18 08:41:00
2020-05-16 01:08:00
2020-05-14 21:55:00
2020-05-15 19:10:00
(22 rows)
我们现在可以将新的时间存储为一个更新的变量,我们将其命名为 newdate。
>>> select date + interval '1h' * (timezone+1) as newdate, temp, mbhpa from weatherdata; newdate | temp | mbhpa
--------------------+------+-------
2020-05-10 03:29:00 | 4.2 | 1010
2020-05-10 05:05:00 | 4.1 | 1009
2020-05-10 05:24:00 | 3.8 | 1009
该子句允许我们生成更新的时间(这将反映记录温度、气压等变量时每个特定地点的实际时间)。
内部连接和 Having 子句
你会注意到上表中包括了许多地方的温度值。
假设还在单独的表中计算每个地方的风速。
在这种情况下,我们希望计算风速高于 20 的每个列出地点的平均温度。
这可以通过使用具有如下子句的内部连接和来实现:
>>> select t1.place, avg(t1.temp), avg(t2.gust) from weatherdata as t1 inner join wind as t2 on t1.place=t2.place group by t1.place having avg(t2.gust)>'20'; place | avg | avg
-----------------+----------------------+----------------------
Place 1 | 17.3 | 22.4
Place 2 | 14.3 | 26.8
Place 3 | 7.1 | 27.1
结论
在本文中,您已经了解了一些使用 SQL 处理时间序列数据的介绍性示例。
特别是,您看到了如何:
- 计算移动平均值
- 使用不同的时区
- 计算不同数据子集的平均值
非常感谢阅读,任何问题或反馈都非常感谢!您还可以在这里找到原始文章,以及有用的 SQL 实践的更多例子。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。
在 BigQuery 中使用时间和日期
你必须知道的

谁不熟悉这种情况?正确的时间戳格式是什么来着?只要谷歌一下,下次它就会再次结束。这里是一些我经常接触到的用例,我想把它们写下来,希望不用再去谷歌它们。
当前日期时间
让我们从一个非常简单但是有用的开始。获取当前时间—您可能需要它来进行比较,例如在分析或技术数据转换中:
SELECT CURRENT_DATETIME() as now;
这个函数支持可选的 timezone 参数。关于如何设置时区[1]的说明,参见时区定义。

当前日期示例—按作者分类的图像
提取
从提供的 datetime_expression 中返回对应于指定部分的值。例如,这里我只提取了年份。我经常将这种逻辑用于分析中的约束或数据转换,在这种情况下,我转换日期格式并将它们以不同的格式存储在新表中。
SELECT EXTRACT(YEAR FROM DATETIME(2021, 07, 02, 15, 45, 00)) as year;

结果—作者提供的图像
日期时间差异
计算两个日期之间的差异。如果第一个日期时间在第二个日期时间之前,则输出为负[1]。
Select DATETIME_DIFF(DATETIME”2021–03–03", DATETIME”2021–04–04", Day)as difference;
这导致-32。
格式化日期时间
根据指定的 format_string 格式化 DATETIME 对象。与其他示例类似,人们喜欢在数据转换过程中使用这种格式化选项。
SELECT
FORMAT_DATETIME(“%c”, DATETIME “2008–12–25 15:30:00”)
AS formatted;
这里,以%c 为例,输出完整的日期和时间表示[2]。

格式化日期时间-按作者排序的图像
解析日期时间
使用 PARSE_DATETIME 你可以做与上面提到的例子相反的事情。它将一个日期时间对象的字符串表示转换成一个日期时间对象。
SELECT PARSE_DATETIME(‘%Y-%m-%d %H:%M:%S’, ‘2021–03–03 13:55:55’) AS datetime;

解析的日期时间-按作者排序的图像
例如,当其他数据源将日期显示为字符串并且您希望在 BigQuery 中将它显示为标准化的日期时间时,这总是有用的。
最后审判日
从包含日期的 DATETIME 表达式中返回最后一天。这通常用于返回一个月的最后一天。您可以选择指定应该返回最后一天的日期部分。
SELECT LAST_DAY(DATETIME ‘2008–11–25’, MONTH) AS last_day
如果不使用该参数,默认值为月。特别是在评估领域,例如有趣且经常使用的财务月报表。

结果—作者提供的图像
摘要
由于我经常在 BigQuery 中搜索日期格式,因为我忘记了正确的 SQL 表达式是什么,所以我为您提供了关于这个主题的备忘单。在我看来,这些是最常见的用例——如果我忘记了什么,请随时告诉我。
资料来源和进一步阅读
[1]https://cloud . Google . com/big query/docs/reference/standard-SQL/datetime _ functions?hl=de
在 SQL Server 中使用用户定义的函数
在本教程中,我们将讨论 SQL Server 中的用户定义函数。更具体地说,我们将讨论标量函数和表值函数。

扬·安东宁·科拉尔在 Unsplash 上拍摄的照片
写代码的时候,一定要以遵循 DRY 原则为目标(不要重复自己)。避免代码重复的一种方法是将代码块放在函数中,并在需要时调用它们。
SQL 中函数的概念类似于 Python 等其他编程语言。主要区别在于它们的实现方式。根据返回的数据,SQL 中有两种主要类型的用户定义函数:
- 标量函数:这些类型的函数返回单个值,例如 float、int、varchar、DateTime 等。
- 表值函数:这些函数返回表。
目录
- 先决条件。
- 创建函数。
- 在语句中使用函数。
- 更新/删除功能。
- 在函数中使用变量和条件语句。
- 结论。
先决条件
- 对 SQL 的基本理解。
- 带有数据库的 SQL Server。
- SQL Server Management Studio连接到您的数据库。
创建函数
标量函数
下面是一个简单函数的定义。它接受两个数字并返回它们的和。因为这个函数返回一个数字,所以它是一个标量函数。
CREATE FUNCTION scalar_func
(
@a AS INT, -- parameter a
@b AS INT -- parameter b
)
RETURNS INT -- return type
AS
BEGIN
RETURN @a + @b -- return statement
END;
- 我们使用
Create function命令来定义函数。它后面是函数的名称。在上面的例子中,函数的名字是scalar_func。 - 我们需要以下面的格式声明函数的参数。
@VariableName AS Data Type
在上面的例子中,我们定义了两个整数参数a和b。
- 结果的返回类型必须在参数的定义下面提到。在上面的例子中,我们返回的总和是一个整数。
- 在返回语句之后,我们创建一个包含函数逻辑的
BEGIN ... END块。虽然在这种情况下,我们有一个返回语句,但我们不需要一个BEGIN ... END块。
表值函数
在创建表值函数之前,我们将创建一个简单的表。
-- Creating new table
CREATE TABLE TEST(
num1 INT,
num2 INT
);
-- Inserting values into new table
INSERT INTO TEST
VALUES
(1,2),
(2,3),
(4,5);
该表包含两列。我们将创建一个函数,返回一个带有额外列的新表。这个额外的列将包含列num1和列num2中的数字之和。
CREATE FUNCTION table_valued_func()
RETURNS TABLE
AS
RETURN
-- statement to calculate sum
SELECT num1 , num2, num1 + num2 AS 'SUM'
FROM TEST;
- 上面的函数不接受任何参数。
- SQL 语句只是计算总和,并将其存储在一个名为
SUM的新列中。
在语句中使用函数
标量函数
-- invoking previously created scalar function
SELECT dbo.scalar_func(1,2);
当在语句中使用函数时,我们需要在函数前面加上与之相关的数据库模式。Microsoft SQL Server 中的默认模式是dbo。如果没有提到数据库模式,SQL 将给出一个错误,
表值函数
由于该函数返回一个表,我们需要选择我们感兴趣的列。
-- invoking previously created table valued function
SELECT * FROM dbo.table_valued_func();
像标量函数一样,我们需要提到数据库模式。
更新/删除功能
更新/删除标量函数和表值函数的语法是相同的。
更新
我们将更新我们的表值函数,在现有的 sum 上加 10,并将列名改为New_Sum。
ALTER FUNCTION table_valued_func()
RETURNS TABLE
AS
RETURN
-- updating statement to add 10 to sum
SELECT num1 , num2, num1 + num2 + 10 AS 'NEW_SUM'
FROM TEST;
Alter关键字用于更新功能。
滴
-- dropping previously created scalar function
DROP FUNCTION dbo.scalar_func;-- dropping previously created tabular function
DROP FUNCTION dbo.table_valued_func;
注意:不要在函数名后面加括号。
在函数中使用变量和条件语句
变量
下面是声明和初始化变量的语法。
-- declaring integer variable
DECLARE @result AS INT;-- initializing created varaible
SET @result = @a + @b;
DECLARE关键字用于创建变量,而SET关键字用于初始化变量。
下面是一个使用变量的标量函数的例子。
CREATE FUNCTION scalar_func
(
@a AS INT,
@b AS INT
)
RETURNS INT
AS
BEGIN
-- using variables inside function
DECLARE @result AS INT
SET @result = @a + @b
RETURN @a + @b
END;
IF…ELSE 语句
IF...ELSE语句的语法类似于 Python 或 C++中的IF...ELSE语句。
DECLARE @num AS INT;
SET @num = 4;
IF @num % 2 = 0
BEGIN
SELECT 'Number is Even'
END ELSE
BEGIN
SELECT 'Number is Odd'
END
上面这段代码检查变量num中的值是偶数还是奇数。基于该值,执行IF或ELSE程序块。
下面列出了使用IF...ELSE块的功能。
CREATE FUNCTION is_even(@num AS INT)
RETURNS BIT
AS
BEGIN
DECLARE @result AS BIT
-- set variable to 1 if number is even
IF @num % 2 = 0
SET @result = 1
-- set variable to 0 if number is odd
ELSE
SET @result = 0
RETURN @result
END;
案例陈述
当您处理多个 if 语句时,最好使用 case 语句。它们使你的代码更容易阅读。下面是 case 语句的一般语法。
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
.
.
.
ELSE result
END
像开关情况一样,检查所有情况,如果满足多个情况,将执行相应的代码块。
下面我们有一个使用 case 语句的函数。
CREATE FUNCTION is_greater
(
@a AS INT,
@b AS INT
)
RETURNS VARCHAR(30)
AS
BEGIN
RETURN(
'A is' +
CASE
-- Case 1
WHEN @a > @b THEN 'Greater than'
-- Case 2
WHEN @a < @b THEN 'Smaller than'
ELSE 'Equal to'
END
+ 'B')
END;
它比较两个整数,并根据比较结果返回一个字符串。
结论
正如我上面提到的,在编写 SQL 语句时,尽量遵循 DRY 原则。当您看到同一段代码在多个语句中使用时,请考虑将其放在一个函数中。函数使你的语句看起来更简洁。
编码快乐!
在 LinkedIn 上与我联系
https://www.linkedin.com/in/rahulbanerjee2699/
原载于section . io2021 年 2 月 1 日
使用光流监测健身程序—第 1 部分
一份关于图像处理项目的详细报告,该项目始于学术界,并在一次行业会议上发表

光流叠加示例练习(作者提供 GIF)
这是我和我的团队 Shaked Cohen,Inbal Rimon 和 Hagai Michel 构建的图像处理项目系列文章的第一部分。本部分将提供项目概述以及工作流程描述。未来的部分将深入系统的不同组件:
第 1 部分:项目概述和工作流程
第三和第四部分:TBD
这个项目是作为一门名为“数字图像处理”(DIP)的学术课程的一部分完成的,由教授塔米·瑞克林·拉维夫领导,我之前在这里写过。这个项目,以及同一课程的其他项目,也在系统学网站(以色列 MATLAB 经销商,希伯来文)上有所介绍。
一些背景
我们在 2020 年初开始从事这个项目,就在 COVID 成为现实的几个月前。为什么我们在将近 2 年后现在写它?因为我们终于找到时间了。哦,可能是因为该项目被接受作为以色列机器视觉大会( IMVC2021 )的现场演示。
该项目的目标是实现一个基于经典图像处理和计算机视觉工具的实时视频处理系统。
等等,没有深度学习?那我为什么要在乎?!
这是一个很好的问题。如果你对 SoTA 结果或一些新颖的方法感兴趣,那么你可能不应该这样做,但是如果你有兴趣了解图像处理的核心工具以及如何用它们解决现实世界的问题,你应该继续读下去。
对于我们的项目,我们选择关注对人类运动的实时监控。我们选择这个主题是因为它提出了一个巨大的挑战:
- 运动过程中的人体运动相对较快,需要允许实时处理速度(10~30 FPS)的高效算法。
- 人体是一个高度可变形的形状,尤其是在运动时。这意味着我们识别和跟踪受训者的方法不能依赖于每个框架内的固定形状或位置。
- 练习和正确练习之间的区别是微妙的。这需要足够灵敏的运动和姿态跟踪方法来注意到这种差异。
项目概述
我们的系统——蔻驰健身中心——为 4 种不同的锻炼提供反馈:仰卧起坐、深蹲、单人举重和双人举重。一旦开始练习,将受训者从背景中分割出来,并进行进一步的裁剪和遮罩细化,以仅保留感兴趣的对象。然后屏蔽的视频被用于监控正在进行的锻炼。最后,在输入视频之上生成反馈,以通知受训者他们的进度并添加任何姿势校正(如果必要)。

系统流程图(作者提供的图表)
每个练习都提出了独特的挑战,需要实施四个独立的反馈机制:
单次举重:检测帧中的径向运动,估计手臂角度。
双人举重:使用面部检测算法定位受训者,并利用达芬奇的身体比例来设置最佳运动范围。
深蹲:L 通过计算给定面具的重心来定位受训者的下半身。
- 仰卧起坐:使用智能裁剪和几何变换最大化 FPS。
下图展示了不同的练习和一些给用户反馈的例子。是的,我们知道 UI 是可怕的,我们做了最少的工作,以便将大部分时间投入到系统的大脑上,而不是美化它。

(作者供图)
每个练习使用的逻辑和算法将在本系列的未来部分详细解释,但现在我们想分享我们的工作过程以及我们如何结束使用光流。
通往最佳细分的漫长道路
由于所有四个练习都依赖于良好的分割,这项任务在早期就成为我们项目的主要焦点。我们对现有方法进行了文献回顾,并测试了不同的实现,直到找到最佳解决方案。我们训练了基于特征的对象检测器,使用休变换来检测图像中的线条和圆形,以及许多其他方法,这些方法提供的结果很差,并且计算量很大(远非实时)。
我们尝试过使用基于颜色的过滤,但是我们很快排除了它,因为它需要使用绿色屏幕或者强迫受训者穿非常特殊的颜色。面对现实吧,基本上就是作弊,没有挑战性。
那时,我们开始提出自己的算法,这些算法都基于两个主要概念:
- 简单性:解决方案必须足够简单,以便我们能够实时提供反馈。
- 基于运动的:解决方案应该利用受训者是帧中唯一的运动对象这一事实。
在实践中,这些算法对两个连续的帧执行一些操作,并试图通过减去处理过的帧来检测运动。我们尝试使用空间导数,索贝尔过滤器,Canny 边缘检测和更多。尽管如此,我们仍然非常依赖于这样的想法,即最好的分割依赖于捕捉时间轴上的运动。
这是其中一种算法的简化版本:
这里的主要思想是:
- 将两个连续帧转换成二进制边缘图像:

两个连续画面的锐利边缘(作者提供照片)
2.减去图像以捕捉视频中的运动物体(中值滤波用于降低噪声):

我不是在黑暗中举重,这是时间导数(视频由作者提供)
重要的是要注意,边缘检测在许多方面是图像空间梯度的近似,减去两帧是图像时间梯度的近似。我们将在第 2 部分详细阐述这些概念。
这些帧差算法比我们以前尝试的解决方案好得多,但它们仍然不够好,主要是由于灵敏度问题。我们的算法要么检测到太多的运动,要么检测到太少的运动,如果不包括灯的闪烁或其他背景噪声,很难提供对受训者的一致检测。
就在我们准备放弃的时候,我们偶然发现了一种算法,它正好给出了我们需要的东西——一种以实时速度检测运动并提供稳健结果的算法。
光流
经过冗长的文献回顾和其他方法的大量反复试验,我们发现在帧中分割受训者的最佳解决方案是使用光流。简而言之,这是一种估计视频中运动的方法,这对我们来说很好,因为我们视频中唯一的运动来自于受训者。
阅读第 2 部分,了解我们解决方案的更多细节。
使用光流监测健身程序—第 2 部分
我们如何用光流追踪举重
这是一系列文章的第二部分。如果错过了第 1 部分,请在此处阅读。
要查看该系列所有文章的列表,请点击这里。

我们在看什么?(作者供图)
前情提要“光流监测锻炼练习”,我们介绍了这个项目,它是如何工作的,以及引导我们最终实现系统的算法过程。
在这一部分中,我们将深入我们系统的理论和技术方面,主要关注光流以及它如何用于为哑铃举重提供反馈。
正如您在上一部分中回忆的那样,我们的主要挑战是将学员从背景中分割出来。这最终通过使用称为光流的运动估计算法来实现。
光流
问题定式化
光流表示场景相对于观察点的运动。用外行的话来说,这是一种测量两个视频帧之间运动的方法。对于给定的视频帧【𝐼(x,y,𝑡】,光流旨在估计序列中连续帧之间的运动,这意味着我们需要一个连接【𝐼(𝑥,𝑦,𝑡】和【𝐼(𝑥+𝛿𝑥,𝑦+𝛿𝑦,𝑡+𝛿𝑡】的方程,其中【𝛿𝑥,𝛿𝑦】是在【𝛿𝑡的一小段时间内帧之间运动的微小变化。下图可以说明这一点:

(作者供图)
为了模拟光流问题,我们必须首先假设恒定的亮度,这意味着每个像素的值在位置和时间周期的微小变化中保持不变。这一假设允许我们写出如下内容:

我们现在可以将一阶泰勒展开式应用于等式的右侧:

移除共同项并除以𝛿𝑡将得出:

假设位置和时间的变化足够小,该等式可以替换为:

其中【𝑉=[𝑉𝑥,𝑉𝑦】是我们感兴趣的速度矢量,𝛁𝑰是帧的梯度 I 。上述方程被称为梯度约束方程,其中图像的梯度可以容易地计算,速度【𝑉𝑥,𝑉𝑦】是感兴趣的未知变量。
用 Farneback 方法求解光流
既然我们理解了眼前的问题,我们终于可以试着解决它了。有许多方法来解决光流,但现在我们将重点放在 Farneback 的方法,因为它是我们在这个项目中使用的方法。
该方法提供了基于矩阵多项式展开和图像金字塔的密集光流解决方案。其思想是将图像分割成固定大小的邻域(例如 3×3 或 5×5),用二阶矩阵多项式表示,如下所示:

其中 x 是代表图像邻域的环境, A 是对称矩阵, b 是矢量, c 是标量。系数 A、B 和 c 是根据对邻域中的图像值的加权最小二乘拟合来估计的。
使用这种表示,我们现在可以将速度场建模为应用于两个帧的多项式函数的平移变换。给定两个不同帧中相同邻域的两个多项式函数,光流方程现在变成:

其中 d 为两帧之间的位移距离。代入多项式表达式,我们得到:

求解 d 给出了每一个像素在两帧之间移动的距离,并给出了帧之间的参考时间单位,这有效地表示了速度场,从而求解光流。更多细节,MATLAB和 OpenCV都有 Farneback 方法的优秀实现。
举重练习
好了,我们理解了光流问题,以及如何有效地近似求解它——这对我们举重有什么帮助?!
使用光流在多个方面帮助我们:
- 将用户从背景中分割出来
- 检测学员面向哪边(有助于获得更好的反馈)
- 计算重复次数,跟踪手臂的运动范围
为了更好地理解功率光流,让我们逐一分析这些用途。
使用光流幅度的分割
光流帮助我们解决了学员细分的挑战。理解它如何工作的最好方法是可视化通过求解光流获得的速度场。这些可以用笛卡尔坐标(沿 X 和 Y 轴的速度)来表示,但是现在让我们使用极坐标来表示每个点的运动幅度和运动角度,以表示运动方向。让我们从可视化运动的归一化幅度开始:

光学流动可视化
这已经看起来非常接近于一个好的分割掩模,因为举重手臂和躯干具有最大的幅度。我们只需要使用智能阈值和过滤机制,以确保我们在手臂和重量周围获得鲁棒的分割掩模。
经过反复试验,我们发现最好的解决方案是设定大小的阈值,然后应用斑点分析,只保留最大的斑点。这个过程如下图所示:

原始幅度(左)、幅度阈值(中)、最大斑点(右)。(作者供图)
在最终的系统中,我们使用原始图像上的颤动图来可视化掩模和其中的光流矢量。下面是一个向下运动的例子:

蒙版图像+颤动图(图片由作者提供)
使用光流角的反馈
一旦我们有了分割蒙版,下一步就是利用速度角度来为受训者提供反馈。让我们观察掩蔽区域内的平均速度角:

两次重复举重过程中的光流方向(原始和平滑的)(作者提供的图表)
该角度以相对于标准轴系统的度数来测量,这意味着当臂平行于 X 轴时角度为 0,当臂平行于 Y 轴时角度为 90。当观察平滑的图表(在右边)时,你可以很容易地看到每次重复练习的正弦曲线模式——在这种情况下,有两对最大值和最小值点。
通过测量分割区域随时间的平均角度,我们可以实时生成此图,并通过检测极值点来计算重复次数。我们还可以通过测量手臂运动的范围,使用角度数据向用户提供更定性的反馈。例如,大约从-50 度到+100 度的重复是好的,但是从-10 度到+45 度的重复就不好了。
使用笛卡尔光流的分割改进
关于光流的最后一个观点,是使用它的笛卡尔表示来确定哪只手正在被用来举起重物。这有助于改进遮罩,使其仅包含受训者的前臂和手,并过滤掉大部分躯干。改进的面罩提供更精确的角度测量,这允许更好和更稳健的锻炼监测和反馈。
如前所述,光流速度场的笛卡尔表示只是一对 (Vx,Vy)——每轴速度。让我们来看看这种表现的两个例子:

你能通过观察速度来判断哪只手臂在举重吗?(作者提供的图表)
为了从这些图表中推断出是哪只手臂在做举升动作,让我们把注意力集中在前 10 帧。 Vy 中的负尖峰与锻炼开始时的向上运动相关(记住,在图像中 Y 轴是反转的)。
假设受训者站在一个侧面,举臂更靠近摄像机,我们可以在练习的第一次向上运动中,通过查看【Vx】来检测举臂。
你能在上面的每张图中找出是哪只手臂在举东西吗?答案在这个帖子的最后。
一旦我们知道哪一侧正在进行提升,我们就可以裁剪分割蒙版,使其只包含足够接近权重的像素。例如,如果受训者用他的右手举着东西,我们可以把所有离最左边的像素(也就是重量)太远的像素裁剪掉。

原始面膜(左)、精制面膜(右)(作者供图)
把所有的放在一起
到目前为止,我们已经使用光流生成了一个分割掩模,并在举重练习中跟踪手臂的角度。这涵盖了我们项目的基本组成部分。最终系统如下图所示:

整个 enchilada(作者提供的框图)
如您所见,还有更多的内容要介绍,但这超出了本文的范围。本系列的未来部分将通过解释如何监控其他练习来触及该系统的不同方面。
至于侧面检测游戏——左边的图形代表用左手举起,右边的图形代表右手。右手对着摄像头举(就像上面的图)是指当手向上举(负 Vy)时,在 X 轴上的运动是正向的,直到角度为 0,然后就切换方向了。这显示在右图的 Vx 小波中,它以一个正尖峰开始,然后改变符号(反之亦然)。
参考
基于多项式展开的两帧运动估计。第 13 届斯堪的纳维亚图像分析会议论文集,363–370。瑞典哈尔姆斯塔德:SCIA,2003 年
(作者的所有可视化)
Tableau 中的世界幸福仪表板
Tableau 实用指南

(图片由作者提供)
Tableau 是创建数据可视化的强大而高效的工具。它允许创建高度信息化的情节,而无需编写任何代码。此外,多个可视化可以很容易地结合到一个仪表板。
讲故事是数据科学家工作的基本部分,而数据可视化是讲故事的必要条件。因此,数据科学家的技能组合中应该至少有一个数据可视化工具或框架。Tableau 的多功能性使其成为数据科学生态系统中非常受欢迎的数据可视化工具。
学习如何有效地使用 Tableau 需要练习。我们可以下载 Windows 和 Mac 都有的 Tableau Desktop】公版免费练习。
Tableau 还提供 Tableau Public,这是一个免费的平台,可以公开分享你的仪表盘。这是展示你的技能和创造力的好方法。
在本文中,我们将使用 public edition 创建一个简单的仪表板。我们将使用来自 Kaggle 的数据集,其中包含 2019 年各国的世界幸福指数和其他指标。
第一步是添加到数据源的连接。这里强调“连接”这个词很重要,因为 Tableau 连接到数据文件并使用它来创建可视化。它不编辑原始文件。

(图片由作者提供)
我们选择适当的格式,然后导航到保存数据集的文件。
Tableau 连接到文件后,它会提供数据源的概述,并自动创建一个工作表。

(图片由作者提供)
除了上面截图中看到的度量之外,数据集还包含 4 个度量。额外的衡量标准是健康的预期寿命、做出生活选择的自由、慷慨和对腐败的看法。
在 Tableau 中,我们使用工作表来创建可视化效果。工作表完成后,我们可以将它们合并成一个仪表板。我们将在本文中创建的仪表板包含三种可视化效果:
- 基于各国幸福指数着色的世界地图
- 展示人均 GPD、幸福指数和健康预期寿命之间关系的散点图
- 展示做出生活选择的自由、社会支持和幸福得分之间关系的线图。
可视化 1
Tableau 的一个很酷的地方是,它可以根据国家名称自动生成经度和纬度信息。创建信息丰富的地图非常容易。
我们首先打开一个新的工作表。生成的纬度和经度列被拖动到绘图区域。然后,“国家名称”列被拖到“标记”窗格中。
Tableau 在右侧窗格中突出显示了可能的可视化类型。我们可以使用世界地图的颜色或大小。彩色的根据给定的数量用不同的颜色显示国家。衡量颜色的标准是快乐分数,所以我们将分数列拖到标记窗格中的颜色图标上。
下面的屏幕记录演示了所有这些步骤。

(作者 gif)
第一次可视化完成了。最好给它起一个更具描述性的名字。我就改名为“各国幸福得分— 2019”。
可视化 2
对于第二个可视化,我们将创建一个散点图。和往常一样,我们首先使用现有工作表旁边左下角的 new sheet 图标创建一个新的空白工作表。
程序是一样的。我们根据所需的可视化类型来拖放列。散点图将包含三条信息。幸福指数和人均 GDP 分别代表 x 轴和 y 轴上的维度。因此,在我们将它们拖动到页面中之后,我们通过使用下拉菜单将它们设置为一个尺寸。
现在,在散点图中,每个国家都用相同的标记表示。我们将使用健康预期寿命列作为第三维度来调整标记的颜色。
下面的屏幕记录演示了所有这些步骤。

(作者 gif)
我将把这张表重新命名为“幸福得分与 GDP 和健康预期寿命”。
可视化 3
我们还为最后一个可视化创建了一个新的工作表。它将包含一个两行的线图,这两行是做出生活选择的自由与幸福得分和社会支持与幸福得分。
我们将 score 列拖到 columns 部分,并将其设置为一个维度。另外两列放在 rows 部分。
下面的屏幕记录演示了所有这些步骤。

(作者 gif)
我会把最上面的支线剧情的 y 轴重命名为“自由”,因为这个名字似乎太长了。这可以通过点击轴名并选择编辑轴来轻松完成。
我将把这张表重新命名为“自由和社会支持与分数”。
我们已经完成了可视化。下一步是将它们组合成一个仪表板。Tableau 也让这个过程变得简单而高效。我们首先通过单击左下方的“new dashboard”图标创建一个新的控制面板。

空仪表板(作者图片)
我们创建的所有工作表都可以在左侧窗格中看到。我们现在可以通过拖动页面来自定义仪表板。
下面是我如何定制仪表板。请随意使用界面,创建您自己的定制仪表板。在 Tableau 界面中,你可以探索许多很酷的特性。

(作者 gif)
Tableau 的一个很酷的特性是,我们可以选择一个特定的可视化作为过滤器,使仪表板具有交互性。

(作者 gif)
我们已经完成了我们的世界幸福分数仪表板。这是我的 Tableau 公共配置文件中仪表板的最终版本。

(图片由作者提供)
结论
我们创建了一个简单的仪表板。Tableau 是一个高度通用和实用的工具,因此它可以用来创建更高级的仪表板。然而,最好先理解基础知识。
成为高级 Tableau 用户需要大量的练习。如果您正在或计划在数据科学领域工作,这是一个非常重要的工具。
感谢您的阅读。如果您有任何反馈,请告诉我。
杰克真的会死在泰坦尼克号上吗?
机器学习如何回答这个问题
逻辑回归和朴素贝叶斯的演练。

图像来源
那是 1912 年,强大的泰坦尼克号开始了它的处女航。杰克,一个“ 20 岁”“三等”“男”乘客,赢了一手扑克和他去自由之地的票。在 4 月 14 日的最后一个小时,泰坦尼克号撞上了冰山,它的命运已经注定。杰克能在这场灾难中幸存下来吗?
(是的,我知道他在电影里死了,但如果他是真人,他会活下来吗?)
这是一个二元分类问题,因为我们试图预测两种结果之一:生存或不生存。有许多分类算法,根据数据的不同,有些算法比其他算法效果更好。
我准备训练两个不同的机器学习模型来回答这个问题,对这些不同的算法做一个深入的比较。
我正在使用来自 Kaggle 的泰坦尼克号数据集。
https://www.kaggle.com/c/titanic/data
这个数据集有很多关于乘客的信息:姓名、年龄、性别、舱位等级(一等、二等或三等)、票价、车上兄弟姐妹的数量等等。
我们应该选择这些特征中的哪一个来预测杰克的命运?
特性选择的艺术和科学应该有自己的文章。现在,让我们应用一些推理。性别和年龄可能很重要(记得在电影中他们就像“妇女和儿童优先”)。乘客等级可能也很重要。让我们挑选这三个特征。
我将使用的第一个算法是逻辑回归。
算法 1:逻辑回归
逻辑回归预测一种结果相对于另一种结果的可能性。在这种情况下,我们使用模型来预测杰克活下来的概率。因为模型计算概率,所以模型的输出总是在 0 和 1 之间。
在 2D,模型是最适合数据集的逻辑曲线。在下图中,每个蓝点都是乘客,x 轴是年龄,y 轴是他们是否幸存。1 表示存活,0 表示未存活。模型是红色曲线。

作者图片
有几种方法可以找到最佳拟合的函数。梯度下降法是其中之一,牛顿法是另一种。要深入了解实现,请阅读本文。
给定 x 轴上的一个新输入点(比如年龄= 39),我们看看曲线落在 y 轴上的位置,看看存活的概率是多少。
注意:该图并不代表真实的数据集,它仅用于说明目的。
在 3D+中,模型是最适合数据集的超平面。

我们来做一些预测吧!
在训练模型之前,我们需要先进行一些数据处理。
- 将数据分为训练集和测试集。训练集用于训练模型,测试集用于测试模型的准确性。
- 通过一键编码将分类变量转换成二进制格式。
分类变量是具有两个或更多类别的变量,这些类别没有内在的顺序。本例中的性别是一个分类变量,有两个类别(男性和女性)。
我们有两个分类变量(性别和阶级)。我们不能用这些变量原始形式的值来进行训练。换句话说,我们不能将格式[male, 3rd class]传入训练模型。我们必须使用一键编码来转换它们。
通过一键编码,变量的每个类别(如性别变量的男性和女性)成为输入向量中自己的二进制列。如果乘客属于该类别,则该列的值为 1,否则为 0。总的来说,我们将得到 6 列。1 代表年龄,2 代表性别,3 代表阶级。
输入向量的格式是:
[age, female?, male?, 1st class?, 2nd class?, 3rd class?]
年龄只是一个数字。第二列female?女性为 1,男性为 0。第三列male?为 1 表示男性,为 0 表示女性,以此类推。
杰克的数据点[20, male, 3rd class]变成了[20, 0, 1, 0, 0, 1]。
为了训练我的模型,我使用了一个名为 SciKit learn 的库。SciKit learn 是一个很棒的机器学习工具,提供了很多学习算法。
import pandas as pd
import numpy as np
import math
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split# read the data from csv file
data = pd.read_csv('train.csv').T.to_dict()X_categorical = []
X_age = []
y = []for idx in data:
info = data[idx]
sex = info['Sex']
p_class = info['Pclass']
survived = info['Survived']
age = info['Age']
# don't use data if age is absent
if not math.isnan(age):
X_categorical.append([sex, p_class])
X_age.append([age])
y.append(survived)# one hot encoding to transform the categorical data:
enc = OneHotEncoder()
enc.fit(X_categorical)
features = enc.transform(X_categorical).toarray()# Combine the age vector with the transformed matrix
X = np.hstack((X_age, features))# split data into train and test set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=40)# logistic regression to fit the model
clf = LogisticRegression().fit(X_train, y_train)# Print out the prediction
print(clf.predict([[20,0,1,0,0,1]]))
print(clf.predict_proba([[20,0,1,0,0,1]]))
要查看更漂亮的原始代码,请在 Github 上下载我的 Jupyter 笔记本。
杰克会活下来吗?很可能不是。这个模型预测存活的概率是 0.1078528,因此死亡的概率是 0.8921472。
现在让我们看看另一种学习算法,朴素贝叶斯。
算法 2:朴素贝叶斯
朴素贝叶斯是一个概率公式。贝叶斯定理求给定 b 的概率。

在我们的例子中,我们需要给定男性、20 岁和三等兵的生存概率。
数学表示为:

P(survive)是幸存的乘客数除以乘客总数。
根据链式法则,

查看这篇文章,我在这里解释了如何更详细地计算提名者和分母。
SciKit Learn 也有朴素贝叶斯。训练数据X_train和标签y_train与上面的逻辑回归示例相同。拟合模型后,我们用它来预测杰克的命运。还是那句话,Jack 一热编码后的数据点是[20,0,1,0,0,1]。
from sklearn.naive_bayes import MultinomialNBnb = MultinomialNB()
nb.fit(X_train, y_train)print(nb.predict([[20,0,1,0,0,1]]))
print(nb.predict_proba([[20,0,1,0,0,1]]))
杰克会活下来吗?可能没有。该模型预测存活概率为 0.10072895,因此死亡概率为 0.89927105。
我的结论是,现实地说,杰克确实已经死了。
那么我们的模型有多可靠呢?
评估模型性能的指标有很多。我们来看其中的两个:准确率和 F1 成绩。
准确性就是正确预测的百分比。

F1 得分是“精确度和召回率之间的调和平均值”。
说什么?
我们来分解一下。
调和平均值两个数(精度和召回)之间的关系是:

精度是模型预测的所有存活数中正确预测的存活数。

Recall 是数据集中所有实际存活数据中正确预测存活数据的数量。

F1 值越高,模型越稳健。
注意:这些指标确实有警告,当数据平衡时(即在训练样本中存活和死亡的数量大致相等),它们工作得很好。在 Kaggle 的数据中,有 59%的人死亡,所以分布相对均匀。
我们可以使用 SciKit learn 和测试数据集来获得准确性和 F1 分数。
from sklearn.metrics import f1_score
from sklearn.metrics import accuracy_scorey_pred_logistic_reg = clf.predict(X_test)
y_pred_naive_bayes = nb.predict(X_test)print(f'logistic regression accuracy: {accuracy_score(y_test, y_pred_logistic_reg)}')
print(f'logistic regression f1 score: {f1_score(y_test, y_pred_logistic_reg)}')
print(f'naive bayes accuracy: {accuracy_score(y_test, y_pred_naive_bayes)}')
print(f'naive bayes f1 score: {f1_score(y_test, y_pred_naive_bayes)}')
这些指标是:

逻辑回归得分高于朴素贝叶斯。
我们能通过不同的特征使模型更精确吗?如果我们包括票价或机上兄弟姐妹的数量呢?这个数据有没有比 Logistic 回归更好的算法?答案很可能是肯定的。我将在以后的文章中跟进这些开放性的问题。
现在,你会在泰坦尼克号上幸存吗?请继续关注我的网络应用程序,在那里您可以输入您的信息,并获得您的生存概率!
您可以从我的 Github repo 中获取这些示例的代码。
感谢您的阅读。你想让我谈什么话题吗?在下面回复或者在 Twitter 或者 LinkedIn 上给我发消息。
你会相信一种算法来选择你的下一个度假目的地吗?
我们有时会想:如果我们让一个人工智能来策划这份简讯,它会和 TDS 的 100%人类团队一样吗?它会依赖于观点、掌声和社交分享吗,或者它会以某种方式检测一篇文章不太有效的品质——作者的声音、原创性或清晰度? Carolina Bento 在她的关于决策树算法的精彩解释中提出了类似的问题。以选择度假目的地的过程为例,她展示了这样一个系统将如何工作,以及它将面临的限制。

照片由 Ricardo Gomez Angel 在 Unsplash 拍摄
许多专家认为,理解一个模型为什么会产生某种结果比满足于它的输出更重要。在某些方面,一个人为的决定是一样的;幸运的是,通过我们的每周精选,解释我们的选择非常容易。罗伯特·兰格的每月深度学习研究论文综述是 TDS 上的常年最爱,读者不断涌向它,因为它不仅列出并总结了该领域的重要发展,还添加了围绕它们的背景和分析。在文章的另一端, Elena Etter 深入探讨了一个很少讨论但至关重要的话题:融入数据可视化的各层主观性,以及它们如何影响媒体的所谓中立性和透明性。
正如你现在可能知道的,我们对执行良好的教程和解释者情有独钟。当作者成功地将一个复杂的话题引入一个吸引人的帖子,激励其他人学习并采取行动时,这总是一种享受。本周,我们特别欣赏了 CJ Sullivan 的动手演示:它专注于注入在 Neo4j 中创建的图形嵌入,并用 Streamlit 仪表板可视化它们。 Pierre Blanchart 转向模型可解释性,并展示了我们如何在 XGBoost 这样的树集合模型中使用反事实解释方法。从理论到实践,博尔哈·维拉斯科(和合著者)向我们介绍了双机器学习的新兴方法,并解释了它在因果推理环境中的应用。对于任何对计算智能越来越好奇的人来说,布兰登·摩根刚刚启动了一个令人兴奋的新项目:一个关于进化计算的完整课程。(如果你已经看过布兰登的介绍,单位一和二已经有了!)
我们对坚实、实用的指南的欣赏,只能与我们在了解一些不经常出现在我们视野中的问题和对话时的喜悦相匹配。TDS 播客正是这种讨论的场所,Jeremie Harris最近与 Jeffrey Ding 的关于中国蓬勃发展的人工智能生态系统的一集也不例外。丹尼尔·安杰洛夫提出了一个发人深省的问题给在工业领域工作的人工智能从业者:“你怎么知道你开发的系统足够可靠,可以在现实世界中部署?”他继续探索软件开发的测试实践,并检验它们在机器学习中是否同样有用。最后,我们在过去的一周主持了一场关于 TDS 的热烈辩论,帖子权衡了数学技能对数据科学家的重要性。我们留给你 Sarem Seitz 的慷慨激昂的案例,他们称之为“ML 中最不受重视的技能”,以及为什么学习一个职业的理论基础与一个人发布好的、干净的代码的能力一样重要。
感谢您接受我们的阅读推荐——我们希望您和我们一样喜欢它们。并且,一如既往地感谢你们让我们的工作成为可能。
直到下一个变量,
TDS 编辑器
我们策划主题的最新内容:
入门
- 提高你作为数据科学家影响力的 10 个策略作者丹尼斯·艾勒斯
- 分位数是理解概率分布的关键
- 数据科学如何掌握熊猫作者 Chanin Nantasenamat
实践教程
- 语义搜索:由詹姆斯·布里格斯测量从 Jaccard 到 BERT 的意义
- 结构方程建模由 Joos Korstanje
- 用这三个工具加速你的命令行导航由 Khuyen Tran
深潜
- 把你的钱投在你的 ML 上:建立对商业关键人工智能的信任
- 线性回归的置信区间从何而来——以魏毅的最小二乘公式为例
- 企业 ML——为什么将你的模型投入生产比构建它需要更长的时间作者 Ketan Doshi
思想与理论
- 通过代理正常化激活消除 CNN 中的批次依赖性Antoine Labatie
- 节点件:由迈克尔·高尔金标记知识图
- 机器学习中的多任务学习由 Devin Soni
争论我们的大脑:我们能使用数据科学来加强直觉吗?
使用数据科学技术在日常生活中做出直觉决策

paweczerwi ski 在 Unsplash 上的照片
什么是直觉?
直觉的力量
韦氏词典将直觉定义为“一种天生的能力或力量,无需任何证明或证据就能知道某事 : 一种引导一个人以某种方式行动而不完全理解原因的感觉。”
有人称之为第六感。其他人将它与“直觉”互换使用一个人日常生活中直觉的例子可能是这样的:
登上飞机,强烈感觉认识一个人,他的行李在同一个箱子里。
尽管没有不良行为的证据,但对某人的真实意图感到怀疑。
考虑给朋友打电话,然后接到他们的电话。
我们生活中的许多直觉时刻是无法解释的。然而,这是我们大多数人一生中至少经历过一次的事情。我自己在生活中也经历过这样的情况,直觉让我有了发现,否则我是不可能有所发现的。而每次出现这种情况,我都会忍不住想知道自己直觉的来源。更重要的是,我想知道是否有办法加强我的直觉,这样我就可以在需要的时候依靠它。
如何激活直觉
当你在网上搜索“如何激活直觉”时,会弹出无数的博客和文章。从商业杂志到关系博客,关于如何激活第六感的文献数不胜数。通读许多博客,可以发现一些关键的共性:
- 沉思
- 在大自然中度过时光
- 多感受,少思考
这些都相当模糊,其中一些是不可测量的。在什么时候你会感觉更多,思考更少?
但是随着我继续研究,我发现了其他一些似乎更容易实现和衡量的常见实践:
与上面的第一组方法相比,这些方法看起来非常不同。虽然它们仍然是旨在激活你的直觉的方法,但从过去学习和记日记等概念是数据收集的形式,这是数据科学领域的第一步,也是最关键的一步。
真的是直觉吗?直觉和数据科学之间的细微差别。
当一个拳击手在看到拳之前挡住了一个毫不起眼的左勾拳,这是直觉的例子吗?或者可能是肌肉记忆,这是他们大脑的潜意识模式识别的结果,这些模式识别来自多年来与特定体型和身材的对手的战斗。
同样,当一个盲人能够在繁忙的街道上行走,同时避开汽车和其他潜在的危险因素时,他们是凭直觉行事,还是已经掌握了使用其他感官收集尽可能多的环境数据的困难任务?
直觉的终点和数据科学的起点之间有一点灰色地带。这就引出了另一个问题:什么是数据科学?
什么是数据科学?

数据科学的力量
关于什么是数据科学,有很多定义和解释,但维基百科说得最好:
“数据科学是一个跨学科领域,它使用科学方法、流程、算法和系统从结构化和非结构化数据中提取知识和见解,并将数据中的知识和可行见解应用于广泛的应用领域。”
换句话说,数据科学是一种利用科学手段分析相关数据,得出关于特定事物或事件的有意义见解的方法。
数据科学的应用
数据科学——特别是机器学习(ML)——似乎是最近的热门话题。大多数企业开始在其行业的各个方面应用一种形式的 ML,无论他们是在金融部门、娱乐部门、各种医疗领域还是在其他任何地方经营。
机器学习有很多方面。根据您试图解决的问题(或者您试图收集的洞察力的类型),您可以应用各种算法。
机器学习算法的应用可以很快变得非常复杂,这取决于你试图解决的问题的类型。一般来说,算法分为四种通用类型,这取决于底层数据集是受监督的(分类和回归)还是不受监督的(聚类或降维)。
回想之前的例子,拳击手在拳击场上迅速躲过一拳,人们可能会说他们的直觉很高。然而,一个相反的论点(也许是更可能的解释)是,拳击手正在练习一种复杂形式的模式识别(分类算法的一个子集)。在拳台上花费无数时间训练和陪练的拳手,对步法、刺拳技术和整体反应的模式和节奏有着更深刻的理解。
在盲人安全地在街上导航的情况下,这也可以是模式识别的一种形式,这一次是在聚类算法的子集中。因为聚类涉及没有先前知识可用于识别新目标的情况(换句话说,盲人可能无法识别行走过程中可能出现的所有潜在危险),所以这一类别中的算法(例如 K-means 聚类)使用模式来分配新对象(例如,汽车、自行车)到一个组中用于预测目的(例如致命的、潜在有害的、无害的)。
通过这些例子,数据科学和直觉之间的相似之处似乎显而易见。此外,这让我想知道我们的大脑产生这些直觉思维的一些原因是否可以用数据科学来解释。如果数据科学可以用来合理化至少一部分我们为什么以看似直觉的方式行事,那么有没有一种方法可以有意地在我们的大脑中应用数据科学来加强我们的直觉?
我将以一篇关于聪明主义的文章中我最喜欢的一段来结束这一部分,这篇文章叫做“直觉如何帮助我们做出更好的决定”
人脑由两部分组成,一部分是我们可以控制的意识,另一部分是我们几乎无法控制的潜意识。人类的大脑处理大量的信息,其中大部分是在潜意识中完成的。因此,源于潜意识的直觉思维可能非常强大,让我们获得意识意识无法掌握的信息。”
我们可以用数据科学来加强我们的直觉吗?一个简短的案例研究:鲨鱼池上的天使投资人
你看过《鲨鱼坦克》这部剧吗?这个节目可能是即时应用数据科学技术(有意或无意)做出直觉决策的最佳描述:交易或不交易。虽然我确信该节目是为了满足娱乐电视的标准而编辑的,但它仍然有助于说明一个人如何使用数据科学来帮助指导他们的直觉的基础。
《鲨鱼池》的前提是围绕一群投资者,该节目称他们为“鲨鱼”,根据企业家的简短推介做出即时投资决定。鲨鱼之间发生的戏谑,以及它们与企业家之间的交流,都很有趣。
“你卖了多少台?”
“你的估值和利润率是多少?”
虽然这些问题说明了任何潜在投资者为了解初创公司的财务状况而进行的标准尽职调查,但最好的鲨鱼提出的问题不太涉及初创公司目前的状况,而是更多地涉及初创公司有可能成为什么样的公司。潜力是非常依赖直觉的东西,但也是可以用数据科学来衡量的东西。通过训练大脑像数据科学家一样思考,鲨鱼可以加强他们的直觉,并最终做出投资决定。
“你的竞争对手是谁?”
“什么样的人会购买这种产品?”
“在这次创业之前,你已经创建了多少家初创公司?”
数据科学技术,如竞争对手分析、推荐系统和预测分析(例如。预测)帮助回答诸如此类的问题。当鲨鱼最终被留下来对一家尚未盈利的公司做出直觉判断时,最好的鲨鱼自然会提出问题,展示他们对数据科学的应用,以指导他们的直觉决策。
《鲨鱼池》是利用数据科学加强直觉的一个很好的例子。在生活中,我们有时会遇到需要直觉反应的机会。通过实践在我们的思维过程中应用数据科学的方法,我们可能能够加强我们的直觉,以做出更好的生活决策。
将数据科学技术应用于日常生活

照片由 Christelle Hayek 在 Unsplash 拍摄
大多数人在一生中都会做出一些重大的人生决定。例子包括没有后备计划的辞职,跟随爱人环游世界,或者拿你一生的积蓄去投资一个有前途的商业想法。虽然这些人生决定通常是经过深思熟虑后做出的,但它们也可能是在一个人经历了支持或反对该决定的强烈直觉时做出的。
当谈到评估你与他人的关系时,使用数据科学技术可以节省你在那些可能不关心你最佳利益的人身上投入的时间。
让我们来看看数据科学可以帮助加强你的直觉的三种方式。
注意危险信号(分类系统中的逻辑回归)
在拿你一生的积蓄冒险投资一个新企业之前,想想所有可能让这个初创企业成为潜在独角兽或失败的变量。在心里给这些变量赋予一个权重,以及它们如何影响结果(在这种情况下,是一个商业想法的成功或失败)。
类似于银行机构使用机器学习来检测欺诈性交易的方式,您可以使用逻辑来直观地检测敌友和操纵者。一开始就注意关系中的“危险信号”可以让你在未来避免不必要的头疼。
逻辑回归是根据关键变量的存在或不存在确定二元结果(0 或 1)的完美例子。递归特征消除是一种分配变量重要性顺序的方法,这将对您的结果产生影响。
执行自然语言处理(抓取、词云和情感分析)
在没有后备计划的情况下辞职之前,研究一下其他这样做的人,看看他们的生活如何变得更好(或更坏),可能会有所帮助。研究可以是任何事情,从采访人们到研究他们的行为。例如,如果你和一个朋友喝咖啡,他最近辞职了,没有后备计划,你可以根据他们使用的词语类型、他们使用这些词语的频率以及他们不得不说的话背后的情绪来了解他们的生活质量。如果他们的大部分对话都涉及到诸如“压力大”、“不确定”和“孤独”之类的词,你就能理解他们对自己决定的总体负面情绪。
就像你可以抓取 LinkedIn 或 Twitter 上的关键词进行分析一样。通过 wordcloud 和情感分析,你可以使用自然语言处理来衡量对某人的整体情感。人们用什么词来定义这个人?社区对他们的性格有什么看法?换句话说,想办法在各种圈子里认可这个人的名声。
信任模式(分类和聚类)
模式识别是本文前面讨论过的内容,并且仍然是直觉的一个非常重要的驱动因素。例如,如果你倾向于做出不会带来积极结果的非理性决定,比如开始几个新项目,但从未完成其中任何一个,也许你最近将毕生积蓄投资于一项业务的直觉不是一个好主意。
同样,如果一个朋友有 X 模式,尽管他们承诺做 Y,你可以肯定他们的真实意图——或者至少倾向——将总是默认为 X。
理解这样的模式有助于确认或重新评估你最初的直觉。
将情感与逻辑分开,增强你的直觉
有一句名言是这样说的:“不要在生气的时候做决定,不要在高兴的时候做承诺。”
这是一个非常重要的引用,因为它揭示了可能会妨碍使用数据科学来加强直觉的东西:情感。
数据科学的应用需要逻辑的、不带偏见的思维。然而,如果你在特定的情况下情绪化,逻辑思维会变得非常困难。
快乐是一种滋生乐观主义的情绪,它会蒙蔽你的头脑,影响你客观处理数据的能力。当你心情愉快时,你的大脑可能会自我选择与那种情绪相关的积极记忆和经历。反过来,在这种情绪中向你提出的任何要求和提议都可能获得积极的直觉反应。
当巧克力商把手工巧克力样品放在鲨鱼池上时,他们希望鲨鱼品尝他们的产品。虽然主要目的可能是让鲨鱼有机会评估巧克力的质量,但另一个动机可能是提升鲨鱼的情绪,以引导它们走向乐观。如果鲨鱼心情愉快(吃了巧克力后谁不会呢!),他们可能愿意忽略巧克力是一个极度饱和市场的现实!悲观主义以类似的方式起作用。愤怒或悲伤时做出的直觉决定很可能会导致未来的后悔。
快乐和愤怒并不是唯一会严重影响我们逻辑思维能力的情绪。有多少次,直到关系结束后,我们才意识到浪漫关系中的危险信号?这是因为爱是一个强大的过滤器。这种强烈的情绪很容易掩盖显而易见的事情。只有在你离开这段关系后,你才能客观地认识到这段关系中所有的危险信号,并“清醒过来”
其他强烈的情绪包括恐惧、钦佩、厌倦和同情。
结论
虽然数据科学和直觉不是一回事,但有意将数据科学应用到你的思维过程中可能有助于增强你的直觉。
感谢阅读!
【www.ayaspencer.com】上 上找我 。我们连线吧!
包装 numpy 的数组
集装箱方法。
记得使用右边的“关注”按钮来关注我→:你会收到新文章的通知,并帮助我达到 100 个关注者的目标:)
Numpy 的数组是功能强大的对象,通常被用作更复杂对象的基础数据结构,如 pandas 或 xarray 。也就是说,您当然也可以在自己的类中使用 numpy 的强大数组——为此,您基本上有两种方法:
- 子类方法:创建从 numpy.ndarray 继承的类
- 容器方法:创建属性为数组的类
在本文中,我们将看到如何使用容器方法包装 numpy 的数组来正确地创建自己的自定义类。

照片由 Guillaume Bolduc 在 Unsplash 上拍摄
让我们以一个示例项目为例:我们想要创建一个简单的项目来处理物理单位和维度,创建长度类似于[1, 2, 3] meter 或重量类似于[55 65 8] kilogram的数组,然后使用这些数组来计算平均身高或【身体质量指数】(https://en.wikipedia.org/wiki/Body_mass_index)。我们希望依靠 numpy 来完成繁重的数字计算(如加、减、幂),但我们也希望能够处理 numpy 数组之类的实例,如np.sort(weights)或np.min(heights)。
为此,我们将创建一个使用容器方法包装 numpy 数组的新类。数值将存储为普通的 numpy 数组,物理维度存储为字符串:
物理阵列的第一种实现
这将简单地打印:[55.6 45.7 80.3] kilogram。同样,这个字符串后面的数字列表是存储在self.value中的实际 numpy 数组。
现在这是完全无用的:我们不能让这个对象与任何其他东西交互,所以我们添加了基本的操作,比如与其他Physical实例的加法或乘法:
现在,物理阵列可以与其他物理阵列相加或相乘。
注意,在增加或减少物理量之前,我们首先检查它们是否有相同的单位:你不能用重量来增加长度(或用胡萝卜增加土豆,或用驴子增加马)。
这太棒了,我们现在可以计算一组体重指数(身体质量指数),给定一组以米为单位的身高和一组以千克为单位的体重。身体质量指数简单地通过将重量除以高度的平方给出,即:
BMI =weight(kg)/height(m)^2
万岁!我们用一个高度数组和一个高度数组计算体重指数数组,用后面的 numpy 数组进行实际的数值计算。但是 numpy 的阵列提供了更多的东西,这就是它真正有趣的地方。
实现 numpy 功能支持
Numpy 提供了许多有用的函数用于数组。仅举几个例子:
np.sin、np.cos、np.tan等np.exp、np.log、np.log10等np.add、np.multiply、np.divide等np.min、np.max、np.argmin、np.argmax等np.floor、np.ceil、np.trunc等np.concatenate、np.vstack等
诸如此类。你可以在 numpy 的网站上找到他们所有的东西:https://numpy.org/doc/stable/reference/routines.html。
让我们试着在课堂上使用其中一个:
试图在我们的物理实例bmi上调用np.mean会引发一个AttributeError,因为 numpy 依赖于整数的加法和除法,而我们的类不能正确地实现这种操作。所以我们必须在某个地方告诉 numpy,我们希望np.mean(bmi)如何表现。
这就是__array_function__接口发挥作用的地方。
接口只是一个规范化的过程,用来重载(某些)numpy 函数如何处理来自你的类的参数。
让我们看一个简单的例子来处理我们的np.mean(bmi)呼叫:
使用 array_function 接口实现 np.mean 支持
再次欢呼,np.mean(bmi)返回我们的物理数组的“平均值”,它确实是一个物理量,单位为“kilogram/meter^2".”
让我们回顾一下为了实现这一点我们在代码中添加了什么。有 4 件事需要注意:
- 首先,我们在类定义之上创建一个名为
HANDLED_FUNCTION = {}的空字典。 - 其次,我们向我们的类中添加了一个名为
**__array_function__**的方法,该方法带有一个名为func的参数。我们一会儿将回到这个方法的内容。 - 第三,我们创建一个装饰器构造函数:这是一个返回装饰器的函数(即另一个接受函数作为参数的函数)。我们的
implements装饰器只是在我们的HANDLED_FUNCTION字典中创建一个 numpy 函数和一个func函数之间的对应关系,这是我们的 numpy 函数版本。 - 第四,当使用作为物理实例的
x调用np.mean(x)时,我们实现了 numpy 的 mean 来处理物理实例。它具有与np.mean大致相同的签名,并执行以下操作:
- 使用 x 的值计算数值平均值,
x._value,这是一个简单的数组。 - 然后使用平均值作为值,输入的单位作为单位,创建一个新的物理实例。
- 最后,我们在那个函数上使用
implements装饰器。
那么当我们调用np.mean(bmi)时会发生什么呢?
嗯,因为 numpy 无法计算平均值,正如我们在上面看到的,它检查bmi是否有一个__array_function__方法,并用在bmi上使用的函数调用它,即np.mean : bmi.__array_function__(np.mean, *args, **kwargs)。
由于np.mean已经在HANDELED_FUNCTIONS中注册,我们用它来代替来称呼np.mean的我们版本:这里HANDLED_FUNCTIONS[np.mean](*args, **kwargs)相当于np_mean_for_physical(*args, **kwargs)。
这就是如何让 numpy 的函数与您的自定义类一起工作。
不幸的是,这并不完全正确。这个接口只适用于一些 numpy 函数,而不是所有的函数。
还记得上面的函数列表吗?我们可以将它们分为两个子列表:常规的 numpy 函数和 numpy 通用函数——或简称为“ufuncs ”:
- 数字功能:
np.min、np.max、np.argmin、np.argmax、np.concatenate、np.vstack. - Numpy ufuncs :
np.sin、np.cos、np.tan、np.exp、np.log、np.log10、np.add、np.multiply、np.divide、np.floor、np.ceil、np.trunc
我们看到了如何使用__array_function__实现 numpy 函数支持。在下一篇文章中,我们将看到如何使用__array_ufunc__接口添加对“ufuncs”的支持。
总结一下:
- 使用 numpy 数组的容器方法在于将数组设置为自定义类实例中的属性(与数组的子类化相反)。
- 要让你的类使用 numpy 函数调用,比如
np.mean(my_array_like_instance),你必须在你的类中实现__array_function__接口。 - 这基本上是通过在你的类中添加一个
__array_function__方法,编写你自己的包装器(就像我们对np_mean_for_physical所做的那样),并将它们链接在一起(就像我们对查找字典HANDLED_FUNCTIONS所做的那样)。 - 请注意,这只适用于“常规”numpy 函数。对于 numpy 的“通用”函数,您也需要实现
__array_ufunc__接口。
这个主题非常广泛,因此您应该阅读以下几个链接,以便更好地了解什么是最重要的:
- 集装箱进场:https://numpy.org/doc/stable/user/basics.dispatch.html
__array_function__参考:https://numpy . org/doc/stable/reference/arrays . classes . html # numpy . class . _ _ array _ function _ _- ufuncs 参考:https://numpy.org/doc/stable/reference/ufuncs.html
以下是我们在本文中编写的完整代码:
干杯!
使用 Python 中的装饰模式包装 PySpark 数据帧

图多尔·巴休在 Unsplash 上的照片
如何包装 PySpark 数据帧?
在我的一个项目中,我需要增强现有的 DataFrame 功能。一种方法是实现实用程序方法,这些方法可以获取数据帧并根据需要实现附加功能。另一种方法是实现 decorator 模式,其中 Decorator 类将接受数据帧并实现其他方法。
让我拿 1
首先,让我们创建一个简单的 DataFrameDecorator 类,通过用常量参数 1 实现 take 方法来增强 DataFrame 的功能。让我们称这个方法为 take1。因此,如果修饰的数据帧不为空,该方法将返回一条记录。
上面的实现正是我们要做的。它实现了 take1 方法,该方法通过在修饰的 df 上调用 take(1) 来显式声明 take1 行。
上面的代码测试了我们刚刚实现的内容,两个打印命令返回相同的值。这是包装数据帧并获得一条记录的简单部分。如果我们想通过 DataFrameDecorator 访问 DataFrame 上所有现有的方法会怎么样?为了解决这个问题,我们将使用 getattr,但是在我们跳到这个问题之前,让我们激励一下我们为什么要使用它。
方法
实例对象理解两种属性名:数据和方法属性。方法只是实例对象的一个属性。对于本文来说,这可能没什么价值,但重要的是要提到,类有函数对象,而类实例有绑定到函数对象的方法对象。
如果你仍然不明白方法是如何工作的,看看实现也许可以澄清问题。当引用实例的非数据属性时,会搜索实例的类。如果名字表示一个有效的类属性,该类属性是一个函数对象,则通过将实例对象和函数对象打包(指向)来创建一个方法对象,这是一个抽象对象方法对象。当用参数列表调用方法对象时,从实例对象和参数列表构造新的参数列表,并且用这个新的参数列表调用函数对象。
来源:https://docs . python . org/3/tutorial/classes . html # class-objects
要查看此操作,让我们看看下面的代码:
类上的 take1 是函数对象,而实例上的 take 1 是类函数对象的绑定方法。
getattr
回到我们的问题,即 DataFrameDecorator 类不能处理所有的 DataFrame 函数。 getattr 是找不到属性时调用的方法。我们能做的就是在 DataFrameDecorator 类上实现 getattr 来处理 DataFrame 的所有功能。让我们首先看看下面的代码,以了解当我们在 DataFrameDecorator 上调用 take 时会发生什么,此时 getattr 被实现来为在 DataFrameDecorator 上未找到的任何属性返回默认字符串“function not found”。
理想情况下, getattr 返回属性,所以在这种情况下,我们返回一个函数 lambda,它在执行时只打印出没有找到原始函数。此外,显式打印 df_decorated.take 可以清楚地表明,它不是一个显式函数,而是 lambda 函数,是类 DataFrameDecorator 上的 getattr 方法的一部分。
现在,这给了我们一种方法来实现所有的 DataFrame 函数,只需在底层的 df 上调用 DataFrameDecorator 中的方法。让我们看看那会是什么样子。
上面的代码将所有这些放在一起。现在,即使没有在 DataFrameDecorator 上定义方法 take,使用 getattr 我们也可以在底层 df 上调用 DataFrame 方法。
结论
在这篇文章中,我讲述了如何使用装饰模式包装 DataFrame 以增强其功能。我希望你喜欢它。
在 LinkedIn 上与我联系或在 Medium 上关注我。如果你喜欢这个故事,你可能会喜欢我关于 python decorators 的其他故事:
https://betterprogramming.pub/decorator-pattern-and-python-decorators-b0b573f4c1ce
用 Code ML 再现性挑战结束论文—2021 年春季

DagsHub x 论文,带代码,图片由作者提供
2021 年春季 ML 再现性挑战正式结束,我们有一些由 DagsHub 社区贡献的鼓舞人心的项目来分享!
数据科学的可再现性是 DagsHub 成立的核心原因之一,我们正在不断开发新的工具和集成来支持完全可再现的工作流。这就是为什么我们对论文与代码-再现性挑战如此兴奋,并决定第二次支持其参与者(剧透 : 我们也支持 2021 年秋季挑战!).
“数据的独立验证是跨学科科学研究的基本原则。科学方法的自我修正机制依赖于研究人员复制已发表研究结果的能力,以便加强证据并在现有工作的基础上更进一步。” 性质
在 2021 年春季版中,我们支持 3 个团队提交了完全开源且可复制的 ML 论文,您现在可以轻松使用它们了!在我们深入项目之前,我们想对组织这次活动的代码为的论文以及投入时间和精力复制论文并使其可访问的社区成员表示敬意。

图片来自期限
因此,没有进一步的到期,我想欢迎 2021 年春季版的转载论文!
上下文分解解释惩罚
贡献者:
“为了有效地解释深度学习模型,它必须提供对模型的洞察,并建议相应的行动,以实现某些目标。太多时候,一连串可解释的深度学习方法在第一步就停止了,为从业者提供了对模型的洞察力,但没有办法对其采取行动。”论文作者
该论文提出了上下文分解解释惩罚,CDEP,其允许在训练期间使用解释来惩罚模型,从而不学习虚假的相关性。CDEP 在解释的帮助下将特征空间分解为相关和不相关特征,并惩罚模型来查看相关特征进行分类。例如,在 ISIC 皮肤癌分类任务中,数据集包含阳性患者佩戴创可贴的偏倚图像。通过使用 CDEP,模型可以被训练成忽略有偏差的创可贴特征,并学习正确的特征。在存储库中,CDEP 已经被应用于跨多种模态的不同架构。

图 S4。来自 ISIC 的良性样本热图,图片来自官方文件
在这个项目中,Shailesh、阿兹哈尔和 Midhush 在 Tensorflow 中重新实现了原来的 PyTorch 项目。这要求他们从头开始编写 PyTorch 的“un pool”函数,以便与 Tensorflow 一起使用。这后来成为一个补丁被推送到 Tensorflow 插件库,让它们以一个的价格贡献给两个开源项目!
少投学习的自我监督
投稿人:
- 哈斯旺斯·艾库拉
- 阿尔琼·阿肖克
在本文中,研究者在**https://research.aimultiple.com/few-shot-learning/【FSL】的背景下考察了** 自监督学习 (SSL)的作用。尽管最近的研究显示了 SSL 在大型无标签数据集上的优势,但它在小型数据集上的效用相对来说还未被探索。他们发现,SSL 将少数元学习者的相对错误率降低了 4%-27%,即使数据集很小,并且只利用数据集中的图像。****

结合监督和自我监督损失进行少镜头学习,图片来自官方论文
“我们选择这篇论文是因为少镜头学习是一种新兴的、越来越受欢迎的机器学习范式,而自我监督学习似乎是一种在 FSL 获得更好性能的简单方法,不需要花里胡哨。”Arjun 和 Haswanth
Arjun 和 Haswanth 基于作者的代码库,在五个基准数据集上复制并验证了论文的主要结果。此外,他们从头实现了域选择算法,并验证了它的好处。
论文使用了 224x224 的图像尺寸,这引起了 Arjun 和 Haswanth 的兴趣,所以他们决定研究它如何影响模型性能。他们将图像大小修改为 84x84,这是 FSL 论文中常用的设置,同时还简化了架构。他们发现这种设置是失败的,它降低了模型的性能。
可解释的 GAN 控制
贡献者
本文描述了一种简单的技术,用于分析 GANs 模型 并为图像合成创建可解释的控件,如视点变化、老化、光照和一天中的时间。

图 1:使用我们的方法发现的控件执行的图像编辑序列,应用于三个不同的 GANs,将官方文件成像
Vishnu 和 Midhush 使用了 StyleGAN 和 StyleGAN2 模型来重现论文的结果。这两种模型都是通过计算映射网络输出的主成分分析来对几个采样的潜在向量进行工作的。这给出了映射网络空间的基础,从中我们可以通过改变 PCA 坐标来编辑新的向量。扩充的向量然后被馈送到合成网络,以获得具有修改的属性的图像。
Vishnu 和 Midhush 将最初的 PyTorch 实现转换为 Tensorflow,并验证了论文中提出的主张。他们用论文中使用的基准数据集训练模型,比如 FFHQ 、 LSUN Car 和 CelebAHQ 。为了验证他们的结果,他们用原始论文中没有使用的数据集测试了模型的性能,如甲虫和动漫肖像。
“最初,我们试图使用原始 PyTorch 代码和我们在 Tensorflow 中修改的代码重新创建具有相同 RGB 值的图像。然而,由于 PyTorch 和 Tensorflow 中随机数生成器的差异,即使使用相同的种子,随机值也不相同。这导致了一些生成图像的背景伪影的微小差异。一旦我们确定这是微小差异的原因,我们就能够在 Tensorflow 实现中插入 PyTorch 的随机数生成器,并成功地再现这些图像。 最终,我们能够验证与 StyleGAN 和 StyleGAN2 型号 相关的所有声明。”毗湿奴和 Midhush
摘要
我们要感谢所有参与这项挑战的了不起的数据科学家。DagsHub 团队喜欢与你们每一个人一起工作,并在这个过程中学到了很多。你对社区产生了巨大的影响,让我们离开源数据科学又近了一步。如前所述,我们正在支持2021 年秋季版的代码可再现性挑战论文。如果你想参与并推动机器学习领域向前发展,请前往新指南页面并加入我们的 Discord 社区开始吧!团队是来帮忙的!
为你的 Keras 模型编写一个定制的训练程序
小窍门
当简单性和易用性开始阻碍你时

对于开始学习深度学习的人来说,Keras 工具箱是无可匹敌的。它拥有你所需要的一切,令人困惑的底层内容保持在最少。该 API 非常直观,让您专注于设计网络的重要部分,允许快速实验而没有太多麻烦。例如,本指南中使用的网络是用不到 25 行 Python 代码指定和训练的。
然而,有时基本 Keras 功能的易用性会受到限制。许多更高级的神经网络训练方案和损失函数变得不必要地复杂,难以用本地 Keras 进行编码。在本指南中,我旨在展示如何将训练神经网络的基本 Keras 方法分解为其基础部分,从而为用户根据需要更改每个部分提供可能性。我没有在示例中包含任何这些自定义部件;本指南的目的只是给你一些工具,让你自己去做更多的实验。
在本指南中,我将使用 FashionMNIST 数据集来设置和展示两种不同的训练神经网络的方法,以对不同服装对象的图片进行分类。该数据集包含来自 10 个对象类的 70000 幅图像,在 60000 幅训练图像和 10000 幅验证图像之间有预定义的分割。这个例子改编自 Keras 教程,在那里你可以找到更多有趣的教程。

时尚主义者数据集的一些示例图像
古典希腊
使用 Keras 构建神经网络非常简单。您可以一层一层地定义它,同时指定层的属性。这些属性是卷积滤波器的数量、内核大小、激活函数等。示例模型由两个 3x3 卷积块组成,中间有一个 4x4 最大池操作。最终的要素地图通过全局平均池操作运行,激活 softmax 的最终密集图层提供类别预测。下面的函数构建神经网络并返回包含所有层的 tf.keras.Model 对象。该对象稍后用于训练和测试神经网络。
在训练模型之前,Keras 要求我们指定一些关于训练过程的细节,比如优化器和损失函数。例如,我们还告诉 Keras 在训练过程中跟踪网络的准确性。
真正的奇迹发生在现在,在网络的训练下。训练神经网络包括在训练样本的大数据集上最小化损失函数。使用取自大数据集的小批量样本来最小化该损失函数。为这些小批量计算损失函数,并且对于每个批量,用梯度下降算法的小步骤更新网络的参数。Keras 只需调用“fit”函数,使用适当的参数,就可以处理所有这些问题。
这告诉 Keras 在带有相应标签“y_val”的训练数据集“x_train”上训练我们的网络。小批量包含 64 个图像。我们的网络将训练 10 个纪元,或者在整个训练数据集上进行 10 次传递。在这样一个时期结束时,为验证数据计算性能,允许我们在训练期间监控网络的泛化潜力。
默认情况下,Keras 会在训练过程运行时向您显示有关训练过程的有价值的信息,例如您让它跟踪的损失和潜在指标,并告诉您它通过数据集的速度。
风俗习惯
仔细想想,“fit”功能会为您处理很多细节。它组成批次,计算损失函数,推断我们应该在参数空间中的哪些方向移动,跟踪验证性能。对于大多数用例,这将是你所需要的。所有这些细节在用例之间不会有太大的变化,将它们留给 API 可以腾出时间来调整和修补重要的东西。当细节发生变化时,Tensorflow 提供了足够的工具。然而,你需要自己做更多的工作。
首先是数据集的批处理。Tensorflow 提供了两种解决这个问题的方法:“TF . data”API 和“tf.keras.utils.Sequence”类。这个例子将使用后者。“tf.data”有可能提供改进的性能,但是对于我自己编写的许多“定制”训练例程来说,“Sequence”类更容易使用。您必须创建自己的子类,实现几个在训练期间使用的函数:
- init,初始化子类的对象
- len,指定批次的数量
- getitem,编写从完整训练集中抽取一个批次的指令(这也是您经常执行某种形式的数据扩充的地方)
- on_epoch_end,可以在训练时期结束时调用,例如,执行一些数据混洗,以改变下一个时期的图像排序
接下来是实际训练的设置。您必须指定您的优化器并获得损失函数的实例。您可能还想初始化一些簿记变量。在这里,我跟踪训练和验证数据集的损失和准确性。你可以把这看作是为 Keras 的“编译”函数编写你自己的替代函数。
最后,我们到了关键的一步:训练网络。Tensorflow 允许我们将使用 Keras API 函数构建的相同模型用于自定义训练循环。然而,其他一切都将改变。现在,训练需要两个嵌套的 for 循环,而不是一个函数调用。外部循环跟踪不同的时期,内部循环提供了迭代批处理的机制。对于每个批处理迭代步骤,我们使用自定义的“Sequence”子类生成相应的批处理。Tensorflow 使用“tf”监视该批次通过网络的转发。GradientTape ',因此我们可以稍后使用损耗的梯度来确定对网络参数的必要改变。在每个时期结束时,平均训练损失和准确度存储在我们的簿记变量中。这也是根据验证数据确定网络性能的时候。
这就是全部内容:在神经网络的训练过程中,将不同部分分开的所有步骤。编写定制的训练循环将允许您更容易地跟踪各种奇异的性能度量,或者构建依赖于直接批量训练输入和标签之外的信息的损失函数。把它看作是你的神经网络工具箱的扩展。
为了完整起见,您可以在下面找到一个 Jupyter 笔记本,它将所有不同的步骤都包含在一个包中,供您自己测试。
包含所有不同步骤的 Jupyter 笔记本
在不到 30 分钟的时间内编写一个文档分类器。
让我们看看如何快速实现一个模型来对文档进行分类。

图片来自皮克斯库
在我过去的一次面试中,有人要求我实现一个模型来对论文摘要进行分类。我的目标不是得到一个完美的调优模型,而是看看我在最短时间内完成整个过程的能力。以下是我所做的。
数据
数据由 PubMed 数据库中的论文摘要组成。PubMed 是所有生物医学文献的资料库。管理 PubMed 的机构 NCBI 提供了一个下载论文的 API。已经有很多库可以用几种语言与 API 交互。我使用了 Python,我找到的最简单的库是 Bio 及其用于这个特定数据库的模块 Entrez。
我们导入该模块,并配置一个电子邮件,这是强制性的,让他们跟踪每秒的请求数。您甚至可以要求 API_KEY 将每秒的文档数增加到 10。
from Bio import EntrezEntrez.email = '[your@email.com](mailto:ya.netsec@gmail.com)'
Entrez.api_key = "abcdefghijklmnopqrstuvwxyz42"
为了从 PubMed 获取文章,我们首先执行一个查询,返回每个文档的元数据,比如它的 ID。然后,我们使用 id 来获取细节(在我的案例摘要中)。
def search(query, max_documents=1000):
handle = Entrez.esearch(db=’pubmed’,
sort=’relevance’,
retmax=max_documents,
retmode=’xml’,
term=query)
results = Entrez.read(handle)
return results
该函数将在 PubMed 数据库的参数中执行查询,根据相关性对结果进行排序,并将结果的数量限制为 max_documents。
查询其实很简单。您可以使用文档关键字和逻辑运算符。PubMed 文档详细解释了如何构建查询。
在面试中,我被要求拿到 4 门课(题目)的资料。我们通过在查询中指定每个类的相关关键字来做到这一点。
该函数的结果是一个没有内容的文档细节列表。然后,我们使用这些 id 来获取文档的所有细节。
def fetch_details(id_list):
handle = Entrez.efetch(db=”pubmed”, id=’,’.join(map(str, id_list)),rettype=”xml”, retmode=”text”)
records = Entrez.read(handle)
abstracts = [pubmed_article[‘MedlineCitation’][‘Article’] [‘Abstract’][‘AbstractText’][0] for pubmed_article in records[‘PubmedArticle’] if ‘Abstract’ in pubmed_article[‘MedlineCitation’][‘Article’].keys()]
return abstracts
该函数将获取一个 id 列表,并返回一个包含所有摘要的数组。获取特定类的所有摘要的完整函数是:
def get_abstracts_for_class(ab_class):
list_abstracts = [] ## get keywords of the class query = " AND ".join(keywords[ab_class]) res = search(query)
list_abstracts = fetch_details(res["IdList"]) return list_abstracts
我将所有关键字保存在字典中,并使用它们来构建查询。
我们为每个类调用函数,以获取所有类的所有摘要。最后,我们将它们重新格式化,得到一个可用的熊猫数据帧。
list_all_classes = []list_all_classes += [{“abs”: a, “class”: 1} for a in list_abs_class1]
list_all_classes += [{“abs”: a, “class”: 2} for a in list_abs_class2]
list_all_classes += [{“abs”: a, “class”: 3} for a in list_abs_class3]
list_all_classes += [{“abs”: a, “class”: 4} for a in list_abs_class4]abs_df = pd.DataFrame(list_all_classes)
数据清理
同样,这里的目标不是完美地清理数据集,但是一个小的标准预处理是必要的。我个人大部分时间使用 NLTK,但是你可以用几乎所有的 NLP 库做同样的事情。
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import string## 1) Lower
abs_df[“abs”] = abs_df[“abs”].str.lower()## 2) Remove tags
abs_df[“abs”] = abs_df.apply(lambda x: re.sub(“<[^>]*>”, “”, x[“abs”]), axis=1)## 3) Tokenize
abs_df[“abs_proc”] = abs_df.apply(lambda x: word_tokenize(x[“abs”]), axis=1)## 4) Remove punctuationnltk.download('punkt')
table = str.maketrans(‘’, ‘’, string.punctuation)
abs_df[“abs_proc”] = abs_df.apply(lambda x: [w.translate(table) for w in x[“abs_proc”]], axis=1)## 5) Remove non-alpha
abs_df[“abs_proc”] = abs_df.apply(lambda x: [w for w in x[“abs_proc”] if w.isalpha()], axis=1)## 6) Remove stop-words
nltk.download('stopwords')
stop_words = set(stopwords.words(‘english’))abs_df[“abs_proc”] = abs_df.apply(lambda x: [w for w in x[“abs_proc”] if not w in stop_words], axis=1)## 7) Reformat to have a single text. abs_df[“abs_proc_res”] = abs_df.apply(lambda x: ‘ ‘.join(x[“abs_proc”]), axis=1)
我们使用 Pandas 应用功能的强大功能,对整个数据帧应用相同的处理:
- 降低所有文本
- 我发现文本中有一些标签,比如来表示粗体文本。即使这些标签可能很重要,但这对于一个小时的练习来说太复杂了。所以我决定用正则表达式删除它们。
- 我们首先对文本进行记号化:即,将它分割成一系列单独的单词。
- 删除所有标点符号,如问号(?)或逗号(,)。
- 我们移除非字母符号,即数字。
- 我们删除停用词。我们首先使用 NLTK 检索英语停用词词汇,然后使用它过滤掉我们的标记。
- 最后,我们将处理过的数据连接起来,使每个摘要都有一个单独的文本。
数据嵌入
如果你熟悉 NLP 问题,那么你知道在处理文本数据时最重要的部分可能是向量表示,即嵌入。关于这一点已经取得了很多进展,并且已经提出了一些强大的模型,如谷歌的 BERT 或 OpenAI 的 GPT。然而,这些模型很难调优,绝对不适合 1 小时的练习。此外,对于许多实际问题,一个非常简单的嵌入就足以获得数据的正确矢量表示。
最简单的大概就是 TF-IDF(词频-逆文档频),也就是我用过的那个。
sklearn 库已经有一个 TF-IDF 模块,可以直接在 dataframe 上使用。
from sklearn.feature_extraction.text import TfidfVectorizervec = TfidfVectorizer()
x = vec.fit_transform(abs_df["abs_proc_res"])
此时,我们有一个矩阵 X 对应于我们所有的矢量化摘要。然而,看着 X 的形状,我们注意到了一些东西:
print(x.shape)(25054, 60329)
我们最终得到大量的列(即 60329)。这是正常的,因为这个数字对应于整个语料库(即整个数据集)的词汇大小。这个数字有两个问题。
首先,它会使模型的训练复杂化。
第二,即使我们做了大量的预处理,词汇表中的大多数单词与分类无关,因为它们没有添加任何相关信息。
幸运的是,有一种方法可以减少列数,同时避免丢失相关信息。最常见的方法称为 PCA(主成分分析),它将矩阵分解为一组不相关的低维矩阵。我们应用 SVD(奇异值分解),这是一种 PCA。还是那句话,有一个 sklearn 模块可以轻松做到。
**from** **sklearn.decomposition** **import** TruncatedSVDsvd = TruncatedSVD(n_components=100)
res = svd.fit_transform(x)print(res.shape)
(25054, 100)
我选择将我们的初始矩阵减少到 100 个组件(即特性)。这是一个要优化的参数:我们越接近初始维度,我们在缩减过程中丢失的信息就越少,而较小的数量将降低我们的模型训练的复杂性。
我们现在准备训练一个分类器。
模型
有很多分类模型。理解和实现最简单的方法之一可能是 SVM(支持向量机)。简而言之,它将试图画一条线,尽可能地将每个类的点分开。
我们还使用交叉验证来提高指标的代表性。
**from** **sklearn** **import** svm
**from** **sklearn.model_selection** **import** RepeatedKFold
**from** **sklearn.model_selection** **import** cross_val_score
**from** **sklearn.model_selection** **import** cross_validate
**from** **numpy** **import** mean
**from** **numpy** **import** stdy = abs_df["class"].values
X = rescv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)model = svm.SVC(kernel='linear', C=1, decision_function_shape='ovo')
我们使用线性内核,也就是说,它会尝试绘制一条线来分隔数据。其他核存在,如多项式,试图找到一个多项式函数,更好地分离点。
决策函数被设置为 ovo,即一对一,这将花费每次尝试来分离每对类别,而忽略其他类别。
我们训练吧!
metrics = cross_validate(model, res, y, scoring=['precision_macro', 'recall_macro'], cv=cv, n_jobs=-1) print('Precision: **%.3f** (**%.3f**)' % (mean(metrics["test_precision_macro"]), std(metrics["test_precision_macro"])))
print('Recall: **%.3f** (**%.3f**)' % (mean(metrics["test_recall_macro"]), -std(metrics["test_recall_macro"])))-----------------------------------Precision: 0.740 (0.021)
Recall: 0.637 (0.014)
这里有两个有趣的指标:精确度和召回率。
精度是指,对于每一类,在预测的文档中,有 74%是正确预测的,这已经不错了。
另一方面,召回意味着在某个类的所有文档中,我们能够捕获 63%。
结论和展望
正如你所看到的,只使用机器学习的基础知识,实现一个快速分类器是相对容易的。当然它并不完美,但是当你什么都没有的时候,即使是一个糟糕的模型也是可以接受的。
很明显,还有很多可以改进的地方。预处理可能是对模型影响最大的部分。例如,不使用 TF-IDF,我们可以尝试更复杂的算法,如 BERT。从模型的角度来看,我们也可以尝试其他分类器,甚至堆叠几个分类器以获得更好的性能。
也就是说,如果您的目标是有一个工作模型来分类您的文档,这是一个很好的起点。
下一步就是把这个投入生产!我将在另一篇文章中讨论这一部分。
写几行代码检测人脸,从复杂图像中画出地标~MediaPipe

图片由皮克斯拜的 Gerd Altmann 提供
使用 MediaPipe 从复杂图像中检测人脸既简单又有趣
媒体管道概述
MediaPipe 是 Google 内部最广泛共享和可重用的媒体处理库之一。”— Kahye Song
G oogle 开源 MediaPipe 于 2019 年 6 月首次推出。它旨在通过提供一些集成的计算机视觉和机器学习功能来使我们的生活变得简单。媒体管道是一个框架,用于构建多模态(如视频、音频或任何时间序列数据)、跨平台(如 eAndroid、IOS、web、边缘设备)的应用 ML 管道。Mediapipe 还有助于将机器学习技术部署到各种不同硬件平台上的演示和应用程序中。
值得注意的应用
- 人脸检测
- 多手跟踪
- 头发分割
- 目标检测和跟踪
- 反对:3D 物体检测和跟踪
- 自动裁剪:自动视频裁剪管道
以此类推..
您为什么需要 MediaPipe
有效管理资源(CPU 和 GPU)以实现低延迟性能,处理音频和视频帧等时序数据的同步,以及 MediaPipe 必不可少的更多有效理由。MediaPipe 将每个感知模型抽象成一个模块,并用维护图将它们连接起来。除了上述特性,MediaPipe 还支持 TensorFlow 和 TF Lite 推理引擎。任何 TensorFlow 和 TF Lite 模型都可以在 MediaPipe 上使用。同时在移动和嵌入式平台上,MediaPipe 也支持设备本身的 GPU 加速。
是时候介绍 MediaPipe 的一个令人惊叹的应用了,人脸检测。
人脸检测
考虑这样一个场景,“一个零售商要求你统计访客的数量,并跟踪访客在他们店里的活动。”
似乎很难!! 怎样才能解决问题? 嗯嗯……
哦,是的!我们将使用人脸跟踪来解决这个问题。现在的问题是怎样才能检测出顾客的脸。这个问题的答案是人脸跟踪技术使用人脸检测作为检测人脸的第一步。
人脸检测是计算机视觉中定位和定位照片中的一个或多个人脸的问题。
这个问题的一般陈述可以定义如下:给定一幅静止或视频图像,检测并定位未知数量(如果有的话)的人脸——人脸检测:一项调查,2001。
使用 MediaPipe 执行面部检测的模型:
为了执行面部检测,使用了三种模型:
- 近距离模式(最适合距离摄像头 2 米以内的人脸)
- 全范围模型(密集,最适合距离摄像机 5 米以内的人脸)
- 全范围模型(稀疏,最适合距离摄像机 5 米以内的人脸)
全范围密集和稀疏模型在**F-score方面具有相同的质量,但是在底层指标方面有所不同。密集模型在 召回 上稍好,而稀疏模型在 精度 上优于密集模型。**
是时候用 MediaPipe 的动手人脸检测模型让我们的手变脏了。
循序渐进方针
安装必要的库
要执行面部检测,您必须首先在机器上安装 MediaPipe。如果您是 windows 用户,那么您可以在计算机的命令提示符下运行下面的代码。
pip install mediapipe
有关详细说明,您可以点击以下链接:
https://Google . github . io/media pipe/getting _ started/python . html
你还需要安装 OpenCV 用于网络摄像头或图像输入。如果您是 windows 用户,您可以在命令提示符下运行下面的代码。
pip install opencv-python
有关详细说明,您可以点击以下链接:
https://pypi.org/project/opencv-python/
写代码了解 API 的使用:
我们使用 Google Colab 来运行代码。你可以随意使用它。导入必要的库。我们需要cv2模块来读取和显示图像,以及 MediaPipe 模块,它公开了我们执行面部检测所需的功能
项目中提供了 google colab 版本和 jupyter 笔记本版本的全部代码。本文结论部分的末尾给出了链接。
*import cv2
import mediapipe as mp*
然后我们将访问两个子模块face_detection 和drawing_utils **。face_detection**为用于加载所有功能以执行人脸检测,而drawing_utils 用于在图像上绘制检测到的人脸。******
*****mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils*****
是时候深入挖掘代码了。首先,我们将一幅图像作为输入。这里我们使用两种类型的图像
(i)包含两米内的面部的图像
(ii)五米内包含人脸的图像。
我们使用来自 colab 的files 直接从本地目录加载图像。当您在本地计算机上工作时,也可以使用cv2.imread()加载图像。
(a)拍摄的第一张图像
*****from google.colab import files
uploaded_short_range = files.upload()*****
(b)拍摄的第二张图像
*****from google.colab import files
uploaded_full_range = files.upload()*****
在本地电脑上工作时,您可以使用
*****cv2.imread() #to take input.*****
点击这里 了解更多cv2.imread()
现在,我们将调整图像的大小并显示图像。为了显示图像,我们必须使用来自 colab 的**cv2_imshow** 模块,或者在本地机器上工作时使用cv2.imshow(frame name, iamge)模块。我们可以使用下面的代码来调整和显示谷歌 colab 中的图像。
用于调整大小和显示图像的代码

上述代码的输出示例(照片由 Radu Florin 在 Unsplash 上拍摄)
现在,我们将在面上绘制地标。
我们可以如下改变thickness 和circle_radius的值。
*****drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)*****
要了解关于mp.solutions.face_detection 运行的详细信息
代码下方:
*****help(mp_face_detection.FaceDetection)*****
在这之后,我们将创建一个类为Face detection的对象。这个对象将允许我们处理图像并执行面部标志估计。此类的构造函数支持以下参数:****
****(一)型号选择:整数索引 0 或 1。使用 0 选择最适合距离摄像机 2 米以内的人脸的短程模型,使用 1 选择最适合距离摄像机 5 米以内的人脸的全程模型。对于全范围选项,使用稀疏模型以提高推理速度。具体请参考 型号卡 。如果未指定,则默认为 0。
****(ii)MIN _ DETECTION _ CONFIDENCE:来自面部检测模型的最小置信度值([0.0,1.0]),用于检测被认为是成功的。默认为 0.5。
*****with mp_face_detection.FaceDetection(min_detection_confidence=0.5, model_selection=0) as face_detection:*****
上面的代码model_selection = 0表示我们选择近距离模型进行人脸检测。使用下面的代码,我们使用一个简短的图像模型进行最终的人脸检测,并绘制出地标。
短长度(2 米以内)图像的人脸检测模型

作者图片
现在对于model_selection = 1来说,这意味着我们选择人脸检测 full-range model。使用下面的代码,我们使用完整的图像模型执行最终的人脸检测,并绘制地标。
全长(5 米以内)图像的人脸检测模型

作者图片
我们也可以用全长人脸检测模型的代码在团体照片上执行这个过程。

作者图片
使用 MediaPipe,我们可以超越人脸检测。下面这篇关于algo scale*的文章,将为你展示一个使用 OpenCV 和 MediaPipe 估算姿态的方向。*****
使用 OpenCV 和 MediaPipe 进行姿态估计
结论
人脸检测是计算机视觉中最常见的问题之一。有许多用于人脸检测和人脸标志绘制的技术。最有效的技术是在深度学习模型的帮助下创建的。但是如果我们试图从零开始制作模型,这需要巨大的计算能力、复杂的知识以及数据集。大多数情况下,是老百姓的问题。Mediapipe 库在使困难的任务变得简单方面是惊人的。该库提供定制的内置模型。在本文中,我们刚刚展示了使用 MediaPipe 进行面部检测和面部标志绘制的简单易行的过程。在接下来的文章中,将在 MediaPipe 的帮助下展示更多简单而有用的技术。
github 中的源代码
Colab 源代码……..
Jupyter 笔记本源代码…..
给你一些更有趣的文章
祖贝尔·侯赛因
使用 PyCaret 编写和训练您自己的自定义机器学习模型
一步一步的,初学者友好的教程,关于如何用 PyCaret 编写和训练定制的机器学习模型

罗布·兰伯特在 Unsplash 上拍摄的照片
PyCaret
PyCaret 是一个开源的低代码机器学习库和端到端的模型管理工具,内置于 Python 中,用于自动化机器学习工作流。它因其易用性、简单性以及快速高效地构建和部署端到端 ML 原型的能力而广受欢迎。
PyCaret 是一个替代的低代码库,可以用几行代码代替数百行代码。这使得实验周期成倍地快速和有效。
py caret简单 好用。PyCaret 中执行的所有操作都顺序存储在一个管道中,该管道对于部署是完全自动化的。无论是输入缺失值、一键编码、转换分类数据、特征工程,甚至是超参数调整,PyCaret 都能实现自动化。
本教程假设您对 PyCaret 有一定的了解和经验。如果您以前没有使用过,没关系,您可以通过这些教程快速入门:
- PyCaret 2.2 已经发布——新功能
- 宣布 PyCaret 2.0
- 关于 PyCaret 你不知道的五件事
正在安装 PyCaret
安装 PyCaret 非常容易,只需要几分钟。我们强烈建议使用虚拟环境来避免与其他库的潜在冲突。
PyCaret 的默认安装是 pycaret 的精简版本,只安装这里列出的硬依赖项。
**# install slim version (default)** pip install pycaret**# install the full version**
pip install pycaret[full]
当你安装 pycaret 的完整版本时,这里列出的所有可选依赖项也会被安装。
👉我们开始吧
在我们开始讨论定制模型训练之前,让我们来看一个 PyCaret 如何处理开箱即用模型的快速演示。我将使用 PyCaret 的存储库上的“保险”数据集。该数据集的目标是基于一些属性预测患者费用。
👉数据集
**# read data from pycaret repo** from pycaret.datasets import get_data
data = get_data('insurance')

保险数据集中的样本行
👉数据准备
对于 PyCaret 中的所有模块来说,setup是在 PyCaret 中执行的任何机器学习实验中的第一个也是唯一一个强制步骤。该函数负责训练模型之前所需的所有数据准备。除了执行一些基本的默认处理任务,PyCaret 还提供了一系列预处理功能。要了解 PyCaret 中所有预处理功能的更多信息,您可以查看这个链接。
**# initialize setup** from pycaret.regression import *
s = setup(data, target = 'charges')

pycaret.regression 模块中的设置函数
每当在 PyCaret 中初始化setup函数时,它都会分析数据集并推断所有输入要素的数据类型。如果所有数据类型都推断正确,您可以按 enter 键继续。

设置的输出—为显示而截断
👉可用型号
要查看所有可用于训练的模型列表,您可以使用名为models的功能。它显示一个表格,其中包含模型 ID、名称和实际评估者的参考。
**# check all the available models** models()

models 的输出()-出于显示目的,输出被截断
👉模型训练和选择
PyCaret 中训练任何模型使用最多的函数是create_model。它需要一个你想要训练的估计器的 ID。
**# train decision tree** dt = create_model('dt')

create_model 的输出(' dt ')
输出显示了带有平均值和标准差的 10 倍交叉验证指标。这个函数的输出是一个经过训练的模型对象,它本质上是一个scikit-learn对象。
print(dt)

打印输出(dt)
要在一个循环中训练多个模型,您可以编写一个简单的列表理解:
**# train multiple models**
multiple_models = [create_model(i) for i in ['dt', 'lr', 'xgboost']]**# check multiple_models** type(multiple_models), len(multiple_models)
>>> (list, 3)print(multiple_models)

打印输出(多种型号)
如果您想训练库中所有可用的模型,而不是选定的几个,您可以使用 PyCaret 的compare_models函数,而不是编写自己的循环(结果将是相同的,尽管)。
**# compare all models**
best_model = compare_models()

compare_models 函数的输出
compare_models返回显示所有模型的交叉验证指标的输出。根据这个输出,梯度推进回归器是最好的模型,在训练集上使用 10 重交叉验证,平均绝对误差【MAE】为 2702 美元。****
****# check the best model**
print(best_model)**

打印输出(最佳模式)
上面表格中显示的指标是交叉验证分数,用于检查保留集上的best_model的分数:
****# predict on hold-out** pred_holdout = predict_model(best_model)**

预测模型(最佳模型)函数的输出
要在看不见的数据集上生成预测,您可以使用相同的predict_model函数,但只需传递一个额外的参数data:
****# create copy of data drop target column**
data2 = data.copy()
data2.drop('charges', axis=1, inplace=True)**# generate predictions** predictions = predict_model(best_model, data = data2)**

预测模型的输出(最佳模型,数据=数据 2)
👉编写和培训自定义模型
到目前为止,我们看到的是 PyCaret 中所有可用模型的训练和模型选择。然而,PyCaret 为定制模型工作的方式是完全相同的。只要您的估算器与sklearn API 风格兼容,它就会以同样的方式工作。我们来看几个例子。
在向您展示如何编写自己的定制类之前,我将首先演示如何使用定制的非 sklearn 模型(sklearn 或 pycaret 的基库中没有的模型)。
👉 GPLearn 车型
虽然遗传编程(GP)可以用来执行非常多种多样的任务,gplearn被有目的地限制于解决符号回归问题。
符号回归是一种机器学习技术,旨在识别最佳描述关系的基础数学表达式。它首先构建一组简单的随机公式来表示已知自变量与其因变量目标之间的关系,以预测新数据。每一代程序都是通过从种群中选择最适合的个体进行遗传操作而从上一代进化而来的。
要使用gplearn的型号,您必须先安装它:
****# install gplearn** pip install gplearn**
现在您可以简单地导入未训练的模型,并在create_model函数中传递它:
****# import untrained estimator**
from gplearn.genetic import SymbolicRegressor
sc = SymbolicRegressor()**# train using create_model** sc_trained = create_model(sc)**

create_model 的输出(sc_trained)
**print(sc_trained)**

打印输出(sc_trained)
您还可以检查这方面的坚持分数:
****# check hold-out score** pred_holdout_sc = predict_model(sc_trained)**

预测模型(sc_trained)的输出
👉NGBoost 型号
ngboost 是一个实现自然梯度提升的 Python 库,如“NGBoost:概率预测的自然梯度提升”所述。它建立在 Scikit-Learn 的基础上,在选择适当的评分规则、分布和基础学习者方面设计为可扩展和模块化的。本幻灯片提供了 NGBoost 基础方法的教学介绍。
要使用 ngboost 中的模型,您必须首先安装 ngboost:
****# install ngboost**
pip install ngboost**
安装后,您可以从 ngboost 库中导入未经训练的评估器,并使用create_model来训练和评估模型:
****# import untrained estimator**
from ngboost import NGBRegressor
ng = NGBRegressor()**# train using create_model** ng_trained = create_model(ng)**

create_model 的输出(ng)
**print(ng_trained)**

打印输出(ng_trained)
👉编写自定义类
上面的两个例子gplearn和ngboost是 pycaret 的定制模型,因为它们在默认库中不可用,但是您可以像使用任何其他现成模型一样使用它们。然而,可能有一个用例涉及编写你自己的算法(即算法背后的数学),在这种情况下,你可以从sklearn继承基类并编写你自己的数学。
让我们创建一个简单的估计器,它在fit阶段学习target变量的平均值,并预测所有新数据点的相同平均值,而不考虑 X 输入(可能在现实生活中没有用,只是为了演示功能)。**
****# create custom estimator**
import numpy as npfrom sklearn.base import BaseEstimatorclass MyOwnModel(BaseEstimator):
def __init__(self):
self.mean = 0
def fit(self, X, y):
self.mean = y.mean()
return self
def predict(self, X):
return np.array(X.shape[0]*[self.mean])**
现在让我们使用这个估计器进行训练:
****# import MyOwnModel class**
mom = MyOwnModel()**# train using create_model** mom_trained = create_model(mom)**

create_model(mom)的输出
****# generate predictions on data**
predictions = predict_model(mom_trained, data=data)**

predict_model 的输出(mom,data=data)
请注意,Label列实际上是所有行的预测,是相同的数字$13,225,这是因为我们以这样的方式创建了该算法,它从训练集的平均值中学习并预测相同的值(只是为了保持简单)。
我希望您会喜欢 PyCaret 的易用性和简单性。只需几行代码,您就可以执行端到端的机器学习实验,并编写自己的算法,而无需调整任何本机代码。
即将推出!
下周我将写一篇教程来推进这篇教程。我们将编写一个更复杂的算法,而不仅仅是一个均值预测。我将在下一个教程中介绍一些复杂的概念。请在 Medium 、 LinkedIn 、 Twitter 关注我,获取更多更新。
使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。
要了解更多关于 PyCaret 的信息,请关注我们的 LinkedIn 和 Youtube。
加入我们的休闲频道。此处邀请链接。
您可能还对以下内容感兴趣:
使用 PyCaret 2.0
在 Power BI 中构建您自己的 AutoML 使用 Docker 在 Azure 上部署机器学习管道
在 Google Kubernetes 引擎上部署机器学习管道
在 AWS Fargate 上部署机器学习管道
构建并部署您的第一个机器学习 web 应用
使用 AWS Fargate serverless
部署 PyCaret 和 Streamlit 应用
重要链接
文档
博客
GitHub
stack overflow
安装 PyCaret 笔记本教程 贡献于 PyCaret
想了解某个特定模块?
单击下面的链接查看文档和工作示例。
使用爱因斯坦符号编写更好更快的 Python
使用“einsum”让你的代码更易读、简洁、高效

刘易斯·康的照片在 Unsplash
在 Python 中处理线性或多线性代数时,求和循环和 NumPy 函数可能会变得相当混乱,难以阅读,甚至很慢。在我发现 NumPy 的einsum功能之前,情况就是这样,我很惊讶不是每个人都在谈论它。
我将向您展示如何使用 NumPy 、 TensorFlow、或 PyTorch 中的爱因斯坦符号来使您的代码更具可读性、简洁和高效。
理解爱因斯坦符号
爱因斯坦符号的基础是去掉求和符号σ,当它不会引起歧义时(当我们可以确定指数的界限时)。
例#1:矩阵的乘积
在下面的公式中,矩阵 A 的形状为(m, n),矩阵 B 的形状为(n, p)。

因为我们从矩阵的形状中知道了 I,j 和 k 的界限。我们可以将公式简化为:

例 2:两个向量的点积
两个 n 维向量的点积为:

我们可以用爱因斯坦符号把它写成:

例 3:两个矩阵的点积
我们可以使用以下公式定义两个矩阵的点积:

在爱因斯坦的符号中,这很简单:

例子#4:张量
我们可以使用两个以上的指数。张量(高阶矩阵)。
例如,我们可以这样写:

或者甚至像这样:

你明白了!
什么时候使用爱因斯坦符号?
这主要发生在你处理向量、矩阵和/或张量的时候,你必须:以特定的方式对它们进行乘法、转置和/或求和。
用爱因斯坦符号写出这些运算的组合结果会更简单。
使用 Python 的 einsum
einsum在numpy、torch、tensorflow中实现。在所有这些模块中,它都遵循语法einsum(equation, operands)。

这里我们用指数代替 ■ 。在->之后,我们将输出指数。
这相当于:

如果输入或输出是标量(它没有索引),我们可以让索引为空。
下面是上面的例子。
例子#1:矩阵乘法

einsum("ik,kj->ij", A, B)
示例 2:矢量点积

einsum("i,i->",u, v)
示例#3:矩阵点积

einsum("ij,ij->", A, B)
例子#4:张量

einsum("ijkl,klij->ij", A, B)

einsum("iqrj,klqmr->ijklm", A, B)
你可以用它来处理几乎任何涉及线性代数和多线性代数的公式。
表演
那么与使用循环或 numpy 函数相比,einsum的性能如何呢?
我决定使用三种方法运行示例#3 :
运行 100 万次测试并使用timeit后:
- 循环:24.36 秒
- 内置函数:7.58 秒
- 爱因斯坦:3.78 秒
einsum显然是更快。实际上,比 numpy 的内置函数快两倍,在这种情况下,比循环快 6 倍。
einsum 为什么快?
这归结为 numpy 是用 c 写的。
当使用本地 Python 循环时,所有的数据操作都发生在 Python 解释器中。
当使用内置的 numpy 函数时,它发生在 C 中,这为 numpy 开发人员提供了优化代码的能力。这就是 numpy 更快的原因。
但在使用einsum时,numpy 在 C 中处理一次数据并返回最终结果,而使用多个 numpy 函数则花费更多时间返回多个值。
在某些情况下,这可能是一个很好的俏皮话。虽然这不是提高代码可读性和效率的唯一方法,但在可能的情况下,使用它肯定是不需要动脑筋的。
不过,还有其他方法可以优化 Python 代码,比如使用缓存,我将在以后的文章中介绍这一点。
在 Rust 中编写更好的匹配语句
使用闭包来编写更清晰的匹配语句

如果你用 Rust 编程,你肯定用过 match 语句。这是 Rust 中决策的首选方式,还有 if-let 语句。然而,许多初学者在使用 match 语句时可能会有冗余,重复的模式会导致可读性降低和糟糕的代码。
情况
为了理解如何优化匹配语句,从而避免冗余,让我们举一个简单的例子。假设我们正在为一个基本的基于文本的应用程序编写解析用户输入的逻辑,该应用程序添加、删除或获取特定部门的雇员。
实现的五个基本命令是~
1。GET
2。添加<姓名> <部门> -向给定部门添加新员工。
3。删除<姓名> <部门> -从部门中删除一个员工。
4。帮助- 显示帮助信息,最后是
5。退出- 退出程序。
考虑到获取、添加和移除命令的逻辑已经被编程。使用它,我们得到了一个接受CmdType对象的函数。CmdType枚举如下所示。
解析命令
现在,我们可以快速编写一个输入解析机制,如下所示,用于从标准输入中获取命令。
我们想到的第一个实现是为输入字符串的第一个单词创建一个match。让我们现在就这样做,然后我们会看到我们如何优化它,以及由此产生的任何问题。
注意,我们没有向
handle_command传递任何其他参数,因为它是一个已经捕获了变量inp的闭包,并将其传递给命令逻辑。以这种方式使用闭包可以防止代码重复,并稍微清理代码。
似乎不错?不完全是。使用当前的 match 语句,我们允许 ADD 和 REMOVE 命令,用户可能没有添加这两个参数,现在需要将处理这些参数的代码添加到处理命令的逻辑中,这并不理想。
解决这个问题的一个方法是在运行 add 和 remove 的匹配语句时检查参数的数量,有点像下面的代码。
这段代码的问题很明显。我们正在重复添加和删除命令的模式。重用的一种方法是用"add" | "remove"替换匹配臂,然后如果参数是正确的,再次检查值,但这也是重复的,因为每个要添加的新命令都需要插入到布尔表达式中。
相反,有两种更好的方法来解决这个问题。
1。在 match 语句中添加 2 个参数要求作为参数。
2。使用闭包来检查参数的数量,并在需要验证时使用闭包。
第二种方法的可伸缩性稍强,因为它不需要对所需的参数数量进行硬编码,但是我们将对这两种解决方案都进行编码。
现在让我们编写第一个方法。为此,我们可以使用第一个单词的元组和命令是否满足 2 个参数的要求,作为匹配参数。
好多了,不是吗!但是,此方法将参数的数量硬编码为 2。如果我们有一个需要三个参数的命令会怎么样?这就引出了我们的最后一个方法。
为此,让我们编写闭包代码。回想一下,闭包应该将命令类型和参数数量作为输入,如果输入符合参数要求,就调用handle_command。
整洁!现在剩下要做的就是从比赛队伍中调用这个关闭。这看起来有点像下面的。
太神奇了!这段代码与我们的第一段代码非常相似,尽管有不同的闭包和另一个参数。这本身就是对我们最终代码的证明,因为它看起来非常简单,同时也是可伸缩和可实现的。
结论,谢谢!

图片来自作者,使用碳生成
在本文中,我们简要介绍了使用闭包重构匹配语句,并涵盖了边缘情况。我希望你能从这篇文章中学到一些东西,并在以后的文章中写得更干净。为任何反馈留下评论。
感谢您的阅读,祝您有美好的一天!
使用管道编写干净的 Python 代码
一种简洁明了的处理迭代的方法
动机
map和filter是处理 iterables 的两种有效的 Python 方法。然而,如果同时使用map和filter,代码看起来会很混乱。

作者图片
如果可以使用管道|在一个可迭代对象上应用多种方法不是很好吗?

作者图片
库管道允许您这样做。
什么是管道?
管道是一个 Python 库,使你能够在 Python 中使用管道。管道(|)将一个方法的结果传递给另一个方法。
我喜欢 Pipe,因为它使我的代码在对 Python iterable 应用多个方法时看起来更干净。由于 Pipe 只提供了几种方法,所以学习 Pipe 也非常容易。在这篇文章中,我将向你展示一些我认为最有用的方法。
要安装管道,请键入:
pip install pipe
其中—可迭代中的过滤元素
与 SQL 类似,Pipe 的where方法也可以用来过滤 iterable 中的元素。

作者图片
选择—将函数应用于可迭代对象
select方法类似于map方法。select对 iterable 的每个元素应用一个方法。
在下面的代码中,我使用select将列表中的每个元素乘以 2。

作者图片
现在,你可能想知道:如果方法where和select具有与map和filter相同的功能,为什么我们还需要它们?
这是因为您可以使用管道将一个方法插入到另一个方法中。因此,使用管道可以删除嵌套的括号,使代码更具可读性。

作者图片
展开迭代
chain —将一系列可重复项链接起来
使用嵌套的 iterable 可能会很痛苦。幸运的是,您可以使用chain来链接一系列可重复项。

作者图片
即使在应用了chain之后 iterable 的嵌套减少了,我们仍然有一个嵌套列表。为了处理深度嵌套的列表,我们可以使用traverse来代替。
遍历-递归展开迭代
traverse方法可用于递归展开可重复项。因此,您可以使用此方法将深度嵌套的列表转换为平面列表。

作者图片
让我们将这个方法与select方法集成在一起,以获取字典的值并展平列表。

作者图片
相当酷!
将列表中的元素分组
有时,使用某个函数对列表中的元素进行分组可能会很有用。用groupby方法很容易做到这一点。
为了了解这种方法是如何工作的,让我们将一个数字列表转换成一个字典,根据数字是奇数还是偶数对其进行分组。

作者图片
在上面的代码中,我们使用groupby将数字分组到Even组和Odd组。应用此方法后的输出如下所示:
[('Even', <itertools._grouper at 0x7fbea8030550>),
('Odd', <itertools._grouper at 0x7fbea80309a0>)]
接下来,我们使用select将元组列表转换为字典列表,字典的键是元组中的第一个元素,值是元组中的第二个元素。
[{'Even': [2, 4, 6, 8]}, {'Odd': [1, 3, 5, 7, 9]}]
酷!为了只获取大于 2 的值,我们可以在select方法中添加where方法:

作者图片
请注意,输出中不再有2和1。
重复数据删除—使用密钥删除重复数据
方法删除了列表中的重复项。

作者图片
这听起来可能没什么意思,因为set方法可以做同样的事情。但是,这种方法更加灵活,因为它使您能够使用键获得唯一的元素。
例如,您可以使用此方法获取一个小于 5 的唯一元素和另一个大于或等于 5 的唯一元素。

作者图片
现在,让我们将这个方法与select和where结合起来,以获得具有重复键和None值的字典的值。

作者图片
在上面的代码中,我们:
- 移除具有相同
name的项目 - 获取
count的值 - 只选择整数。
在几行代码中,我们可以对一个 iterable 应用多种方法,同时仍然保持代码的整洁。很酷,不是吗?
结论
恭喜你!您刚刚学习了如何使用管道来保持代码简洁。我希望这篇文章能给你一些知识,把复杂的迭代运算变成一行简单的代码。
随意发挥,并在这里叉这篇文章的源代码:
https://github.com/khuyentran1401/Data-science/blob/master/productive_tools/pipe.ipynb
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。
星这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:
</4-pre-commit-plugins-to-automate-code-reviewing-and-formatting-in-python-c80c6d2e9f5> 💔-python-tricks-to-read-create-and-run-multiple-files-automatically-5221ebaad2ba>
用人工智能助手编写高质量的代码
跟上潮流,提高工作效率,并遵循 Tabnine 的最佳实践

你开发新功能的流程是什么?例如,如何向 API 发出新的请求来检索一些关键信息?如何实例化客户端?你用什么方法发布你的 JSON 数据?它的标志是什么?你期望得到什么回报?
全屏显示您的 IDE,开始编写代码,并有一个助手在您的指尖为您提供基于上下文的高质量建议,这不是很好吗?
我猜你的第一个想法是查看库的文档。然后呢?也许可以试着跟随一些教程或者建立几个例子。你也可以在 StackOverflow 停下来复制粘贴,我的意思是,寻找答案,获得一些灵感。

StackOverflow 的愚人节恶作剧——作者截图
然而,这种工作流程会把你扔出那个区域,让你的工作效率降低。此外,您不会从复制粘贴的固定解决方案中学到任何新东西,同时,您会用质量可疑的代码污染您的代码库。
全屏你的 IDE,开始写代码,指尖有一个助手根据上下文给你高质量的建议不是很好吗?
让我们看看世界领先的人工智能代码完成工具如何让我们离最终目标更近一步。
学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
泰伯宁
Tabnine 是世界领先的人工智能代码完成工具,可用于 30 多种语言,受到 100 多万开发人员的信任。

Tabnine —作者图片
Tabnine 的底层模型(主要是 GPT-2 )是在高质量的公共 GitHub 库上训练出来的。然而,如果你选择加入team计划(每个用户每月 24.00 美元),你可以在你的代码库上个性化和培训他们。
您可以利用它的预测来更快地编写代码,提高生产率,减少错误,并遵循使那些伟大的开源项目大放异彩的编码实践。
Tabnine 在 15 个 ide 上支持 30 多种语言。因此,在大多数情况下,它独立于语言和工具。为此,tabnine 为 Python、JavaScript TypeScript、Java、Ruby、PHP、Go、C++等等提供了代码完成功能。此外,它可以在 VS Code、IntelliJ、Android Studio、Atom、PyCharm、Sublime Text 和任何其他主流 IDE 或代码编辑器上使用。
因此,如果您正在用 tabnine 支持的任何语言编写代码,并且可以在您的 IDE 中安装 tabnine 扩展,那么您可以:
- 跟上潮流:停止在网上寻找答案,滚动文档页面,或者浏览论坛主题,让你的代码发挥作用。专注于你的工作环境,减少上下文切换。
- 更快地编码:拥有一个强大的开源库让你更加自信和高效。再也不需要键入整行的代码,记住特定操作的语法,并担心打字错误。
- 发现最佳实践: Tabnine 后端使用 GPT-2,一种可以预测序列中下一个单词的语言模型。使用高质量的 GitHub 库,对 GPT-2 进行了扩展和再利用,以涵盖代码完成的用例。因此,它为您提供了最佳的通用编码实践,因此您可以将精力集中在尚未解决的问题上。
- 保护你的隐私:你写的代码会保存在你的机器上。Tabnine 将模型下载到您的本地环境,并在您的机器上进行预测。如果你愿意,你甚至可以离线工作!
技术细节
正如我们已经看到的,tabnine 使用深度学习,主要是 GPT-2,来执行代码完成。GPT-2 是一个语言模型,这意味着它是在一个大型文本语料库上训练的,以预测句子中的下一个单词。
tabnine 团队通过在高质量的 GitHub repos 上训练它,重新利用它来学习常见的代码习惯用法和模式。tabnine 最强大的模型使用超过 3.8 亿个参数,并将 GPT-2 扩展到编程世界。
因此,tabnine 使用语言模型(如 GPT-2 和语义模型)的组合,根据同一文件中的所有其他内容来预测最有可能的命令。此外,tabnine 模型会定期更新,以跟上公共 GitHub 存储库和其他可靠来源中的新模式。
Tabnine 支持所有主流编程语言:Javascript、Python、Typescript、PHP、Java、C++、C、C#、Objective-C、Go、Rust、Perl、Ruby、Swift、Haskell、Scala、F#、Kotlin、Julia、Lua、SQL 和 Bash。
它还支持最常见的配置语言和 web 技术:JSON、YAML、TOML、HTML、CSS 和 Markdown。
最后,由于 tabnine 使用的模型理解英语,它提供了注释完成支持,这在其他地方是不容易找到的!
结论
编写代码时,上下文切换越多,效率就越低。在一个理想的场景中,您将全屏显示您的 IDE,开始编写代码,并且有一个助手在您的指尖为您提供基于上下文的高质量建议。
这个故事介绍了 tabnine,世界领先的人工智能代码完成工具,可用于 30 多种语言,受到 100 多万开发人员的信任。您可以使用 tabnine 更快地编写代码,提高生产率,减少错误,并遵循让那些伟大的开源项目大放异彩的编码实践。
首先,在 IDE 或代码编辑器中安装 tabnine,然后像往常一样开始编写代码!例如,在这里你可以找到如何在 VS 代码上设置标签的说明。此外,您可以通过它的创建者之一使用 VS 代码来观看这个演练。
关于作者
我的名字是迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。请访问我的网站上的资源页面,这里有很多好书和顶级课程,开始构建您自己的数据科学课程吧!
用 Python 集合编写超简洁的代码

内置的 Python collections库是有用工具的宝库。我将把重点放在我认为最有用的两个结构上:Counter和defaultdict。理解这些数据结构将有助于你使你的代码更加简洁、易读和易于调试。
计数器
Counter对象接受一个 iterable 并将项目聚合成 iterable 中唯一值的计数。结果存储在一个类似字典的结构中,其中唯一项是键,计数是值。例如,以下代码获取单词列表并返回每个单词的计数:
from collections import Countertext = "apple banana orange apple apple orange"
counts = Counter(text.split())print(counts.most_common())
# [('apple', 3), ('orange', 2), ('banana', 1)]print(counts['apple'])
# 3print(counts['pear'])
# 0
您可以像使用普通 Python 字典一样从Counter中检索值。注意,Counter有一个非常好的特性,如果你查询一个不存在的键(比如上面的‘pear’),它会返回 0 而不是给你一个KeyError。
Counter对象的另一个非常有用的特性是它们可以用一个简单的+操作符合并。这使得合并来自不同位置/文件的项目计数变得轻而易举:
这节省了大量时间和代码行。我在文本处理/NLP 任务中经常使用Counter,这无疑让我的生活变得更加轻松。以下是使用Counter的一些最终提示和技巧:
- 使用
dict()将计数器转换成普通的 Python 字典。 - 使用不带参数的
most_common()函数返回(item,count)元组列表,按计数降序排序。 - 使用
Counter计算字符串中的字符数——这很有效,因为字符串在 Python 中是可迭代的。
默认字典
当你不想担心KeyErrors和特殊情况时,这是基本字典数据结构的一个很好的替代。您只需用您选择的默认值创建一个defaultdict,数据结构会自动将默认值分配给任何以前看不见的键。需要理解的重要一点是,defaultdict构造函数的参数应该是callable。这包括以下内容:
list:默认为空列表int:默认为 0lambda表情:非常灵活,可以让任何东西随时调用set:默认为空集
这是一个非常有用的数据结构,因为它消除了在增加/修改一个条目的值之前检查它是否存在于字典中的需要。
让我们看一个实际的例子,看看什么时候我们可以使用 defaultdict 来编写真正优雅、简洁的代码。下面的代码使用defaultdict在 5 行代码中从头开始实现了一个二元语法语言模型的训练循环!关于 n-gram 语言模型的更多内容,你可以查看我以前写的一篇文章这里,在这篇文章中我没有使用defaultdict。注意我们通过使用defaultdict节省的代码量!
诀窍是在上面的第 6 行嵌套使用defaultdict。语言模型被训练来学习单词在上下文中的概率。我们希望有一个嵌套的数据结构,其中外层键指定上下文(即在二元模型的情况下是前一个单词),内层键指定当前单词。我们希望能够提出这样的问题:“在训练数据中,单词后面跟着单词 cat 出现了多少次?
请注意,内部的defaultdict实际上只是在做与Counter完全相同的事情,所以我们可以用下面的代码行替换上面的代码行,得到相同的结果:
self.d = defaultdict(Counter)
结论
谢谢你读到这里!我希望你在下一个 Python 项目中尝试使用Counter和defaultdict结构。如果你有其他好的用例,请在评论中告诉我!如果您对此感兴趣,请查看我的其他 Python 相关文章:
</7-essential-python-skills-for-research-496e1888e7c2>
撰写您自己的令人惊叹的数据科学博客
给我的数据初学者的建议
帮助你开始旅程的 5 大技巧

源不溅
问题陈述
我对机器学习特别是深度学习感兴趣,也在谷歌工作。当你说你被谷歌采访是因为你在 Medium 上有一个博客时,也许很快我会开始我的 Medium 博客。
如果我可以问的话,你有一些关于如何开始在媒体上写博客的建议吗?再次感谢你接受我的请求,祝你有美好的一天。
—一位富有灵感的数据科学家
2019 年,作为一个新手,开始写博客。我以前是云系统软件工程师,没有机器学习和数据科学方面的经验。但是我还是开始写博客,通过阅读和分享我的知识来学习。
写了 3 年博客后,我觉得写作给了我被动收入、更好的沟通软技能,以及在谷歌宝贵的职业机会。
从那以后,我也收到了大量关于如何开博客和积累专业知识的问题。
总的来说,我想和你分享五个小技巧:
- 沟通:了解你的受众
- 交付:教育和激励
- 节奏:调整自己的节奏
- 共享:识别分发内容的渠道
- 庆祝:产生技能、品牌、收入
沟通:了解你的受众
你的读者是你写作的关键原因。
每一个写作和交流都需要一个听众。如何从一个到另一个取决于谁是观众。
这意味着一篇有如此多方程式和公式的过于技术性的文章可能不为该领域的一些新手所接受。这就是为什么专家会根据不同受众的专业知识水平来识别不同复杂程度的重要概念。
因此,知道你在为谁写作,并据此制定你的成功标准是至关重要的。
一般来说,这些是你写作的关键受众:
- 你自己:分享学习之旅、集思广益和未来有影响力的项目。
- 团队成员/同事:确定协作点、升级和系统特定影响。为同事写作的一个好例子是设计文档,它是指导你的数据项目的概念灯塔。
- 有抱负的/专业的数据从业者:传授相关的技巧和诀窍,以帮助有抱负的数据从业者。比如这个博客就是为了指导你写数据科学博客而创建的。
就我而言,我的数据科学博客的最大受众是 25-35 岁的年轻人,他们勇敢地迈出了涉足数据科学世界的第一步,并积累了数据科学和技术的各种专业知识。
当我 25 岁的时候,我面临着类似的问题。因此,我创建了这个博客,以帮助有类似需求的人在数据科学职业生涯中导航。
同样,你应该知道你在和谁一起建设,以及你为建设你的博客所提供的价值。
交付:教育和激励
教育为可能性铺路,而灵感推动影响
一般来说,写作有两个目标:
- 教育:分享知识,创造可能性,避免陷阱,减少时间沉淀。
- 激发:分享趣闻轶事和故事,煽动观众行动起来。
记住你的读者时间有限。他们来看你的文章是因为你能带给他们的价值。
例如,我给我的读者简单的指示,让他们在 10 分钟内创建一个谷歌数据工作室仪表板。在我的分析工具箱中,我展示了代码片段,供读者快速启动他们的数据科学项目。我还创造了一个关于员工保留的数据驱动故事,意在教育和激励员工。
这意味着,如果你能从你的指令(教育)中节省读者的时间,同时将行动转化为真正的结果(灵感),你就是成功的。
节奏:调整自己
决定你想多久发送一次
决定你发布博客和文章的频率。这可以是每月一次、每两周一次、每周一次,甚至每天一次。
我见过许多新手每天和每周都发布文章,但是过一会儿就停止发布了。对我来说,我每周写一次,但由于其他生活优先事项,我经常拨回每月。重要的不是你写了多少次,而是你将如何按照一个管道或时间表去写。
例如,我的写作管道如下:
- 头脑风暴:思考对我的听众可能有用并且与我的案例相关的话题。例如,当我使用 tmux 时,我写了关于 tmux 的文章,并认为这将帮助其他人提高他们的生产力。
- 自由写作:我把我所知道的关于某个话题的所有观点都写下来,并把它们组织起来,没有任何事先的批评或太多的思考。主要目的是让你的思路不受任何评判地自由驰骋。
- 我进一步在我的文章中加入了更多的想法,这进一步支持了我的观点。
- 回顾:我让我的一两个朋友调查这件事,并给了我反馈。这帮助我发现了我的盲点并修复了它。
- 校对:我修正了关键的语法错误以增加可读性。
- 启动:我在周六或周一启动它,以便在第二天休息时完成管道。最近,我一直在周一发布我的博客,以吸引工作日的读者。

我的总体写作计划(可能因繁忙程度而异)
总之,你可以创建一个写作管道,并随着你生活重心的改变而调整节奏。
就像跑马拉松一样,你不能一直冲刺,相反,你需要调整你的速度并保持你的耐力。暂时停下来关注生活中的其他优先事项是可以的。
共享:确定分发内容的渠道
决定你想如何传授你的知识。
在你喜欢的任何平台上写作。理解你写作的核心价值和目标。
由于我总是在媒体上写作,我想在这里发表一些关于写作的建议。

媒体博客和个人博客写作的利弊
在我看来,你可以从 Medium 开始快速启动,然后一旦你达到一定的势头和读者数量,就在你自己的博客上投入更多的时间。这可以让你平坦的学习曲线,开始更简单,直到你建立你的写作管道和声誉,然后再去主持你自己的网站。
庆祝:创造技能、品牌和收入
写博客如何帮助我成为一名更好的数据科学家并增加我的收入
写作的回报是多方面的:
- 创造共享影响:除了帮助他人,使用你的技能的最佳方式是什么。知识是上帝赐予我们的礼物。分享的越多,知识越丰富。你分享的越多,钱就会用完,但知识永远不会用完。事实上,它在每次分享后都会扩大。
- 建立你的技能:通过分享,你正在建立你的技能。通过教学,你正在学习超越。
- 培养更好的个人品牌:这为合作和职业机会铺平了道路。这就是我如何被谷歌挖走的。
顺便说一下,你也可以创造被动收入:我目前每月收到 500 美元的博客周刊/月刊,偶尔还有顶级作家的奖金。虽然不多,但是写作的收入足以让我在朝九晚五的工作之外靠写数据科学博客生活。
结论
希望一旦你读到这里,你知道如何开始你的旅程。如果你有进一步的想法,请在评论中告诉我。
- 沟通:了解你的受众
- 交付:教育和激励
- 节奏:调整自己的节奏
- 共享:识别分发内容的渠道
- 庆祝:产生技能、品牌、收入
下一步是什么
如果你想了解更多关于我的写作历程,请随时访问我的以下帖子:
还有更多对你有用的职业指导
最后,感谢 Albert Bingei 和 ranon sim 快速阅读和评论本文。反馈很好!
索利·德奥·格洛丽亚
关于作者
文森特用 ML @ Google 对抗网络滥用。文森特使用高级数据分析、机器学习和软件工程来保护 Chrome 和 Gmail 用户。
除了在谷歌的工作,文森特还是佐治亚理工学院计算机科学硕士校友、三项全能运动员和面向数据科学媒体的特约作家,该媒体在全球拥有 100 万以上的观众,为有志的 ML 和数据从业者提供指导。
最后,请通过 LinkedIn , Medium 或 Youtube 频道 联系文森特
编写自己的 C-extension,将 Python 的速度提高 100 倍
如何编写、编译、打包和导入你自己的超高速 C 模块到 Python 中

这就像在你的卡车上安装喷气发动机一样!(图片由 Y S 在 Unsplash 上拍摄)
Python 是一种非常棒的语言,非常容易掌握,开发速度非常快,而且读起来非常清晰。所有这些好处都是有代价的:与其他一些语言相比,Python 相当慢。在继续弄清楚我们正在试图解决的问题之前,我强烈推荐阅读 这篇文章 。本文的目标是回答这个问题:
如何在不牺牲速度的情况下,结合 Python 的易开发性?
讽刺的答案是用另一种语言重写项目,但这不是我们在这里的目的。你是一名 Python 程序员,已经有很多用 Python 编写的程序,并且只想加快其中一小部分的速度。另外:如果你习惯于用 Python 来写,转换到另一种语言如 C#或 Java 可能会很困难。
我们将结合两个世界的精华:我们用一个用 C 编写的小模块来扩展我们的程序。Python 程序员只需导入这个包,不需要知道一行 C 语言,仍然可以享受 100 倍的速度提升。

将 C 模块导入我们的 Python 程序(图片由(Kat sazo novaonUnsplash)提供)
用 C 写?听起来很难
”用 C 写的!?“我听到你问了。
"你刚刚谈到了向 Java 的粗略过渡,现在我们要转向 C 了?!”。的确,用 C 写代码可能有点挑战性,但是你会发现 100 倍的速度提升绝对是值得的!
此外,我们只需用 C 语言重写一小部分代码(在我们的例子中,只是一个函数)。
这和用另一种语言重新写项目不一样吗?
本文描述的解决方案的美妙之处在于,您只需重写代码中较慢的部分。假设我们用 Python 编写了一个 API 来接收和分析音频文件。用 C 重写分析音频文件的函数是有意义的,因为这是项目的瓶颈。我们会浪费很多时间来重写我们的 API。
这个不能做的简单一点吗?
是的,有更简单的方法来创建 C 扩展,这将大大加快我们的程序。在 这篇文章 中,我们使用 Cython 将一些类似 Python 的代码转换成 C 模块,实现了大致相同的性能提升。
然而,在这篇文章中,我们将努力用 C 编写我们自己的模块,因为它让我们对 Python 的内部工作以及它如何集成用 C 编写的模块有了一个非常有趣的了解。
什么时候创建 C 模块有意义?
我们能够优化的任务类型是 CPU 密集型任务,而不是等待响应之类的 I/O 任务。在“更快的语言”中,等待 API 并不会更快。
我们希望优化一小部分执行 CPU 密集型任务的代码。这类任务非常适合在 c 语言中进行优化。

让我们开始工作,加速这个项目(图片由 Damir Kopezhanov 在 Unsplash 上提供)
设置
首先要做的事情是:建立一个虚拟环境。这并不是绝对必要的,但是保持你的依赖关系不混乱是最好的做法。
正如你在前面读到的,我们需要一个做大量计算的函数。我们将使用一个简单的例子:计算一个范围内的素数。下面是实现这一点的普通 Python 代码。
上面的代码看起来有点乱,我听说你认为“ WHILE LOOPS?!旗帜?!”。相信我,他们进去是有原因的。
还要注意,这不是计算质数最有效的方法,但这不是重点:我们只需要一个需要大量计算的函数!
您已经在使用 C 编译的功能
代替 while 循环和标志,我们可以使用内置函数range()。这通过了生成、迭代,并检查我们是否完成了更快的 C 模块。让我们用range()升级那个讨厌的功能:
请注意,这段代码不仅可读性更好,而且速度更快。我们可以使用这两个函数来查找 0 到 100.000 之间的素数:
[Vanilla] examined 100000 numbers; found 9592 primes in 30.38632 sec
[V+range] examined 100000 numbers; found 9592 primes in 20.00026 sec
使用一些内置的 C 模块已经稍微提高了执行速度,但是我们才刚刚开始。

我们必须让我们的手有点脏,但结果将是惊人的(图片由 Adi Goldstein 在 Unsplash 上提供)
为 Python 编写 C 模块
我们要做的第一件事是将寻找素数的函数转换成 C 语言。然后,我们必须让 Python 与 C 语言的函数进行对话。这个问题的解决方案是将 C 函数封装在 Python 模块中。
你对这些已经很熟悉了。想想time、os和sys,例如,我们称我们的模块为Fastcount。
在这一部分的最后,我们将安装我们的模块,以便您可以导入模块并在模块上执行如下方法:
import Fastcount
res = Fastcount.primecounter(0, 100)
print(res)
# 25
我们将分三步完成:
- 用 C 重写求素数函数
- 将 C 函数打包到 Python 模块中
- 构建并安装模块
第一步:C 功能
如果你不熟悉 C 语言,这部分可能有点难。它很像 Python,但有更多的限制。看看这个:
除了这里或那里的一些语法,这个函数看起来很像我们在前一章写的那个讨厌的函数。
2.在模块中包装 C 函数
好,我们有一个用 C 写的函数和一个 Python 文件。我们如何从 Python 文件中访问 C 函数?我们必须采取一些措施:

所有元素如何组合在一起(图片由作者提供)
我们已经在上面定义了我们的 C 函数,所以让我们将 C 函数包装在一个 Python 对象和(黑色)中,并向外展开:
2.1 将 **C-function** 包装成 **Python Object** 。 我们来看一下代码:
记住 Python 中的一切都是对象?事实上 Python 中的一切都是一个PyObject。即使声明 integer 也会导致一个PyObject。在幕后,Python 引擎使用这种结构来支持动态类型。
在上面的代码中,我们将 C 函数包装在一个PyObject中。这个函数使用PyArg_ParseTuple函数解析 Python 发送给我们的参数。ii表示我们期望两个整数(更多信息在这里)。
接下来,我们称之为 C-函数,即发现的素数。最后,在我们把它们转换成一个PyLong之后,我们返回找到的素数;Python 可以解释为类型的对象long。
2.2 将 **Python Object** 增加到一个 **list of methods** 。下面的代码指定了我们在 Python 中调用的函数名:
在这里,我们定义了模块拥有的所有方法的列表。
在这个例子中,我们将primecounter定义为函数名;这就是我们在 Python 中称之为函数的东西。然后我们引用上一步中创建PyObject的函数。METH_VARAGS定义了签名:它期望来自 Python 的self和*args。最后,我们为 docstring 定义了一个方法描述。
如果我们希望我们的模块有更多的功能,我们可以在那里添加更多的对象,但是为了这个演示的目的,我们将保持简单。这个PyMethodDef也需要 3 号线;包含所有NULL的行。
2.3 创建 **Module Definition** 并在上面注册模块名称、描述和 **list of methods** 。 下面是代码:
这段代码定义了我们的模块。创建PyModuleDef需要第一项。在第 4 行和第 5 行,我们指定了模块的名称和描述。
在第 6 行中,我们可以指定存储程序状态所需的内存量。当您的程序在多个子解释器中使用时,这是必需的。
负值表示这个模块不支持子解释器。用一个非负值指定要在每个子解释器会话上分配的模块的内存需求。
第 7 行的最后一项引用了我们在上一步中指定的方法列表。
2.4 创建一个 **Initialization function** ,从 **Module Definition** 创建我们的模块。 最后一块!这是 Python 第一次导入我们的模块时将调用的函数:
我们使用PyModule_Create并传递给它一个对前一部分的PyModuleDef的引用。这将返回一个包装了我们的 C 函数的PyObject。点击查看全部代码。
3.构建、安装和运行扩展
这部分类似于创建您自己的公共或私有 Python 包的过程中的步骤。我们必须创建一个setup.py文件,该文件指向上一步中的 C 代码,然后创建包。我们走吧:
上面的代码非常简单明了;最重要的一行是第 11 行,在这里我们指定了在哪里可以找到我们在步骤 2 中编写的 C 文件。
下一步:简单地给python setup.py install打电话。这将把我们所有的代码打包到一个名为 Fastcount 的模块中。现在在 Python 中,我们可以:
故障排除
Windows:调用python setup.py install可能会得到一个类似如下的错误:
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": [https://visualstudio.microsoft.com/visual-cpp-build-tools](https://visualstudio.microsoft.com/visual-cpp-build-tools)
你可以通过安装 C++构建工具来解决这个问题,你可以在这里下载。

让我们比赛算法(图片由 Jonathan Chng 在 Unsplash 上提供)
标杆管理
让我们测试一下我们的代码;我们想计算 0 到 500.000 之间的质数。为了做到这一点,我们需要检查大约 13 亿个数字;我可怜的笔记本有很多工作要做。我们将进行基准测试:
- 香草蟒
- 普通 Python +内置(比如 range)
- 快速计数(我们的 C 模块)
- 快速计数 MP
在多次执行所有方法并花费最少的时间后,结果就出来了。所有方法都找到了正确的素数(41.538),这是它们花费的时间(越少越好):

查找一个范围内素数的所有方法的执行时间(越低越好,图片由作者提供)
使用像range()这样的内置函数已经节省了大约 35%的完成时间。尽管这是一个相当不错的增加,但我们自己的模块几乎比普通 Python 完成了这个任务。
为了获得更快的速度,我们通过多重处理我们的函数将所有计算分散到多个 CPU 上,与普通 Python 相比,这个函数完成了我们的计算。查看 这篇文章 或 这篇文章 了解更多关于使用线程和进程在 Python 中安全执行多任务的信息。
结论
这是一篇很长很复杂的文章,但是我们学到了很多关于 Python 如何在幕后工作的知识。我们已经编写、编译、打包并导入了我们自己的定制 C 模块。尽管这篇文章相当长,但最终,我们将执行速度从 10 分钟以上降低到了近 6 秒:减少了 99%的执行时间!
如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章:
- Python 为什么这么慢,如何加速
- 【Cython 入门:如何在 Python 中执行>每秒 17 亿次计算
- Python 中的多任务处理:通过同时执行,将程序速度提高 10 倍
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 用 FastAPI 用 5 行代码创建一个快速自动归档、可维护且易于使用的 Python API
- 创建并发布你自己的 Python 包
- 创建您的定制私有 Python 包,您可以从您的 Git 库 PIP 安装该包
- 绝对初学者的虚拟环境——什么是虚拟环境,如何创建虚拟环境(+示例)
- 通过简单的升级大大提高您的数据库插入速度
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
写你自己的朱莉娅
Julia 是一门很棒的语言,但是如果你根据自己的喜好对它进行定制,它会变得更好!

(src =https://pixabay.com/images/id-918449/
介绍
J ulia 是一种编程语言,它带来了各种令人敬畏的可能性。真正让 Julia 语言令人敬畏的一点是,它主要是自己编写的。这意味着大多数 Julia 用户可以解释和重写 Julia 的几乎任何部分,而无需学习任何其他编程语言。既然如此,这意味着我们实际上可以改变朱莉娅做完全不同的事情,并按照我们的想法编程,而不必做任何出格的事情。没有多少其他编程语言可以完成这样的目标,所以 Julia 在这方面确实很酷。今天我想通过一些方法来定制我们的 Julia 安装。这将是新功能的增加,以及改变一些方法来处理不同的类型。
Startup.jl
每次实例化 Julia 时,它都会运行一系列文件,这些文件被加载到新的 Julia 环境中。其中一个很好的例子就是 Base.jl 文件。Julia 库中所有可用的方法和类型都存储在这里,无论何时启动 Julia,它们都会自动存在以供使用。为了改变或增加 Julia 语言已经拥有的功能,您可能有很多原因想要这样做。
Julia 记住,用户可能希望在启动时对他们的环境进行一些更改,并为我们提供了使用 startup.jl 进行更改的能力。在 Linux 和其他类似 Unix 的系统上,这将是~/.julia。julia/config 路径不存在,那么您可以简单地创建它:
shell> cd ~/.julia
/home/emmett/.juliashell> ls
artifacts compiled environments makiegallery pluto_notebooks registries
clones conda logs packages prefs scratchspacesshell> mkdir configshell> cd config
/home/emmett/.julia/configshell> nano startup.jlshell>
改变朱莉娅
现在我们有了 startup.jl,我们可以使用 dispatch 修改 Julia 中的任何代码。我写了一篇文章,详细介绍了我们将要对其他一些类型做些什么,我认为这肯定值得一读。它更详细地介绍了在 Julia 中使用类型和分派,我认为代码很好地展示了 Julia 的全部内容:
我将在其中添加一些代码,根据我的喜好稍微改变一下 Julia。对于这个定制,我决定对加法运算符+进行修改。每当我们通过这个操作符传递两个数组时,我们都会得到一个元素相加的返回。这很奇怪,因为还有另一个操作符,元素相加操作符。+.很容易理解为什么这是他们附带的功能,我想代码应该是这样的:
+(x::Array, y::Array) = .+(x::Array, y::Array)
但这不是我想要它做的。当然,加法运算符不仅用作数字加法的符号,也用于组合。我认为看到这个操作符的另一个用途,组合数组,会很酷。为此,我们只需显式导入操作符,然后添加一行 dispatch。
import Base: +
+(x::Array, y::Array) = [x;y]
现在,这将连接我们的两个数组,而不是元素相加。我将这段代码添加到我的 startup.jl 中,现在在我的本地系统上总是这样!
结论
Julia 的一个非常酷的特性是,不管你知道什么,只要你知道语言本身,你就能在最底层使用它。它本身就是写出来的,真的很酷。我们可以通过简单地使用 dispatch 非常快速地改变不同基础组件的功能,这真的很酷。虽然我的例子非常简单,但是可以用其他类型和方法来做得更好。我认为这甚至对&&位操作符也有意义,我认为它可以产生一些非常棒的结果。感谢你的阅读,我希望这有助于使朱莉娅成为你自己!
py 使用 Python 在 30 分钟内编写一个命令行界面模拟游戏
用一些简单的 Python 代码在几分钟内制作的快速游戏

(图片由作者提供)
介绍
我非常喜欢给计算机编程,并让编程和软件工程变得如此诱人和有益的一点是,你可以通过一路上创建的数据系统,真正释放你的创造力和对问题的思考。我觉得有时候我有很酷的想法来处理问题,而且通常所有这些想法都可以很好地结合在一起,有时候——没那么好。这当然可以用来说明编码前计划的有效性。
也就是说,在最近写一篇关于 Click 模块的文章时,我决定用一个完整的软件来演示这个模块会很有趣,但是我也发现很难想象我到底想做什么。我认为这将是一个有趣的项目,因为这将是一个伟大的点击模块的应用程序,但我也认为这将是非常有趣的编程。

看一下我的 Github 统计就知道了!
多有问题啊,看来我这辈子都没离开过笔记本。然而,本例中使用的语言有点像骗局,因为它是根据文件大小来确定的——Jupyter 笔记本包含各种不同内容的大量数据,它的代码比普通代码多得多,这就是我的观点。也就是说,Python 只占我在 Github 上的文件大小的 0.08%,我认为我应该着手开始一个 Python 项目。该项目的代码可在以下存储库中找到:
https://github.com/emmettgb/characterclash/tree/main
获得基本视觉效果
今天,我们将创建一个简洁的可视化界面,通过带有 ASCII 艺术的 CLI 来查看游戏的输出…这是本文前半部分的 Github 链接。以下是该分支机构的链接:
https://github.com/emmettgb/characterclash/tree/0.0.1-basic-functionality
让我们首先导入我们所有的依赖项,以及我们可能得到的任何新的依赖项:
import click as clk
from numpy import random as rnd
from time import sleep
from os import system, name
现在让我们开始学习一个基础课程,让我们可以开始制作这个的视觉效果:
class PlayGrid:def __init__(self, players):
self.players = players
我们真正需要初始化这个新类的是一个未来的玩家字典,每当我们加载这个游戏时,我们将在我们的主函数中提供这个字典。现在,我将编写一些未来的函数,随着我们逐步编写这些函数,它们将变得更加有用,让我们来看看结果:
class PlayGrid:def __init__(self, players):
self.players = players
draw_grid()
def update(self, message):
passdef move(dir, speed):
pass
def draw_grid():
pass
像往常一样,我要练习提取技术。如果你想更深入地了解这项技术,以及它将如何应用到这个项目的未来代码中,我有一整篇关于它的文章,你可以在这里阅读——它有助于清理你的代码,使它运行得更好!
[## 更多的方法意味着更好的代码
towardsdatascience.com](/more-methods-means-better-code-1d3b237f6cf2)
回到我们的网格,我还将创建一个单独的函数来绘制一个空网格。因为我们在其他地方不需要这个函数,所以我将私下声明它。现在,我不会太关注任何细节,所以我会画一些空白的地方。我将把这些值存储在一个字典中。字典将包含带有整数索引的字符串,类似于我可能用来处理不同玩家数量的玩家类的方法。
无论如何…如果我先写代码,然后再解释,这将会更好,因为我认为这样的组合可能会更合适,并使系统作为一个整体更有意义。
网格
def empty_grid():
self.grid = dict()
str = ""
for row in range(1, 10):
str = ""
for i in range(1, 10):
str += "`"
self.grid[row] = str
return(self.grid)
我添加到这个类的第一个函数是用来创建一个空版本来添加我们的小玩家的函数。我将简单地用“"`字符填充一些字符串。我们将能够通过调用 self.grid 字典来索引我们正在处理的实际行,并且我们将能够使用字典字符串值对来分别按 char 设置索引。我们将在另一个网格函数中调用它:
def draw_grid():
self.empty_grid()
请允许我通过一些简单的交互代码来解释这将会是什么,我将把这些代码写入我们的主函数中。在我们通过调用刚刚编写的 empty_grid()函数清空网格之后,我们现在将有一个新的表面可以查看。让我们继续绘制网格,首先用正则表达式创建一个新的打印字符串,返回 0 并跳过当前行。
def draw_grid():
self.empty_grid()
print_s = "\n"
别名 print_s 是 print string 的缩写。我们之前的数据,以及它未来的变化和一系列的正则表达式将成为我们最终的单行打印语句,只提供一种类型,这非常方便。我们将使用这个函数来测试 empty_grid()函数,方法是迭代地连接字符串,然后打印它们。这是我想到的:
def draw_grid():
empty_grid()
print_s = "\n"
for key in self.grid:
print_s = print_s + self.grid[key] + "\n"
return(print_s)def empty_grid():
self.grid = dict()
str = ""
for row in range(1, 10):
str = ""
for i in range(1, 10):
str += "`"
self.grid[row] = str
这两个函数完美地结合在一起,在视觉上改变了我们的类型。我想说的一件事是,如果这没有意义,或者看起来像我们在随机组件上工作,这是对形势的明智看法。目前,这看起来并不多,但是在编程中最大的障碍总是开始。这是您开始软件流程的地方,事物被抽象地定义,以便它们可以适合另一个组件。在许多情况下,程序员可能会选择先做逻辑,再做视觉。然而,在这种情况下,我认为首先处理视觉效果是很有意义的,这样我们就可以根据屏幕上实际需要发生的事情来设计逻辑。毕竟,这个项目的主要组成部分是视觉效果。不管怎样,现在已经完成了,我们要测试这两个函数,以确保它能正常工作。
布局初始化
现在,我们将花一点时间来关注初始化,以及更新整个打印输出所需的函数。这个更新函数现在将调用网格函数。我还必须创建一个清晰的()函数。
def clear():
if name == 'nt':
_ = system('cls')
else:
_ = system('clear')
这个函数是系统化的,并且是全局定义的,因为它的目的是用这个命令快速清除整个 REPL。它只是为相应的终端类型调用系统的 clear 命令。我们问名字是不是 NT,就像在 Windows NT 中一样,如果是就用 cls。如果不是这样,我们使用 clear,因为在大多数情况下,其他系统将是 Unix-line。下面是更新函数:
def update(self, message):
clear()
grid = self.draw_grid()
print(grid)
print(string("\n", message))
这里发生了几件事,首先,任何先前的输出被清除。之后我们在这个函数的作用域里赋一个变量叫做 grid,这个变量就是 self.draw_grid 的返回。我们不需要在这里调用 return,但是在这种情况下这是很方便的,因为我们根本不希望我们的网格在工作的时候被改变。如果我们为这样的定义使用类作用域,那么当这个函数运行时,它可能会在其他地方发生变化。
我们的 init 函数就是用来总结所有这些的。将有更多的功能添加到这将扩展功能,但目前这是这个项目的核心功能。
def __init__(self, players):
self.players = players
update()
我在这里做的只是把类属性 players 赋给提供的参数 players,然后调用 update。现在,让我们全面看看这个类:
class PlayGrid:def __init__(self, players):
self.players = players
self.update("Hello")def update(self, message):
clear()
grid = self.draw_grid()
print(grid)
print(string("\n", message))def draw_grid(self):
self.empty_grid()
print_s = "\n"
for key in self.grid:
print_s = print_s + self.grid[key] + "\n"
return(print_s)def empty_grid(self):
self.grid = dict()
str = ""
for row in range(1, 10):
str = ""
for i in range(1, 10):
str += "`"
self.grid[row] = str
希望里面的一切都是正确的,但我想我们很快就会知道了。让我们运行这个宝贝:
[emmac@fedora CharacterClash]$ python3 character_clash.py
`````````Traceback (most recent call last):
File "/home/emmac/dev/CharacterClash/character_clash.py", line 48, in <module>
main()
File "/home/emmac/dev/CharacterClash/character_clash.py", line 8, in main
game = PlayGrid(players)
File "/home/emmac/dev/CharacterClash/character_clash.py", line 16, in __init__
self.update("Hello")
File "/home/emmac/dev/CharacterClash/character_clash.py", line 22, in update
print(string("\n", message))
```
> 哎哟
我犯了一个严重的“对不起,我是 Julia 程序员”的错误。我们需要使用加法运算符来连接这些字符串:
```
print("\n" + message)`````````
Hello
```
# 演员
幸运的是,这个游戏是关于输出的—
> 在你的网络浏览器中,看着迷失的人工智能灵魂为你的娱乐而战斗到死。
因此,这些类在某种程度上可以完全随机化。这就是作为旁观者的好处,你不必参与其中。因此,对于这个奇观,我们不需要编程任何类型的输入,这使得这个玩家的过程容易得多。在以后的文章中,我将添加命令行参数,并进一步推进这个小项目。
另一件要注意的事情是,我们需要对网格做一些工作,也许是它未来的内容。我在想,如果我们在网格中添加一些不同的角色,它可能看起来更像随机的地面纹理——也就是说,因为它们不是让核心游戏工作所必需的,也许我会在未来的文章中讨论这个问题。让我们继续用所有的标准初始化材料创建一个玩家类,在此之前,我将为这个类列出一些数据值以供参考,以便为每个单独的类获得不同的统计数据:
```
# Sword # bow # assassin
# Classes = ["o/", "o)", "o-"]
# stats = [speed, damage, range, time]stats_dict = {"o/" : [2, 25, 2, 3],
"o)" : [2, 35, 3, 5],
"o-" : [3, 20, 3, 2]}
```
这是我们的基本类:
```
class Player:
def __init__(self, pos):
pass
```
## 加载数据
接下来,我们将把这些数据加载到这个类型中,还有一些其他的默认数据。
```
class Player:
def __init__(self, pos):
self.pos = []
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(stats_dict.keys())
self.speed = stats_dict[type][1]
```
注意最后两行,首先我得到一个键的数组放入 random.choice 函数,然后产生一个选择,然后我们通过索引该键并从数组中提取值来应用数据。另外,Python 中的索引是从零开始的——所以我刚刚意识到代码中有一个小错误。无论如何,我们将对这些属性中的每一个都这样做:
```
class Player:
def __init__(self, pos):
self.pos = []
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(stats_dict.keys())
self.speed = stats_dict[type][0]
self.damage = stats_dict[type][1]
self.range = stats_dict[type][2]
self.time = stats_dict[type][3]
```
现在所有的数据都已经初始化了,让我们开始移动实际的字符。虽然我们可以使用 vector two 类型,或者类似的东西——也许可以创建我们自己的,但这不是我在这个例子中要做的。我觉得没有必要,因为索引这个位置向量很容易。查看添加了两个新方法头的完整类:
```
stats_dict = {"o/" : [2, 25, 2, 3],
"o)" : [2, 35, 3, 5],
"o-" : [3, 20, 3, 2]}
class Player:
def __init__(self, pos):
self.pos = []
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(stats_dict.keys())
self.speed = stats_dict[type][0]
self.damage = stats_dict[type][1]
self.range = stats_dict[type][2]
self.time = stats_dict[type][3]
self.symbol = type def walk(self, pos):
pass def move(self, players):
pass
```
move(players)函数用于获取玩家数组,并基于此做出某种选择。现在,这一切都将被搁置,因为我们现在实际上要回到我们的旧 PlayGrid 类,然后开始映射这些球员的位置。
# 组合元素
我们需要在我们的 PlayGrid 类中定义一个新函数,以便从前面的网格字典中获取并修改它来包含这些字符。好消息是,我们可以简单地使用索引在适当的位置显示我们的玩家。
```
class PlayGrid:
# Essentials
def __init__(self, players):
self.players = players
self.update("Character Crash Game Started")def update(self, message):
clear()
grid = self.draw_grid()
print(grid)
print("\n" + message)# Player Management
def draw_players(grid):
```
## 绘图播放器
我们的新功能 draw_players(grid)将简单地获取玩家及其各自的索引,然后将玩家角色放在这些索引处,取代之前的位置。我们要做的第一件事是决定向哪个方向绘制字符。换句话说,字符是面向右还是面向左:
```
for player in self.players:
# True = right, False = left
if player.facing == True
modifier = 1
else:
modifier = -1
```
我们需要将这个修饰符添加到一个索引中,以确定该值应该在字符的后面还是前面。我们将在玩家类中处理剩下的部分。我们暂时不会做所有的事情,因为我们有一个函数要写。你可能已经注意到我也打开了一个 for 循环。这是至关重要的,因为我们需要单独调用每个球员的数据来做我们需要用它做的工作。
这背后的方法很简单。在字典中,坐标平面的 y 是键。正如我们在 empty_grid()函数中所做的那样,这些只是由一个范围生成器生成的。然后我们有 x,它是字典的值对。记住,要在一个特定的位置设置一个字符,我们需要用我们的 y 键索引字典,这是我们的 pos 列表中的位置 2,然后我们需要用我们的 x 值索引它的返回,这是我们需要替换的字符在我们的字符串中的位置。
```
# [0] = x, [1] = y
newpos = player.pos
x, y = newpos[0]
grid[y][x] = player.symbol[0]
grid[y][x + modifier] = player.symbol[1]
```
最后,我们将返回网格,正如我之前提到的,我们将不再使用 class 属性,因为我们将在最后更新它。
```
def draw_players(grid):
for player in self.players:
# True = right, False = left
if player.facing == True
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
x, y = newpos[0]
grid[y][x] = player.symbol[0]
grid[y][x + modifier] = player.symbol[1]
return(grid)
```
现在我们已经写好了这个函数,我们要再写一个函数,叫做 make_moves(players)。这个函数将调用我们玩家的移动函数。
```
def make_moves(players):
[player.move(players) for player in players]
return
```
从这个意义上说,移动不像从[x,y]到[x,y],这就是我们 walk()函数的作用。相反,我们的移动功能指定轮到他们做什么。现在,我们将回到我们的球员类,并整理出一个基本的结构,这个东西最初可能会对其环境作出反应。在未来,我将为这个项目实现一个机器学习算法,这将使这个项目变得更酷。
现在,我只想看看一些运动可能是什么样子,记住,我要试着写一个简单的小行走模式:
```
def move(self, players):
if self.blocking == False
self.pos += 1
self.blocking = True
elif self.blocking == True:
self.pos -= 1
self.blocking = Falseself.blocking = False
```
这个基本的小函数只是让我们的玩家在网格上走来走去,让我们稍微包装一下我们的主函数,测试一下数据和显示的关系。
```
def move(self, players):
if self.blocking == False:
self.pos += 1
self.blocking = True
if self.blocking == True:
self.pos -= 1
self.blocking = False# self.blocking = False
```
在不久的将来,唯一保留下来的代码是被注释掉的部分。在未来,这将完全是随机的,直到我在下一篇文章中加入一些人工智能。既然我们在这里,我们不妨添加前面的 facing 属性:
```
class Player:
def __init__(self, pos):
self.pos = []
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(stats_dict.keys())
self.speed = stats_dict[type][0]
self.damage = stats_dict[type][1]
self.range = stats_dict[type][2]
self.time = stats_dict[type][3]
self.symbol = type
self.facing = True
```
# 到目前为止…
到目前为止,我们已经制作了一个玩家网格和将居住在该网格上的玩家。我们需要看看到目前为止所有的代码是否都有效。我们现在需要做的就是稍微调整一下我们的主函数,将一个玩家添加到我们的玩家列表中。另一个随机注意,我也调整了 REPL 打印输出的尺寸。这意味着网格现在比以前大得多。
```
def main():
players = []
game = PlayGrid(players)
game.update("Hello")
# while len(game.players) < 1:
sleep(2)
```
我们需要在玩家列表中添加一个玩家。这相当简单,我们将创建一个新玩家——毕竟,它目前唯一需要的是一个位置。下面是修改后的 main()函数:
```
def main():
players = []
players.append(Player([5, 6]))
game = PlayGrid(players)
game.update("Up")
# while len(game.players) < 1:
sleep(2)
game.update("Down")
sleep(2)
game.update("Up")
sleep(2)
game.update("Down")
```
希望我没记错!
```
[emmac@fedora CharacterClash]$ python3 character_clash.pyFile "/home/emmac/dev/CharacterClash/character_clash.py", line 43, in draw_players
x, y = newpos[0], newpos[1]
IndexError: list index out of range
```
> 让我们看一看…
问题来自这里:
```
class Player:
def __init__(self, pos):
self.pos = []
```
我的意思是提供 pos,然后把它设置成那样,但是它被设置成一个空列表——有趣。
```
File "/home/emmac/dev/CharacterClash/character_clash.py", line 44, in draw_players
grid[y][x] = player.symbol[0]
TypeError: 'str' object does not support item assignment
```
哦,糟糕,看起来我解决这个问题的方法是愚蠢的。这可能比预期的要多一点。幸运的是,有一些很好的方法可以解决这个问题,其中一些我可能在我的 Pythonic 标签处理综合指南中提到过,您可以在这里查看:
</essential-python-string-processing-techniques-aa5be43a4f1f>
> 这次失败的真正原因是,Julia 允许你做任何事情,所以如果我想替换一个字符串索引,我可以导入并扩展索引方法来实现… Julia 太棒了,它毁了我的这个项目。
# 解决我们的问题
为了解决这个问题,我们将通过使它变得非常简单来反抗 Python 之类的东西。我们要做的第一件事是将字符串转换成列表类型。我们知道我们可以设置这种类型的索引,所以我们知道这种方法会有效。然后,我们将使用 str.join()将我们的字符串与新的字符串列表连接起来。
```
>>> list("Hello")
['H', 'e', 'l', 'l', 'o']
>>> "".join(list("Hello"))
'Hello'
>>>
```
让我们回头看看导致这种情况的函数:
```
def draw_players(self, grid):
for player in self.players:
# True = right, False = left
if player.facing == True:
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
x, y = newpos[0], newpos[1]
grid[y][x] = player.symbol[0]
grid[y][x] + modifier] = player.symbol[1]
return(grid)
```
我们将从将网格转换成 for 循环底部的列表开始:
```
newpos = player.pos
current = list(grid[newpos[1]])
```
现在我们有了 current,这是我们当前列的一个字符串,它是通过获取我们的 y 值获得的,y 值是我们的 newpos 列表中的第二个位置(1,不是 2)。
这是最后一个新函数:
```
def draw_players(self, grid):
for player in self.players:
# True = right, False = left
if player.facing == True:
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
current = list(grid[newpos[1])current[newpos[0]] = player.symbol[0]
current [newpos + modifier] = player.symbol[2]
current = "".join(current)
grid[y] = current
return(grid)
```
老实说,这里有很多地方我不得不修改,但这里是对该函数的最后一次检查,它现在工作得非常完美:
```
def draw_players(self, grid):
for player in self.players:
# True = right, False = left
if player.facing == True:
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
current = list(grid[newpos[1]])
current[newpos[0]] = player.symbol[0]
current[newpos[0] + modifier] = player.symbol[1]
current = "".join(current)
self.grid[newpos[1]] = current
```
我也不得不在这里和那里做一些调整,主要是
* 不得不调整播放器的 move()函数,位置正在调用。由于某种原因,它们没有被编入索引。
* 有几个愚蠢的索引错误,还有一些地方我忘了写自己。
* 我必须稍微修改一下主函数,以及 draw_grid()、empty_grid()和 update()函数的返回。
这是我们的新班级:
```
class PlayGrid:
# Essentials
def __init__(self, players):
self.players = players
self.update("Character Clash Game Started")def update(self, message):
clear()
self.empty_grid()
self.draw_players(self.grid)
self.make_moves()
print(self.draw_grid())
print("\n" + message)# Player Management
def draw_players(self, grid):
for player in self.players:
# True = right, False = left
if player.facing == True:
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
current = list(grid[newpos[1]])
current[newpos[0]] = player.symbol[0]
current[newpos[0] + modifier] = player.symbol[1]
current = "".join(current)
self.grid[newpos[1]] = currentdef make_moves(self):
[player.move(self.players) for player in self.players]# Grid
def draw_grid(self):
print_s = "\n"
for key in self.grid:
print_s = print_s + self.grid[key] + "\n"
return(print_s)def empty_grid(self):
self.grid = dict()
str = ""
for row in range(1, 30):
str = ""
for i in range(1, 100):
str += "`"
self.grid[row] = str
```
这是我们新的 main()函数:
```
def main():
players = []
players.append(Player([5, 6]))
game = PlayGrid(players)
game.update("Up")
# while len(game.players) < 1:
sleep(2)
game.update("Down")
sleep(2)
game.update("Up")
sleep(2)
game.update("Down")
```
现在让我们运行它!
```
[emmac@fedora CharacterClash]$ python3 character_clash.py
```

> 还不错!
# 运动/寻路
我们需要克服的下一个大障碍是运动。我们如何让角色决定如何在每一帧上移动?好吧,我们将从在课堂上加入一些新的数据开始,来表明我们周围世界的一些事情。每当我为这个项目编写一些人工智能程序时,这些都会成为我们模型的特征。在我们深入研究之前,我还想提一件事——到目前为止,该项目的代码在核心功能分支中,我们现在将转移到战斗分支。这个分支将会更加专注于移动和战斗,这样我们的新外形将会真正的发挥作用。
这是我们之前工作过的分支的链接:
<https://github.com/emmettgb/characterclash/tree/0.0.1-basic-functionality>
这里有一个链接指向我们现在所在的网站:
<https://github.com/emmettgb/characterclash/tree/0.0.2-combat>
让我们回到移动函数:
```
def move(self, players):
if self.blocking == False:
self.pos[1] -= 1
self.blocking = True
elif self.blocking == True:
self.pos[1] += 1
self.blocking = False
```
如前所述,我们可以删除所有这些代码,除了将 blocking 设置为 false 的第一件事,如果玩家决定阻止,可以在最后将其切换回来。
## 移动()
```
def move(self, players):
self.blocking = False
```
我们需要做的第一件事是评估其他玩家的位置,以及我们作为玩家的状态。这个类可以帮我们做到这一点,所以我添加了更多的属性:
```
class Player:
def __init__(self, pos):
self.pos = pos
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(["o/", "o)", "o-"])
self.speed = stats_dict[type][0]
self.damage = stats_dict[type][1]
self.range = stats_dict[type][2]
self.time = stats_dict[type][3]
self.symbol = type
self.facing = True
self.attacking = False
self.pursuing = None
self.attackavailable = False
self.attacks_available = []
self.turns = 0
```
self.turns 值将在 turn 系统中发挥作用,我们将在此之后为其创建一个经理。一旦我们到了那里,我们将详细讨论这个问题。现在,让我们把重点放在指导这些玩家做什么的功能上:
```
def move(self, players):
self.blocking = False
self.attacking = False
selection = 1
selections = []
param = ""
```
第一件事是初始化一些变量。有很多这样的方法,但有一个很好的理由——这是一种创建一些基于条件的行为的简单方法,但该算法肯定是有意义的,并且有可能被扩展。一旦该说的都说了,该做的都做了,我打算让这个类调用 AI。我还添加了将攻击设置为假,因为如果我们现在移动,我们不能做任何一件事——当我们回顾核心游戏规则和管理系统如何工作时,这可能更有意义。
接下来,我们将进入一个奇怪的迭代循环,它只需要评估事物,以得出三个结论之一,走到某个地方,阻止或攻击某个东西:
```
for player in players:
if player.pos[1] == self.pos[1] and player.pos[0] == self.pos[0]:
pass
else:
if attackavailable == True:
# walk = 1, block = 2, attack = 3
if self.health > 45 and index in attacks_available:
if player.attacking == True:
selection = 3
self.pursuing = index
else:
if player.health > 35 and self.health < 50:
self.pursuing = player.pos
selection = 2
else:
selection = random.choice([1, 2, 3])
selections.append(selection)
```
那里的格式转换很糟糕,但仍然清晰可辨,只是不要把这种缩进当成现实。这个循环也很可怕,而且它出现在主事件循环中有点吓人,但是我怀疑我们会遇到很多这样的问题,此外,这只是我将在以后的文章中做的一些迭代工作的临时占位符。无论如何,接下来我要对选择进行舍入,得到一组选择的平均值。
```
mu = sum(selections) / len(selections)
selection = int(round(mu))
```
最后,我会对每个决策进行函数调用:
```
if selection == 1:
if self.pursuing != None:
self.pursue()
else:
self.random_walk()
if selection == 2:
pass
if selection == 3:
pass
```
这也使得机器学习部分主要只是猜测分类特征,尽管只有一个参数。目前,我们所有可能被调用或实际执行的操作将是 random_walk()方法,这是我刚刚编写的——然而,我实际上并没有添加 pursue()函数,这是一个原因,我想用一秒钟的时间在这里展示,但首先让我们看看 random_walk 函数:
```
def random_walk(self):
# 1 r, 2 l, 3 up, 4, down
dir = random.choice([1, 2, 3, 4])
if dir == 1:
self.pos[0] += self.speed
elif dir == 2:
self.pos[0] -= self.speed
elif dir == 3:
self.pos[1] += self.speed
self.turns = 1
```
这个函数所做的基本上就是选择一个随机的方向行走,然后朝那个方向行进。你可能已经注意到了最后的回合功能,每当我们用主控制器完成这个并运行我们的第一个 REPL 中角色间战争的模拟时,这个功能会更有意义。
## 阻挡/攻击
如果您还记得,前面我说过我没有在这个类中添加 pursue()函数。我这样做的原因是为了测试追求价值的保真度。这是因为无论何时调用该方法,我们都会得到一个错误。然而,我们还需要一个函数来实现这个功能,这个函数就是攻击可用函数。为了开始这个函数,我要写一个和我们之前写的一样的循环。唯一不同的是,这次我想确定一个值是否在攻击范围内,这是我第一次尝试这样的函数:
```
def attack_available(self, players):
for player in players:
if player.pos[0] != self.pos[0] and self.pos[1] != player.pos[1]:
if abs(player.pos[0] - self.pos[0]) <= self.range:
self.attacks_available.append(player.id)
elif abs(player.pos[1] - self.pos[1]) <= self.range:
self.attacks_available.append(player.id)
```
这有些完美,有些不完美。现在,我相信它会很好地为我们服务。现在让我们转到 main()函数,并向我们的输出添加另一个播放器类:
```
def main():
players = []
players.append(Player([5, 6], 1))
players.append(Player([40, 20], 2))
game = PlayGrid(players)
for i in range(1, 25):
sleep(3)
game.update("".join(["Iteration: ", str(i)]))
```
这段代码运行完美。现在让我们稍微润色一下。
## 润色
我决定放弃任何级别的路径寻找,并期待将人工智能放在它的位置上,因为代码相当粗糙,也不是真的需要。所有这一切意味着,就目前而言,这些角色没有遵循任何策略,除了随机性。我想复习一下我做的修饰。首先,我重写了行走函数,包括随机行走和行走函数。
```
def random_walk(self):
# 1 r, 2 l, 3 up, 4, down
dir = random.choice([1, 2, 3, 4])
self.walk(dir)def walk(self, dir):
if dir == 1:
if not self.pos[0] + self.speed >= CHAR_H - 1:
self.pos[0] += self.speed
self.facing = Trueelif dir == 2:
if not self.pos[0] - self.speed <= 2:
self.pos[0] -= self.speed
self.facing = Falseelif dir == 3:
if not self.pos[0] - self.speed <= 2:
self.pos[1] -= self.speed
elif dir == 4:
if not self.pos[0] + self.speed >= CHAR_W - 1:
self.pos[1] += self.speed
self.turns += 1
```
在这个函数中,我还必须添加一个条件来确保字符不会离开边缘。最后,我更新了主函数,它现在将运行 50 步棋,假设所有 50 步棋都有效,那么这应该是一个工作项目!这是最后一次查看这些类和 main()函数:
```
def main():
players = []
players.append(Player([11, 20], 0))
players.append(Player([5, 6], 1))
players.append(Player([20, 10], 2))
game = PlayGrid(players)
for i in range(1, 50):
sleep(.5)
game.update("".join(["Iteration: ", str(i)]))class PlayGrid:
# Essentials
def __init__(self, players):
self.players = players
self.update("Character Clash Game Started")def update(self, message):
clear()
self.empty_grid()
self.draw_players(self.grid)
self.make_moves()
print(self.draw_grid())
print("\n" + message)# Player Management
def draw_players(self, grid):
for player in self.players:
modifier = 0
# True = right, False = left
if player.facing == True:
modifier = 1
else:
modifer = -1
# [0] = x, [1] = y
newpos = player.pos
current = list(grid[newpos[1]])
current[newpos[0]] = player.symbol[0]
current[newpos[0] + modifier] = player.symbol[1]
current = "".join(current)
self.grid[newpos[1]] = currentdef make_moves(self):
[player.move(self.players) for player in self.players]# Grid
def draw_grid(self):
print_s = "\n"
for key in self.grid:
print_s = print_s + self.grid[key] + "\n"
return(print_s)def empty_grid(self):
self.grid = dict()
str = ""
for row in range(1, CHAR_W):
str = ""
for i in range(1, CHAR_H):
str += "`"
self.grid[row] = str# Sword # bow # assassin
# Classes = ["o/", "o)", "o-"]
# stats = [speed, damage, range, time]stats_dict = {"o/" : [2, 25, 2, 3],
"o)" : [2, 35, 3, 4],
"o-" : [3, 20, 1, 2]}
class Player:
def __init__(self, pos, id):
self.id = id
self.pos = pos
self.health = 100
self.blocking = True
self.attacking = False
type = random.choice(["o/", "o)", "o-"])
self.speed = stats_dict[type][0]
self.damage = stats_dict[type][1]
self.range = stats_dict[type][2]
self.time = stats_dict[type][3]
self.symbol = type
self.facing = True
self.attacking = False
self.pursuing = None
self.attackavailable = False
self.attacks_available = []
self.turns = 0
# Base
def random_walk(self):
# 1 r, 2 l, 3 up, 4, down
dir = random.choice([1, 2, 3, 4])
self.walk(dir)def walk(self, dir):
if dir == 1:
if not self.pos[0] + self.speed >= CHAR_H - 1:
self.pos[0] += self.speed
self.facing = Trueelif dir == 2:
if not self.pos[0] - self.speed <= 2:
self.pos[0] -= self.speed
self.facing = Falseelif dir == 3:
if not self.pos[0] - self.speed <= 2:
self.pos[1] -= self.speed
elif dir == 4:
if not self.pos[0] + self.speed >= CHAR_W - 1:
self.pos[1] += self.speed
self.turns += 1# Behaviors
def attack_available(self, players):
for player in players:
if player.pos[0] != self.pos[0] and self.pos[1] != player.pos[1]:
if abs(player.pos[0] - self.pos[0]) <= self.range:
self.attacks_available.append(player.id)
elif abs(player.pos[1] - self.pos[1]) <= self.range:
self.attacks_available.append(player.id)def move(self, players):
self.attacks_available = []
self.blocking = False
self.attacking = False
self.attack_available(players)
selection = 1
selections = [1, 1, 1, 1, 2, 2]
if len(self.attacks_available) > 0:
selections.append(3)
selection = random.choice(selections)
if selection == 1:
if self.pursuing != None:
self.pursue()
else:
self.random_walk()
if selection == 2:
self.blocking = True
self.turns += 1
if selection == 3:
self.attacking = True
self.call_attack()
def call_attack(self):
pass
def pursue(self):
pass
```
# 结论
我发现这个项目非常有趣和令人兴奋,我希望那些阅读的人也一样。我想通过构建这个软件来展示这么多随机的很酷的东西,但总的来说,我认为做这样的事情然后交流它们只是娱乐性的。这个项目肯定是一个了不起的项目。
当我们继续这部分的工作时,这段代码所需要的只是一些攻击代码,以及一个运行这些攻击的管理器,以及其他与游戏逻辑规则相比较的东西。非常感谢你的阅读,我希望这个项目对你来说是尽可能愉快的,我希望你对我将要把它进行到的长度感到兴奋!祝你有美好的一天!
> 还有一件事,这是我们创作的 GIF:

# 在 Go 中编写有限状态机
> 原文:<https://towardsdatascience.com/writing-a-finite-state-machine-in-go-e5535e89d615?source=collection_archive---------8----------------------->

作者图片
经过漫长的时间,我带着一篇新的博文回来了。抱歉,无线电中断了。我的生活中有太多的事情要做,尽管我在此期间做了一些非常酷的事情(例如使用一些传统的 ML 和图像处理技术在特定领域以很大的优势击败了所谓的艺术级深度学习算法),但我没有时间写任何东西。
我最近在工作中从事一个非常有趣的项目,涉及机器学习、代码翻译和其他一些很酷的东西。项目的一部分正在用 Go 开发,一部分在用 Python 开发。多亏了它,几年后我有机会回去写一些 Go 代码。
我希望拥有的东西之一是一个**有限状态机**。

作者图片
直观地说,有限状态机(从现在开始是 FSM)只是一个有向图,其中每个节点代表一个状态,从一个状态(节点)你可以访问另一个节点(状态),主要基于两件事
* 这两个节点之间必须有一条边(如果你愿意,可以用箭头表示)(这条边实际上是显而易见的,但最好还是明确表示出来)
* 每条边代表某种动作/规则,当你处于一个状态时,只要你接收到的输入(我们称之为事件)与连接这两个状态的规则相匹配,你就可以进入任何其他连接的状态。
简单来说,请看一下我用作文章顶部图像的状态机。为了简单起见,我再次把它放在这里。

作者图片
在这里,您从`locked`状态开始。这通常被称为初始状态。在那里,您有两个可用的操作。一个是**投币**,另一个是**推**。现在,如您在图像中所见,如果操作是投币,那么您将状态更改为**未锁定**。但是如果动作是推,那么你又回到相同的状态,即锁定。
FSM 是计算的数学模型。这意味着你可以用有限状态机来表达大量的计算任务。它不如图灵机(另一种计算模型)或下推自动机强大,但已经相当强大了。

作者图片
是的,好的!:)我要展示代码了。
在我开始之前,我只想声明,我的代码有一个外部依赖,`gonum/graph`你可以在这里看到完整的文档[。](https://pkg.go.dev/gonum.org/v1/gonum@v0.9.3/graph)
此外,我必须提到,已经有一些很好的 FSM 实现。比如来自 [looplab](https://github.com/looplab/fsm) 的那个。但我想推出自己的产品,以提高灵活性和学习性。
让我们从进口开始。
这个相当简单。让我们继续看第一个街区。我们在这里定义了 FSM 的主干。这意味着我们将定义一个简单的节点(状态)和边(链接)结构。
在 gonum 中,只要实现了`ID`函数,你就可以让任何结构成为一个节点。此外,我们将使用有向多图。对于这种类型的图,gonum 称边为线。所以我们也定义了我们的生产线。
我们的节点结构有一个值字段,它是一个`interface`,这意味着它可以包含许多不同类型的值。
我们的 Link struct 有一个 Rules 字段,它是从一个`Operator`到一个`Event`的映射,这两个是定制的数据类型。我很快会给他们看的。但是为了不让你感到困惑,我现在要告诉你,他们只是`string`类型。
现在让我们定义一下`StateMachine`
这很简单。
现在,我们将继续定义一些重要的函数。第一个是创建新的状态机。
做完这些,我们还需要几个。让我们定义一个函数来初始化 FSM,一个函数向它添加更多的状态,另一个函数用一个链接来链接两个状态(它也包含需要满足的规则,以便可以遍历它。)
很简单。我们还将创建一个函数来创建一个新规则。
这些都很重要,然而,它们在某种意义上可以被称为`boiler-plate`。现在,我们将定义 FSM 的核心。我们将定义一个名为`FireEvent`的函数,它将触发一个事件,并基于触发的事件、FSM 所在的当前状态以及从该状态发出的链接,它将调用是否应该将状态更改为新的状态。
因此,在这个函数的开始,我们得到了 FSM 当前所处的状态。一旦我们获得了它,我们就获得了一个迭代器,用于所有连接到它的状态(因此有了`From`函数调用)。一旦我们得到迭代器,我们迭代所有这些节点。
对于那里的每个状态,在输出意义上连接到`PresentNode` ,我们获得连接它们两者的链接。
我们获取该链接的规则,并将规则的值与事件的值进行比较。
举个例子,假设一个链接`l`有这个规则`eq:coin`,当前事件是`coin.`,这意味着状态接收到的事件等同于该规则。这意味着我们可以把这个链接,移动到这个链接结束的状态。
在这里,我想花一点时间来解释为什么我引入了一个叫做`Operator.`的东西。我的想法很简单。我们将有一组预定义的操作符,如`eq`、`neq`、`gt`、`lt`、`gte`、`lte`等。许多人可能对它们很熟悉。他们=,!=,>,<。≥,≤分别为。通过在我们的状态机中引入这个概念,它可以表达的规则变得非常大。所以我们可以用这个来模拟大量的过程。(我的秘密野心是引入概率算子,把 FSM 变成非确定性 FSM:)。当然,出于演示和本文的目的,我只展示了一个操作符:`eq`
在这个阶段,我们基本上完成了。然而,我将定义一个名为`Compute`的小函数,它获取一部分事件,并计算 FSM 在事件链末端的位置。
我们完了!😅
虽然看起来很多,实际上我们到目前为止写的代码非常简单易懂。它也非常简洁。
现在是时候让状态机转一转了。
当我们运行这段代码时,结果如下
```
$ go run main.go ✔
Initial state is ------- locked
unlocked
locked
------------ Final state is locked
```
这正是我们所期望的!
这种实现的一些优点如下
* 状态值可以是任何值。
* 规则可以是任何东西,只要它涉及支持的操作符
* 我们实际上可以从任何地方读取一系列事件,用户提示,网络流量,csv 文件…你能想到的。
* 高度可扩展和可定制(没有 main,代码少于 150 行)
* 它依赖于一个非常健壮的库`gonum`,这个库带来了强大的功能和可靠性。
这就是所有的人。如果你喜欢这篇文章,请鼓掌分享。给了我以后写其他文章的灵感。你也可以在 [Linkedin](https://www.linkedin.com/in/shubhadeep-roychowdhury/) 和我联系。
和往常一样,该代码是开源的,可以以您想要的任何方式使用。完整的代码可以在这个 github 链接找到—[https://gist . github . com/rcshubhadeep/1606 c 7 F3 ee 4606 EDB 194 a 30 c 04811 bb 2](https://gist.github.com/rcshubhadeep/1606c7f3ee4606edb194a30c04811bb2)
希望你喜欢这篇文章,并希望很快再次见到你。
# 用深度学习写音乐专辑
> 原文:<https://towardsdatascience.com/writing-a-music-album-with-deep-learning-4ee3bd2e9b05?source=collection_archive---------19----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 我使用深度学习生成架构创建了一个环境音乐 EP

[斯蒂夫·约翰森](https://unsplash.com/@steve_j?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/fender-rhodes-piano?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍照
我一直相信音乐和技术领域允许一个对双方都有利的反馈生态系统。一方面,技术为音乐家提供了新的声音,与他们的作品互动的界面,他们以前没有探索过的作曲方式。另一方面,由这些新技术创造的音乐本身,激发人类的思维创造新的工具,以便与这些新的创作互动。
在我的声音和音乐计算硕士课程中,我有机会了解和探索各种用于音乐生成的深度学习架构。从可变的自动编码器,它在符号表示的音乐生成的频谱中操作,到在波形表示上操作的更复杂的系统。
有机会探索这些网络,用我收集的音频从头开始训练它们,并能够创作新的音乐,这让我非常兴奋。这是因为,作为一名作曲家、音乐制作人,同时也是一名软件工程师,我开启了无尽的表达可能性,这在限制我的创造力的同时也推动了我的创造力。
因此,在这篇文章中,我打算分享和反思我在计算机音乐创作课程的期末项目开发过程中的创作过程。
# 探索深度学习架构
在项目开始时,我不太清楚自己想做什么。最终项目的规格主要由我们决定,但他们必须至少利用一些在课堂上学到的不同技术和资源。这些可以是:为效果创建界面;音乐效果的创造;为音乐生成训练某种算法或深度学习架构;将已编译的工具用于创作目的;通过与 Python 的交互在 PureData 中创建补丁;在许多其他选项中。
由于这项任务的自由性质,在第一个例子中,我让自己完全被我的好奇心所驱使。我想知道那里有什么。有一点对我来说很清楚:我想探索算法和深度学习架构,以产生独特的音乐、声音和纹理。所以,我遇到的第一个架构是 [WaveNet](https://arxiv.org/pdf/1609.03499.pdf) 。
Wavenet 是一种生成波形架构,主要通过卷积神经网络工作。它是由总部位于伦敦的人工智能公司 DeepMind 在 2016 年创建的。WaveNet 能够通过使用直接在波形域上操作的卷积滤波器来逐个样本地生成音频样本。

从波网中存在的卷积层生成新样本。摘自 A. et .范登欧德。艾尔。:WaveNet:原始音频的生成模型。2016.在 https://arxiv.org/pdf/1609.03499.pdf[有售](https://arxiv.org/pdf/1609.03499.pdf)
WaveNet 主要用于语音合成和文本到语音的任务。这就是为什么你能找到的大多数开源实现都是基于对 [VCTK 数据集](https://datashare.ed.ac.uk/handle/10283/3443)或类似数据集的训练。VCTK 数据集是一组说英语的人的录音。110 名操不同口音的英语者在一个消声室中背诵不同的短语。
我尝试了一些 WaveNet 实现,但是给我最好结果的是这个。我的第一个实验是训练它“唱歌”。带着这个目标,我使用了 [MUSDB18 数据集](https://sigsep.github.io/datasets/musdb.html)中的声带。MUSDB18 是专门用于源分离任务的数据集。它由 144 首专业制作的歌曲组成,其中的音轨被简化为 4 个混合音轨:人声、鼓声、低音、其他(其他一切)+最终的混合音轨。在这些音轨上训练了 wavenet 之后,我得到了第一个生成的样本:
MUSDB18 数据集中 vocals 文件经过 200,000 个时期后的 Wavenet 实施示例。
正如我们所听到的,WaveNet 能够学习一些非常有特色的声音元素,比如一些辅音。最突出的是“s”音,可以在曲目的后半段听到。
老实说,对于第一次尝试,我对生成的音轨不是很满意,因为它不可能欣赏演唱的人声的音高或其他音乐特征。出于这个原因,我继续寻找可以用于音乐生成的深度学习架构的其他实现。这就是我如何遇到[Google Magenta](https://magenta.tensorflow.org/):*一个开源研究项目,探索机器学习作为一种工具在创作过程中的作用。*
那一刻,我觉得自己就像一个在玩新玩具的孩子。Magenta 有大约 20 个基于深度学习的最先进的模型,可用于各种音乐目的;鼓的人性化;钢琴声部的产生;旋律的延续;以旋律为条件的和弦伴奏;基于变分自动编码器的测度间插值:除其他外。除此之外,它还有其他模型来执行任务,如图像风格化和生成矢量化的类似草图的绘图。它为创作者开启了无限可能!
> 在 Ableton 中,一些用于音乐创作的 Magenta 模型被实现为独立的应用程序和 Max for Live 补丁。我强烈推荐[下载并探索它们](https://magenta.tensorflow.org/studio/)。
最引起我注意的两款是 [PerformanceRNN](https://magenta.tensorflow.org/performance-rnn) 和 [Piano Transformer](https://magenta.tensorflow.org/piano-transformer) 。第一个是基于 LSTM 的递归神经网络,旨在通过一系列 MIDI 事件(具有学习的开始、持续时间、速度和音高)来模拟具有表现力的时序和动态的复调音乐。另一方面,钢琴转换器是一种自回归模型,能够生成学习长期结构的富有表现力的钢琴演奏。钢琴变形金刚由 YouTube 用户直接上传的大约 10.000 小时的钢琴演奏进行训练,并使用谷歌的 [Wave2Midi2Wave](https://magenta.tensorflow.org/maestro-wave2midi2wave) 转换为 MIDI。遗憾的是,与 PerformanceRNN 不同的是,Piano Transformer 模型还没有向社区发布,我们只能在这个 [Colab 笔记本](https://colab.research.google.com/notebooks/magenta/piano_transformer/piano_transformer.ipynb)中使用预先训练好的模型。
> 如果你也想尝试一下性能 RNN,Magenta 团队已经发布了一个 [Colab 笔记本](https://colab.research.google.com/notebooks/magenta/performance_rnn/performance_rnn.ipynb),你可以在[雅马哈电子钢琴比赛数据集](https://www.piano-e-competition.com/)上与预先训练的模型一起玩:大约 1400 场专业钢琴家的 MIDI 表演。
# 动机
在尝试了 WaveNet、PerformanceRNN 和 Piano Transformer 之后,我准备开始创作音乐了。通过我所做的实验,特别是 PerformanceRNN,我确信我想要*保留机器的声音*。我的意思是,不管一个或另一个模型是用什么数据集训练的,当生成一个样本时,你会得到一个独特的作品,它的作者是分散的。一方面,我们有所有的艺术家,他们是训练网络的数据集的一部分。我们还让所有的研究人员和开发人员参与架构和模型的实现。最后,作为这个复杂系统的用户,我要对训练集、生成样本的持续时间以及一段音乐的后续安排和创作做出决定。
我做这个练习的目的纯粹是出于好奇。这是一个探索性的过程,让我能够聆听这个跨学科团队创作的织体和作品,这个团队不同意合作,但仍然发生了。这样,我想通过 EP 的创作来探索这些新形式的音乐创作给我们提供的不同空间。当一个人获得从未听过的纹理和构图时,创造力就会爆发,但与此同时,这与一种伦理冲突,这种伦理建议对“编曲者”或我的空间采取某些限制,我是一个决策者,决定哪些东西不动,以保留系统中所有演员的声音,哪些东西要修改。
这样,我挑战自己创作了 3 首歌。这张 EP 将带听众踏上一段旅程,关于我自己的创造力的限制,为了网络的创造力和它背后的所有人的创造力。
# 第一首:萨蒂诺
在与声音艺术家 Nico Rosenberg 谈论这些技术时,他建议去看看布赖恩·伊诺和埃里克·萨蒂的作品。布赖恩·伊诺被认为是环境音乐之父之一。另一方面,埃里克·萨蒂是二十世纪的作曲家和钢琴家。他称他的流派为家具音乐,因为他创作音乐不是为了坐下来听,而是为了“装饰”空间。
> 埃里克·萨蒂甚至要求他的音乐会上的观众互相交谈,以强调他的音乐的家具功能。
通过增加历元数训练的性能 RNN 网络示例。最后一个是一个非常过分的版本。
我汇编了一套埃里克·萨蒂作品的 MIDI 转录集来训练演奏者。大约过了 16000 个纪元,网络学会了高表现力地弹琴。有时,也许是因为我训练这个系统的作品数量很少,可以听到《裸地 1 号》中一些可辨认的部分。由于这个原因,我对温度参数进行了调整,以使网络在生成时有一定的自由度。
我最终选择了一首由系统生成的特别有感情的曲子,并决定将它作为第一首歌的主旋律。
为 PerformanceRNN 生成的第一首曲目选择样本 RNN
钢琴曲目很棒,但不足以创作一首歌。我的意图是能够将 PerformanceRNN 生成的旋律与直接生成波形的网络生成的其他一些音频样本混合在一起。这就是为什么我回到 WaveNet,但这次我用了近 2 个小时的布赖恩·伊诺音乐来训练它。我用了专辑《机场音乐》( T1)和《T2 》( Ascent)一个 1 小时的加长版结尾。
> 两张专辑都是从 YouTube 上获得的,数据集被用*分割成 16 秒长的小块。/download-from-youtube.sh* 由 SampleRNN 提供的脚本由 Deepsound 实现:[https://github.com/deepsound-project/samplernn-pytorch](https://github.com/deepsound-project/samplernn-pytorch)
123,000 个纪元后,结果令人惊讶:
为 WaveNet 生成的第一个音轨选择样本
有了 WaveNet 生成的这个环境音频文件和 PerformanceRNN 生成的钢琴音轨,我去了 Ableton 开始创作第一首歌。
我想限制我作为编曲者的角色。通过这种方式,做出的决定试图尽可能地保持 WaveNet 和 PerformanceRNN 生成的样本不变。总之,在这两个音频上制作音轨的主要安排是:
* 从 WaveNet 产生的音频中剔除不和谐的和弦。
* 使用 Ableton 的音阶对象,使得由 PerformanceRNN 生成的 MIDI 钢琴旋律所演奏的所有音符都处于由 WaveNet: D 大调生成的旋律的相同主调中。
* 我复制了钢琴旋律的最低音符,为歌曲的某些部分生成了大提琴伴奏。
* 我复制了一些钢琴旋律中比较有特色的音符来创作编钟。
* wavenet 生成的音频是复制的。第一个副本保持不变,我把它一直摇到最左边。第二个副本被分成两半,并颠倒了两个片段,因此后半部分在开头,前半部分在结尾,平移到右边。这个音轨是基本的环境声音。
* 我再次复制了环境轨道,并将音高上移了一个音阶,以获得高音信息(WaveNet 在 16kHz 采样,因此在 8kHz 上没有信息)。
* 为主旋律达到恒定节拍的部分增加了鼓轨。
* 添加了一个声码器轨道,带有钢琴旋律的低音和弦和 WaveNet 生成的环境。
* 在歌曲的两个特定部分添加了电吉他。
# 第二轨道:响应
对于第二首歌,我的意图是我作为编曲者的决定将比第一首歌少得多,但同时应该有一个连贯的故事和声音设计。因此,我使用 Piano Transformer 模型来生成第一首歌曲中使用的钢琴音轨的延续。我认为这特别有趣,因为这意味着两种不同架构的交互,因此是原始的变体,但现在采用雅马哈数据集作为合成风格。
结果也很显著,但与萨蒂训练有素的演奏样本不同,这首曲子没有容易识别的节拍,这使它成为一首更具氛围的钢琴独奏作品的理想选择。
然而,对于这首曲子,我不想抛开布赖恩·伊诺的影响,所以我从 WaveNet 上取了另一个样本,但这次我对它进行了数量减少的时期训练,以使这首曲子更具原始气息。生成的音频比第一首歌曲中使用的音频具有更恒定的音高。然而,人们可以清楚地听到信号的失真,因为如果你不给 WaveNet 一个预先的种子,音频是从白噪声中雕刻出来的。我认为音频中的爆音和滴答声给了这首歌一种复古的黑胶风格,并给这首歌增添了许多特色。
对于这件作品,我的干预更加有限:
* 我没有接触 WaveNet 生成的音频,我只是复制了它,以创建与上一首曲目相同的立体声图像。
* 我再次在钢琴变形器生成的钢琴曲上使用了 Ableton 的音阶对象,这次使用的是大调。
* 我复制并颠倒了钢琴音轨。加了很多混响又反转了一遍。这创造了一个非常梦幻的声音设计。
* 大提琴的旋律来自钢琴轨道的最低音符,做了一些调整以避免不和谐。
* 加了一些铃铛来强调一些音符。
# 最后一首歌:罗德斯不喜欢流行音乐
对于最后一首歌,我想尽可能少地介入。在这里,我想尝试一些不同于 Satie + Eno 的东西。我想要一个 100%在波形域中生成的音频,这将允许我探索新的声音设计。在我的探索中,我试图训练 SampleRNN 的两种不同实现: [1](https://github.com/deepsound-project/samplernn-pytorch) 和 [2](https://github.com/Unisound/SampleRNN) 。除了损坏我耳机的音频爆音外,他们都没有给我结果。存储库已经很多年没有维护了,我找不到一个能够给出我期望的结果的分支。
我还尝试了 OpenAI 的点唱机。我找到了一个 [Colab](https://colab.research.google.com/github/openai/jukebox/blob/master/jukebox/Interacting_with_Jukebox.ipynb) 笔记本来测试从自动点唱机中按照流派、艺术家和歌词进行的采样。结果令人惊讶:
然而,我想自己训练网络,用我觉得有趣的音乐和片段。所以最后我回到 WaveNet,在平行的两个网络中训练。第一个用了[大约 10 个小时的 DubStep 音乐](https://www.youtube.com/watch?v=AvxBRHM9U9k)。
WaveNet 训练了超过 10 个小时的 DubStep,共 131350 步。
WaveNet 训练了超过 10 个小时的 DubStep,共 519900 步。
有趣的是,WaveNet 是如何设法学习小军鼓和鼓鼓的声音,以及这种特殊音乐类型中属于中断的许多噪音的。然而,结果是迄今为止从概念上从其他两个轨道,我决定不与此样本工作。
WaveNet 在 6 小时的史诗音乐上训练了超过 999999 步。
第二次尝试是使用 [6 小时的“史诗”音乐](https://www.youtube.com/watch?v=yR2ElHDhPVo)。这个结果让我很沮丧,因为我以为人声、管弦乐或弦乐会占据更大的优势。然而,这最后两个实验让我意识到,当数据集的音色特征在文件之间相似时,至少这种 WaveNet 实现学习得更好。这就是为什么有 WaveNet 实现,如 [WaveNet Tacotron2](https://github.com/Rayhane-mamah/Tacotron-2) ,它允许根据 VCTK 数据集中的说话者 ID 进行文本到语音合成。换句话说,为了获得更好的结果,训练网络的音频段必须共享声学特征。
这就是我最后一次尝试的过程。在这方面训练网络:
小笠原郁美在她的罗德斯演奏流行歌曲。
在我看来,用罗兹的表演来训练 WaveNet,这是第一首歌的主要元素,加上小笠原郁美对著名流行歌曲的出色诠释,可以取得良好的效果。不过, *WaveNet 好像不喜欢 Pop* ,更喜欢具象的音乐。对于这最后一个轨道,唯一的安排是降低主要有噪声的部分的增益。
# 结果
所有的音轨都是我混音的,所以混音过程的一部分包括了诸如压缩器、均衡器、延迟、混响和一些自动化的效果。最终专辑可以在下面听到。
罗兹不喜欢流行音乐。EP。
# 奖金
此外,只是为了好玩,我用梦剧场的歌曲数据集来训练 PerformanceRNN。这种过度拟合的模型创造了一些众所周知的作品的变化,如《泪之小径》和《迷失不能忘记》的介绍。
# 用 HTML 和 ML5.js 为初学者编写一个图像分类程序
> 原文:<https://towardsdatascience.com/writing-an-image-classification-program-for-beginners-using-html-and-ml5-js-13c8dc000aed?source=collection_archive---------22----------------------->
## 数据科学家
## HTML、Javascript 和库

照片由 [Manja Vitolic](https://unsplash.com/@madhatterzone?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
如你所知,机器学习有潜力影响每个行业。今天,我将指导您使用 ml5.js 和 p5.js 在浏览器中编写第一个机器学习程序。它使您能够使用 TensorFlow.js 迈出第一步。
# 你需要知道的是
以下所有知识都是新手级别所需要的。
* 超文本标记语言
* 半铸钢ˌ钢性铸铁(Cast Semi-Steel)
* Java Script 语言
# 这个程序怎么运行?
您可以通过点击`index.html` 文件或使用下面的`Python`命令来运行该程序。
```
python3 -m http.server 8081
```
在浏览器 URL 中,键入以下内容。
```
localhost:8081
```
# 编写一个图像分类程序
首先我写一个静态的 HTML 页面,有基本的内容。
* *index.html*
```
<!-- There are not any stylesheets and scripts here --><html>
<head>
</script>
<meta charset="UTF-8">
<title>Image Classification program for Beginners</title>
</head>
<body>
...
</body>
</html>
```
然后,我需要上传一个稍后将被分类的图像并显示它。因此,我添加了一个上传按钮和 180x180 大小的占位符图像。
* *index.html*
```
<html>
<head>
...
<head><body>
<input type='file' id='getval'/>
<img id="image" src="http://placehold.it/180" alt="the image"/>
</body>
</html>
```
接下来,我编写一个脚本来获取上传的图像并显示它。
* *myscript.js*
```
const image = document.getElementById('image');
document.getElementById('getval').addEventListener('change', readURL, true);
function readURL() {
var file = document.getElementById("getval").files[0];
if (file) {
var reader = new FileReader();
reader.onload = function (e) {
image
.src = e.target.result;
};
reader.readAsDataURL(file);
}
}
```
然后,我在 HTML 页面的 body 标签中加入了`myscript.js`。
```
<body>
...
<script src="myscript.js"></script>
...
</body>
```
试图测试我的程序。它运行良好。

然后,我将使用 ML5.js 库对我的图像进行分类。
## ml5.js 库是什么?
`ml5.js`图书馆创建于纽约大学,2018 年 7 月公开发布。它可以访问程序中的人工智能方法和模型,在`TensorFlow.js`上扩展,没有其他外部依赖性。
* 我将`ml5.js`库添加到 HTML 页面中。
```
<head>
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
</head>
```
* 我更新了`myscript.js`文件来处理上传的图片。
```
const result = document.getElementById('result');
const probability = document.getElementById('probability');
ml5.imageClassifier('MobileNet')
.then(classifier => classifier.classify(image))
.then(results => {
result.innerText = results[0].label;
probability.innerText = results[0].confidence.toFixed(4);
});
```
* 显示了分类器算法的结果。
```
<!-- There are not any stylesheets and scripts here. -->
<html>
<head>
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
<link rel="stylesheet" href="mystyle.css">
<meta charset="UTF-8">
<title>Image Classification program for Beginners</title>
</head>
<body>
<!-- <h1>Image classification using MobileNet model</h1> -->
<p>That's
<span id="result">... </span> with a confidence of
<span id="probability">...</span>.
</p>
</body>
```
最后,我用一个 CSS 文件来设计我的程序。
* *mystyle.css*
```
img{
max-width:180px;
}
input[type=file]{
padding:10px;
background:#ffffff;
}
```
这是最终的结果。

# 结论
在这篇文章中,你学习了如何使用`ml5.js`来在浏览器上构建和部署一个简单的机器学习应用程序。这个新的和改进的库已经在许多应用案例中证明了自己的有效性。
很简单,对吧?
# 参考
[1][https://ml5js.org](https://ml5js.org)
[2][https://github . com/house cricket/Beginning-Machine-Learning-in-the-Browser/tree/master/Image-class ification-using-MobileNet-model](https://github.com/housecricket/Beginning-Machine-Learning-in-the-Browser/tree/master/Image-classification-using-MobileNet-model)
# 通过测试编写更好的代码
> 原文:<https://towardsdatascience.com/writing-better-code-through-testing-f3150abec6ca?source=collection_archive---------24----------------------->
## [入门](https://towardsdatascience.com/tagged/getting-started)
## pytest 库的快速概述

来源( [Unsplash](https://unsplash.com/) )
当处理生产级代码时,测试变得和代码本身一样重要。如果你曾经参与过任何著名的软件项目,你会知道即使是很小的变化也需要你写测试来确保你的代码不会崩溃。在另一篇[文章](/the-importance-of-testing-your-python-code-9b1262d009dc)中,我们研究了使用 unittest 库测试代码的基础知识。这里我们将看看另一个广泛使用的 Python 测试库, [pytest](https://docs.pytest.org/en/stable/) 。使用 pytest 库很快就会变得混乱,有时甚至不符合 Pythonic 语言。我们将看看这个库的一些重要部分,以及可能会让新用户感到困惑的功能。
**参数化测试功能**
在很多情况下,我们可能想要测试同一个测试函数的多个输入。按照惯例,这将使用一个`for`循环来完成,该循环遍历一些 iterable。在 pytest 中,我们可以通过用`@pytest.mark.parametrize`装饰器装饰我们的测试函数来隐式地做到这一点。
将函数参数化
使用上面的装饰器,我们看到我们正在装饰`test_numeric_values`函数。装饰器首先接受一个表示参数名称的字符串输入。在我们的例子中,这将是`my_int`和`my_float`。下一个参数是我们希望分配给参数名的一组值。这里我们有两个列表,`[1, 1.1]`和`[2, 2.2]`,这意味着这个测试函数将运行两次;每个列表一次。有了这样的设置,我们就不再需要手动循环遍历可迭代的输入。也可以参数化一个类,使`parametrize`装饰器对该类中的每个方法都可用。我们在下面的代码片段中看到了一个一般的例子。
下面的片段。参数化一个类
**使用夹具**
使用夹具允许我们在运行实际测试之前设置测试环境**。夹具是一种环境,我们用它来确保我们拥有成功运行测试所需的一切。Pytest 有自己的 fixture decorator 用于这个目的。假设我们在运行测试之前需要执行一组任务。这可能是登录到服务器或获取一些数据。在夹具的帮助下,我们可以在运行测试之前自动运行这样的操作。**
在测试前使用夹具读入 CSV 文件
上面我们看到了带有`@pytest.fixture`装饰器的函数`grab_test_file`。在运行测试之前,夹具读入一个 CSV 文件。一旦我们有了我们的 fixture,我们想在我们的一个测试函数中使用它作为参数。这正是函数`test_file_content`所做的。fixture 不会为每个单独的测试运行,只是那些我们将其设置为参数的测试。像这样设置我们的代码使得在我们需要的时候重用这些设备变得很容易。
**conftest . py 文件**
当我第一次开始使用 pytest 测试我的代码时,conftest.py 文件的使用(和存在)对我来说是一个谜。我会看到几个测试的参数既不是在同一个文件中定义的,也不是从另一个文件中显式导入的。这很不寻常,我花了一些时间才完全理解。直到我意识到 conftest.py 文件的存在,我才终于明白这些参数是从哪里来的。
检查输入是否为字符串的简单测试
假设我们有一个名为`test_if_string`的测试函数。我们在这里看到了一些不寻常的东西。我们传入了`just_some_strings`参数,但是它没有在我们的测试文件中定义。那么它在哪里?这就是`conftest.py`文件发挥作用的地方。我们可以在测试目录中有一个名为`conftest.py`的单独文件,代码如下:
通过创建`conftest.py`文件并添加到这个 fixture 中,我们现在将能够在我们的任何测试文件中访问它,而不需要导入。让这些夹具在全球范围内可用允许我们将夹具从我们实际的测试功能中分离出来。尽管这可能不是使用 fixtures 的最明确的方式,但它使我们的代码更有条理。
您可能还注意到,我们在夹具中使用了一个名为`request`的参数。`request`是 pytest 中的一个特殊工具,它为我们提供了关于测试函数的信息。当我们在第 5 行返回`request.param`属性时,它返回我们在装饰器的`params`参数中传递的值。因此对于这个具体的例子,`test_if_string`函数将运行两次,因为`just_some_strings` fixture 有一个两个元素的列表作为它的`params`参数。
**模拟功能**
要使用 pytest 的 mock,我们可以使用 [pytest-mock](https://pypi.org/project/mock/) 库。但是到底什么是模仿呢?当运行测试时,我们有时可能不得不运行一个计算量很大的函数。如果我们必须在一次测试中多次运行这个函数,那么整个过程需要很长时间才能完成。不用在测试中运行这个函数,我们可以创建一个模仿它的模拟对象,而不用实际运行昂贵的计算。通过这样做,我们简化了测试并节省了时间。创建模拟允许我们模仿常量、函数、类或任何其他 Python 对象的行为。让我们看一个例子来更好地说明这一点。
假设我们正在测试一个模块,我们必须在一个巨大的数据集上执行一些复杂的数据聚合才能完成它。
具有昂贵函数调用的模块
在这个例子中,我们有一个`expensive_data_aggregation`函数,它需要 100 秒来完成。我们还有调用`expensive_data_aggregation`函数的`slow_computing_function`。如果我们想测试这个,每次需要 100 秒。然而,我们只关心函数运行后会输出什么,而不关心它是否真的运行了。我们可以使用模拟对象来修补它的行为,而不是实际运行它。
模仿昂贵的函数调用的返回值
在我们的`test_my_computation`函数中,我们调用了`mocker.patch`。这个函数允许我们修补传递给它的任何东西的属性。我们通过了`'preprocessing_module.expensive_data_aggregation’`。这是我们在前面的代码片段中看到的开销很大的函数。我们模仿了它的`return_value`属性,将其设置为 12。测试时,这相当于运行昂贵的函数并让它返回值 12。我们刚刚节省了 100 秒的测试时间!
还可以将模拟与我们之前学习的其他概念结合起来使用。假设我们需要在多个测试中运行那个`expensive_data_aggregation`函数,而不是一个?让我们把它变成一个固定装置。
将模拟对象设置为在多个测试函数中使用的 fixture
在这里,我们已经将`mocker.patch`呼叫转换成它自己与`@pytest.fixture`的固定连接。不像以前那样在每个测试函数中调用它,我们可以在函数中包含 fixture 名称作为参数,以便能够自动使用它!
**结论**
我们讨论了 pytest 库中的一些重要功能。我们研究了如何对测试函数进行参数化,以便使用多个不同的输入/输入集轻松运行测试。这消除了使用笨重的 for 循环进行测试的需要。接下来,我们看了夹具,以及如何在运行测试之前使用它们来设置我们的测试环境。我们还研究了`conftest.py`文件,它允许我们设置全局可用的测试对象,供我们在任何测试模块中使用。最后,我们看了如何模拟对象进行测试。在我们的例子中,我们专门研究了多次运行计算量很大的模仿函数。我希望这篇概述能让您将代码测试提升到一个新的水平!
感谢您的阅读!
您可以通过以下渠道与我联系:
* [中等](https://zito-relova.medium.com/)
* [领英](https://www.linkedin.com/in/zrelova/)。
* [Github](https://github.com/zitorelova)
* [卡格尔](https://www.kaggle.com/zitorelova)
# 用 Triton 编写自定义 CUDA 内核
> 原文:<https://towardsdatascience.com/writing-custom-cuda-kernels-with-triton-abf6b6ad1168?source=collection_archive---------26----------------------->
## 探索 Triton 的实时(JIT)编译器和代码生成后端
随着深度学习的成功和研究论文的爆炸,我们经常会发现自己处于这样一种情况:我们提出了一个新想法,但却发现它没有经过硬件加速。更具体地说,当我们提出一个新的激活函数或自我关注机制,并且必须依赖 PyTorch/Tensorflow 提供的功能来处理模块的向前和向后传递时。
PyTorch JIT 是这些情况下的一种选择。但是 PyTorch JIT 是一个高级编译器,只能优化部分代码,不能用来写自定义 CUDA 内核。
写 CUDA 内核还有一个问题。这很难做到。针对局部性和并行性优化计算非常耗时且容易出错,通常需要花费大量时间学习如何编写 CUDA 代码的专家。此外,GPU 架构正在快速发展,如最新版本的张量核心,这意味着编写代码以利用硬件的最大性能是一个更大的挑战。
这就是 OpenAI [Triton](https://github.com/openai/triton) 的用武之地。Triton 有三个主要组成部分

图 Triton 主要组件概述。[来源](http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf)(经许可)
1. Triton-C:一种类似 C 的语言,主要面向已经熟悉 CUDA 的程序员。
2. Triton-IR:一种基于 LLVM 的中间表示(IR)。Triton-IR 程序直接从 Triton-C 构建而来。简而言之,LLVM 提供了许多特定于硬件的优化,这意味着我们可以直接使用 Nvidia 的 CUDA 编译器(NVCC)来优化我们特定于特定硬件的代码。
3. Triton-JIT:一个实时(JIT)编译器和代码生成后端,用于将 Triton-IR 程序编译成高效的 LLVM 位代码。这还包括许多独立于机器的优化,这意味着我们的工作量减少了。
对我来说,Triton-JIT 是 Triton 项目中最令人兴奋的部分。它允许几乎没有 CUDA 编程经验的程序员用 Python 编写高度优化的 CUDA 内核。在讨论 Triton 之前,我们需要了解 CUDA 程序是如何在 GPU 上工作的。
有用的链接
* [Triton:用于平铺式神经网络计算的中间语言和编译器](http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf)
* [Triton 简介:用于神经网络的开源 GPU 编程](https://openai.com/blog/triton/)
* [triton github](https://github.com/openai/triton)
* [triton 文档](https://triton-lang.org/)
# GPU 编程基础
从 CPU(主机)开始。CPU 可以访问 RAM、存储磁盘和所有连接的外围设备。另一方面,GPU(设备)无法访问 RAM 或任何东西。GPU 有自己的内存,称为 VRAM,数据必须从 CPU->GPU 复制,以便 GPU 对其进行处理,数据必须再次从 GPU->CPU 复制,以便 CPU 将其存储在其中一个存储设备中或与连接的外围设备共享。
> ***注:*** *这就是你要尽可能减少 CPU 和 GPU 之间数据移动的原因。要做到这一点,您必须集思广益,研究如何以块的形式加载数据,以便并行处理数据,或者在导入下一个数据项之前,以可以多次重用的方式导入数据。*
在 CUDA 中,我们在线程组**块**中启动许多**线程**,形成一个**网格**。线程*块*中的所有*线程*可以相互通信。您可以一次启动每个块 1024 个线程和 2^32-1 块。图 2 显示了一个这样的例子。

图 CUDA 程序的架构。[来源](https://www.wikiwand.com/en/Thread_block_(CUDA_programming))
使用*块*背后的想法是,如果你将来得到一个新的 GPU,你不需要改变你的代码。因此,新的 GPU 可以简单地同时执行更多的*块*,而无需更改任何代码。
# CPU 程序 vs CUDA 程序
不涉及技术细节,让我们考虑一个简单的例子,添加两个长度为 3 的数组。
在 C++中,如果我们想要添加这些数组,那么我们将创建一个运行三次的 for 循环(假设是单线程程序)。
但是在 CUDA 中,我们将启动 3 个线程,每个线程将在一个索引处执行加法,而 for 循环在一个步骤中完成。实际上,会发生以下情况
1. 将`arr1`、`arr2`从 CPU 复制到 GPU。
2. 创建一个大小为 3 的新数组(或者将加法的结果存储在`arr1`中)。
3. 启动 3 个线程进行加法运算,并将结果存储在新数组中。
4. 将结果从 GPU 复制到 CPU。
因为 GPU 有 1000 个内核,所以在 GPU 上做加法、矩阵乘法等简单的事情要比 CPU 快得多(前提是加速比大于 CPU 和 GPU 之间传输数据所花费的时间)。
# CUDA vs Triton
我们在上面看到了 CUDA 执行模型。现在我们来看看 Triton 和上面的模型有什么不同。
在 CUDA 中,每个内核都与一个*线程块*相关联(即一组*线程*)。在 Triton 中,每个内核都与一个*线程*相关联。这种执行范例解决了线程间的内存同步、线程间通信的问题,同时允许自动并行化。
现在,块由一个*范围*组成,即指向线程的指针块,而不是将线程存储在线程块中。有趣的是,你可以想有多少范围就有多少范围。因此,如果您的输入是 2D,您可以为 x 轴指定 Range(10 ),为 y 轴指定 Range(5 ),总共 50 个线程。同样,您可以根据需要定义任意多个维度的范围。

图 3: CUDA 执行模型与 Triton 执行模型。[来源](http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf)(经许可)
# 使用 Triton 添加两个数组
现在我们对 CUDA 和 Triton 的工作原理有了基本的了解,我们可以编写 Triton 程序了。使用以下命令安装 Triton
```
pip install triton
```
下面给出了这些步骤的摘要:
1. 定义**块** 。我们知道**块** 是通过指定一个范围来定义的。所以除此之外,我们只需要定义一维的范围。设它是 512,我们定义它为全局`BLOCK_SIZE=512`。
2. 512 的范围实际上意味着我们启动 512 个线程来进行计算。
3. 接下来,我们得到输入数据的索引。假设输入数组的大小为 1000。因为我们定义了大小为 512 的块,所以我们将以大小为 512 的块来处理输入数组。所以第一个数据块来自`0:512`,第二个数据块来自`512:1024`。这是使用下面显示的代码完成的
```
*# Addition is 1D, so we only need to get the index of axis=0* pid = triton.language.program_id(axis=0)*# Below offsets are a list of pointers* block_start = pid * BLOCK_SIZEoffsets = block_start + triton.language.arange(0, BLOCK_SIZE)
```
4.屏蔽以保护内存操作。在上面的例子中,输入数组的大小为`N=1000`,但是偏移量来自`512:1024`。因此,我们需要指定一个掩码来防止越界访问。需要为每个轴指定该掩码。
```
mask = offsets < N
```
5.加载数据。现在我们已经定义了偏移量和掩码,我们可以从 RAM 加载数据并屏蔽掉所有额外的元素。
```
def add_kernel(arr1_ptr, arr2_ptr, output_ptr, ...):
...
arr1 = triton.language.load(arr1_ptr + offsets, mask=mask)
arr2 = triton.language.load(arr2_ptr + offsets, mask=mask)
```
6.做相关操作。在这种情况下,我们只需要做加法。
```
output = arr1 + arr2
```
7.完成计算后,将结果存储在 RAM 中。GPU 无法访问存储,所以我们必须首先将数据移动到 RAM,然后如果需要,我们可以将数据存储到磁盘。
```
triton.language.store(output_ptr + offsets, output, mask=mask)
```
整个内核的代码如下所示
```
import triton
import triton.language as tlBLOCK_SIZE = 512@triton.jit
def add_kernel(arr1_ptr, arr2_ptr, output_ptr, N):
*# Step 1: Get range of axis* pid = tl.program_id(axis=0)
*# Step 2: Define the offsets and mask* block_start = pid * BLOCK_SIZE
offsets = block_start + tl.arange(0, BLOCK_SIZE)
mask = offsets < N *# Step 3: Load the data from RAM* arr1 = tl.load(arr1_ptr + offsets, mask=mask)
arr2 = tl.load(arr2_ptr + offsets, mask=mask)
*# Step 4: Do the computation* output = arr1 + arr2 *# Step 5: Store the result in RAM* tl.store(output_ptr + offsets, output, mask=mask)
```
要使用内核,我们可以定义一个如下所示的助手函数
```
def add(arr1: torch.Tensor, arr2: torch.Tensor):
output = torch.empty_like(arr1)
N = output.numel() grid = lambda meta: (triton.cdiv(N, BLOCK_SIZE),) add_kernel[grid](arr1, arr2, output, N)
return output
```
基本上是指定我们工作的空间。在我们的例子中,网格是 1D,我们指定如何沿着网格分割数据。因此,如果输入数组的大小,那么我们希望网格如下`[0:512], [512:1024]`。所以在这一步,我们基本上是指定如何分割输入数据并将其传递给内核。
默认情况下,`grid`接受一个位置参数,我们称之为`meta`。`meta`的目的是提供类似`BLOCK_SIZE`的信息,但是在这个例子中,我们使用了一个全局变量来定义,所以使用`meta`是多余的。
现在我们可以像调用普通 python 函数一样调用`add`函数,如下所示(确保传递给函数的输入已经在 GPU 上)
```
arr_size = 100_000
arr1 = torch.rand(arr_size, device='cuda')
arr2 = torch.rand(arr_size, device='cuda')pytorch_out = arr1 + arr2
triton_out = add(arr1, arr2)print(torch.sum(torch.abs(pytorch_out - triton_out)))
```
在外
```
> python main.py
tensor(0., device='cuda:0')
```
## 添加更高的维数张量
我们也可以对 N 维张量重用相同的核。这给了我们避免为不同数量的输入维度编写多个内核的灵活性。这个想法很简单,我们在辅助函数中把张量整形为 1D 张量,然后整形输出张量。
整形操作不是耗时的操作,因为我们只是修改张量类中的步幅值。修改后的助手函数如下所示。
```
def add(arr1: torch.Tensor, arr2: torch.Tensor):
input_shape = arr1.shape
arr1 = arr1.view(-1)
arr2 = arr2.view(-1) output = torch.empty_like(arr1)
N = output.numel() grid = lambda meta: (triton.cdiv(N, **BLOCK_SIZE**),) add_kernel[grid](arr1, arr2, output, N) output = output.view(input_shape)
return output
```
然后我们像以前一样调用这个函数
```
arr_size = (100, 100, 100)
arr1 = torch.rand(arr_size, device='cuda')
arr2 = torch.rand(arr_size, device='cuda')pytorch_out = arr1 + arr2
triton_out = add(arr1, arr2)print(torch.sum(torch.abs(pytorch_out - triton_out)))
```
在外
```
> python main.py
tensor(0., device='cuda:0')
```
这是一个简单的例子。但是在矩阵乘法这样的复杂例子中,如何分割数据会对性能产生巨大的影响。Triton docs 提供了一个关于如何编写一个高效的矩阵乘法内核的教程[,其中包含了更多的细节。](https://triton-lang.org/getting-started/tutorials/03-matrix-multiplication.html)
这是一个简短的教程,介绍了 GPU 编程基础知识,并让您开始使用 Triton。如果您想了解更多信息,请查看 openai/github 上的 Triton 项目。
[twitter](https://twitter.com/Kkushaj) , [linkedin](https://www.linkedin.com/in/kushaj/) , [github](https://github.com/KushajveerSingh)
*原载于 2021 年 9 月 12 日*[*https://kushajveersingh . github . io*](https://kushajveersingh.github.io/blog/writing-custom-cuda-kernels-with-triton)*。*
# 编写可伸缩的 ML 代码
> 原文:<https://towardsdatascience.com/writing-ml-code-that-scales-51028e9872a0?source=collection_archive---------31----------------------->
## 从单台机器到多工人设置
在你最终创建了培训脚本之后,是时候扩大规模了。从一个本地开发环境,无论是 IDE 还是 Colab,到一个大型的计算机集群,这是一个相当大的挑战。以下最佳实践使这种转变更加容易。

弗兰克·布施在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
## 参数分析器
首先是使用参数解析器。Python 通过 *argparse* 模块提供了这样的功能。通常,您希望将批处理大小、时期数和任何目录作为可选参数。手动浏览一个脚本,找到所有使用特定参数的地方,然后全部手动更改,这非常令人沮丧。因此,避免硬编码这样的参数。而不是写作
写起来更方便
## 将参数解析器的设置与训练代码分开
这是我经常犯的错误。我在训练脚本中创建了参数解析器,并在本地验证了训练。接下来,我部署了这段代码。却看到它在远程机器上失败了:我设置为默认参数的目录指向我的本地磁盘。
**编辑/注释**:虽然我仍然推荐使用参数解析器,但我发现处理超过 10 个、20 个参数是混乱的。在这种情况下,我建议仅将它们用于主要设置。对于更高级的设置,我推荐使用 [*Gin* python 库](https://github.com/google/gin-config)。
点击此处了解更多信息:
</many-hyperparameters-use-gin-fdf6741d282>
## 将参数解析器的设置与训练代码分开
而不是编写类似于
(覆盖您在部署机器上手动更改的参数),您有一个不受更新影响的单独文件:
您的本地项目和已部署的项目都有这个文件。然后在主训练代码中导入这个方法;在本地,路径指向您的本地文件夹,在远程机器上,它们指向相应的目录。这样,在从本地单个 GPU 机器转到多工作器设置后,您就不必记得更改默认参数了。
**注意**:和之前类似,我现在推荐用 [*Gin* python 库](https://github.com/google/gin-config)处理参数设置。我在这里写了[一个简短的介绍](/many-hyperparameters-use-gin-fdf6741d282)。
# 单独的模型创建脚本
将您的模型创建例程从主要的训练代码中分离出来会产生精益代码。与上一点类似,您使用一个单独的文件来存储模型及其配置,然后在主代码中导入该方法。将这一点与下一点结合起来,实现巨大的可伸缩性。
# 使超参数易于编辑
当您硬编码任何参数时,您将很难更新它们。使用 512 个内核而不是 16 个?使用更大的致密层?
每次你尝试一个新的配置,你首先要找到相关的代码,然后改变它。这将很快变得令人讨厌。解决方案是将超参数作为脚本参数的一部分,或者用一个单独的文件来存储它们。
第一个选项看起来像
第二个选项类似于
在这两种情况下,您都有一个管理默认参数的中心位置。从 16 核扩展到 512 核?那只是几秒钟的事了。
**注意**:和之前类似,我现在推荐用 [*Gin* python 库](https://github.com/google/gin-config)处理参数设置。我在这里写了[一个简短的介绍](/many-hyperparameters-use-gin-fdf6741d282)。
# 使用与设备无关的代码
这是我在 [TensorFlow](https://www.tensorflow.org/api_docs/python/tf/distribute/Strategy) 上看到的一个极好的特性:无论是单个 GPU 还是 20 个工人,每个人都有 16 个 GPU,编写你的代码,它无论如何都可以工作——无需任何修改。TensorFlow 用*策略*对象为您处理这个问题。这使您能够在单个 GPU 和多个工作人员之间无缝切换,当您在编码时只能访问单个设备,但在部署时使用多个设备时,这非常方便。
而不是写作
您用策略对象包装模型创建和编译例程:
将您的代码包装在策略对象的范围内负责分发模型和优化器。您可以使用 Hugginface 的 Transformer 库中的以下代码片段来创建这样一个对象:
我使用相同的技术从本地非 GPU 设置扩展到多 GPU 集群和 TPU 培训。在一个命令中使用四个、八个甚至更多的 GPU 是很神奇的。
# 让您的框架来处理数据集
您可以让 ML 框架来完成这项任务,而不是手动负责加载数据、解析、缓存等等。许多人为这些项目做出了贡献,使得默认管道非常有效。它们本身支持多处理、缓存、批处理等等。如果您正在使用 TensorFlow,有许多方法可以让您的数据为这些操作做好准备;在这篇文章中,我展示了其中的一些[。](/a-practical-guide-to-tfrecords-584536bc786c)
尤其是对于复杂的环境,这一点至关重要:如何将数据提供给三个工作人员?这些功能通常已经实现。不要多此一举,但**专注于有趣的部分:**
**创建和训练模型。**
# 编写可伸缩的张量流代码
> 原文:<https://towardsdatascience.com/writing-tensorflow-code-that-scales-c0c93aa3736d?source=collection_archive---------22----------------------->
## 添加两行代码来增强您的脚本
大多数时候,我们在本地编写和调试代码。通过任何测试后,我们将脚本部署到远程环境中。如果幸运的话,我们可能可以访问多个 GPU。这种转变曾经是错误的来源,但是对复杂计算设置的支持已经显著增加。

杰里米·贝赞格在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
如果您主要使用 Keras 的 *model.fit()* 调用,那么您可以通过添加两行代码来快速地让您的脚本分发知晓。
# 在分配工作负载之前
最初,我们的代码可能是这样的:
# 分配工作负载后
为了让代码准备好发布,我们必须修改第 1 行到第 4 行。为此,我们创建了一个*策略*对象,这是 TensorFlow 处理计算环境的方式。然后,我们使用它来包装我们的代码,如下面的代码片段所示:
在**我们添加的第一行**,第一行,我们设置了我们的策略对象。在示例中,我们使用了一个 *MirroredStrategy* ,它告诉 TensorFlow 在一台机器上的多个计算设备上复制模型。对于其他环境,我们可以选择不同的策略,如文档中列出的[。](https://www.tensorflow.org/guide/distributed_training#types_of_strategies)
除了我们将模型和优化器创建例程包装在所选的*策略*对象的范围内之外,其余代码基本相同。这是**我们添加的第二行代码**并且是必要的修改:*作用域*修改变量是如何创建的以及在哪里放置它们。
之后,我们可以照常进行,调用 *model.fit()* 。然后,工作量会自动分配。我们在这里什么都不用做,因为 TensorFlow 在内部处理所有事情,这非常舒服!这也是一个巨大的进步:仅仅几年前,研究人员和从业者还必须自己编写分发算法。
现在,必须一直手动实例化*策略*对象是令人厌烦的。然而,我们可以用下面的代码自动创建正确的对象,我通常喜欢把它放在一个单独的 *utils.py* 文件中:
这段代码[取自 Hugginface 的存储库](https://github.com/huggingface/transformers/blob/1c06240e1b3477728129bb58e7b6c7734bb5074e/src/transformers/training_args_tf.py#L191),在创建分发策略之前检查计算环境。根据设置,返回的*策略*对象处理 TPU、GPU 以及混合精度训练。
如果您使用这种方法,那么您只需更改这一行:
# 摘要
这就是你要在代码中修改的全部内容。总而言之,你
* 制定分销策略
* 将所有变量创建例程都包含在策略范围内
* 像平常一样调用 model.fit()
这些步骤涵盖了使用 Keras 的高级 API 进行任何计算的情况。但是,如果您正在使用自定义训练循环,则需要注意更多事项。在这种情况下,[我这里有你罩着](/a-template-for-custom-and-distributed-training-c24e17bd5d8d)。
# 从头开始编写验证指标
> 原文:<https://towardsdatascience.com/writing-validation-metrics-from-scratch-e7d10af59f98?source=collection_archive---------40----------------------->
## 从头做起
## 查看各种验证指标,以及如何计算它们

(src =[https://pixabay.com/images/id-1044725/](https://pixabay.com/images/id-1044725/)
# 介绍
我们都经历过,你的模型完成了,你想测试它。您将测试数据放入模型中,然后得到一个预测作为回报。现在是时候验证您的模型了,并弄清楚您在创建模型方面做得如何。有几个验证指标可供选择,但是您如何知道选择哪一个来验证您的模型呢?此外,这些技术背后有哪些公式可以让我们更好地理解它们?今天,我想揭示有多少评估指标是实际计算出来的,然后再详细介绍一下它们的用法。希望下次你想检查一个模型的性能时,这些信息能派上用场!如果您想亲自查看这段代码的运行情况,也可以在笔记本中查看这段代码,网址为:
> [笔记本](https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Python3/Validation%20metrics.ipynb)
# r
我想回顾的第一个指标是 r 指标。这是评估回归模型的一个非常流行的指标。该值以百分比的形式出现,该百分比通常用于衡量两个数组的相似性。当然,这仅适用于连续值,这种度量并不真正适用于分类目标。r 度规顾名思义,它实际上是 r 的平方。但是 r 从哪里开始呢?r 值用于表示皮尔逊相关系数。这是一种非常流行的推断方法,用于确定两个给定数组的相关程度。虽然它不用于假设检验,但它仍然可以用于机器学习的许多应用中。也就是说,我们需要首先计算相关系数,以获得决定的相关系数,或 r。为此,函数的输入将是两个数组:
```
def r(x, y):
```
我们需要做的第一件事是得到 n,对于那些没有统计学知识的人来说,n 是我们样本的大小。我们可以使用 Python 中的 len()方法来实现这一点:
```
n = len(x)
```
接下来,我们需要执行一些元素级的乘法来得到一个新的 xy 数组。在 Python 中这样做的最好方法是使用 Numpy.multiply(),所以我将在这个函数上面导入 Numpy,然后这样做:
```
import numpy as np
def r(x, y): n = len(x)
xy = np.multiply(x, y)
```
现在我们需要得到一系列的总和,每个数组都有一个总和:x,y,xy。
```
Σxy, Σx, Σy = sum(xy), sum(x), sum(y)
```
接下来,我们需要获得更多的数组,x 和 y。
```
x2, y2 = np.power(x, 2), np.power(y, 2)
```
最后,我们得到这两者的和:
```
Σx2, Σy2 = sum(x2), sum(y2)
```
现在我们可以把这些代入一个公式来计算 r,公式是
(n *σ(x * y))-(σx *σy))/√(((nσx)-((σx)))*((n *σy)-((σy))
请注意,σx 和(σx)之间的公式表示有所不同。他们可能读的一样,
> 但它们并不相同。
一个是 x 的和,另一个是 x 的和,平方。这真的很难用文字表达。在一个例子中,σx,我们得到数组 x 的平方和。这是因为在运算顺序中,指数在求和之前。在另一个例子中,(σx),我们首先对 x 求和,因为它在括号中,然后平方它。我真的希望这有意义!无论如何,把那个公式放进 Python 看起来像这样:
```
((n * sxy) - (sx * sy)) / (np.sqrt((((n * sx2)-(sx ^ 2)) * ((n * sy2)-(sy ^ 2)))))
```
> 等等!
我来自科学计算的世界,^的意思是指数。遗憾的是,Python 不是这样的,^在 Python 中是 xor 运算符。我们需要改用**。这是又一个例子,表明 Python 经常看起来不像论文,并且最初并没有打算成为一种科学语言。让我们来解决这个问题:
```
((n * Σxy) - (Σx * Σy)) / (np.sqrt((((n * Σx2)-(Σx ** 2)) * ((n * Σy2)-(Σy ** 2)))))
```
有了返回,现在我们的最终函数看起来像这样:
```
import numpy as np
def r(x, y):
n = len(x)
xy = np.multiply(x, y)
Σxy, Σx, Σy = sum(xy), sum(x), sum(y)
x2, y2 = np.power(x, 2), np.power(y, 2)
Σx2, Σy2 = sum(x2), sum(y2)
return(((n * Σxy) - (Σx * Σy)) / (np.sqrt((((n * Σx2)-(Σx ** 2)) * ((n * Σy2)-(Σy ** 2))))))
```
虽然我们可以写一个函数来做 r,并在那里计算所有这些,但把它写成一个单独的函数更有意义。我说过一次,我还要再说一次,更多的方法意味着更好的代码!如果你想了解更多关于我如何使用提取和命名方法来改进我的软件,我写了一整篇文章。这篇文章链接在另一篇文章中,我也将让您眼花缭乱,这篇文章提供了一些更好的重构方法来使您的代码更好:
</5-fabulous-refactoring-methods-for-prettier-code-e3bc3447c0b2> [## 更漂亮的代码的 5 个惊人的重构方法
towardsdatascience.com](/5-fabulous-refactoring-methods-for-prettier-code-e3bc3447c0b2)
这篇文章是我最喜欢的文章之一,因为其中的技术非常简单,但是在提高代码的可读性、可伸缩性和可用性方面非常有效。继续,我们终于可以创建 r 函数了。在这个新函数中,我们需要做的就是将相关系数设为 2 的幂。
```
def r2(y, ŷ):
return(r(y, ŷ) ** 2)
```
现在让我们用一组基本的数组来测试这个函数,它应该返回 100%的准确性:
```
x = [8, 7, 6, 5, 3, 0, 9]
y = x
r2(x, y)
```
> 是的,我有珍妮的号码。

# 平均绝对误差
平均绝对误差(MAE)是另一种用于测量连续目标精度的流行指标。这个算法与 r 的算法相差甚远,而且非常简单。MAE 是一个有点缺陷的度量,因为尽管它可以给我们一个模型准确性的好主意,它也需要一些关于连续值的方差的先验知识。我倾向于偏离这个标准,但它并不是完全无用的。这仍然是一个很好的指标,我可以推荐使用。我们可以使用 Numpy.subtract()方法来做元素减法,然后我们只需要得到这两个值的差的平均值:
```
def mae(y, ŷ):
error = np.subtract(y, ŷ)
return(np.mean(error))
```
现在让我们用珍妮的号码试试:
```
mae(x, y)
```

# 均方误差(mean square error)
均方误差(MSE)指标遵循平均绝对误差中概述的相同约定。然而,有了这个度量标准,我们最终会得到一个更准确的模型准确性状态的表示。正如您所料,该值用于连续应用程序,虽然它通常用于回归模型,但由于它在其他机器学习应用程序中的广泛使用,该指标非常突出。这可能是机器学习和神经网络估计误差的最常见的连续度量之一。计算该值的第一步是按元素减去两个数组,就像我们对 MAE 度量所做的那样:
```
def mse(y, ŷ):
error = np.subtract(y, ŷ)
```
最后一步是得到这些值的平均值的平方。我们可以使用 np.power()方法像对 r 那样做。我经常把这与 np.exp()方法混淆,所以不要这样做。相信我,它们不是混合的好方法!这个常见错误的不好之处在于,文档并没有很好地解释这两者之间的区别,只有示例和公式符号可以让人们真正注意到这种区别。当然,这只是主观上让我感到困惑,尽管我听说过很多人把这两者混为一谈。我认为这是一个常见的错误,尤其是对于那些主要不属于 Python 编程语言的人来说,所以不要犯这个错误!
```
def mse(y, ŷ):
error = np.subtract(y, ŷ)
return(np.mean(np.power(error, 2)))
```

我知道我的 Jenny 的数字例子并不令人兴奋,但它表明这些指标是有效的,因为它们能够表明我们知道具有 100%准确性的东西。在我们完成最终的度量后,我们将使用不同的随机元素数组进行同样的尝试。
# 分类准确性
当然,并不是所有计算中的问题都是连续的。有时,您可能会使用一些分类特征。有一种可能是,您可能已经使用了算法来获得绝对的准确性,但却从未意识到这一点。为了保证分类的准确性,这些值需要完全相等。这偏离了连续精度,我们可以专注于数值空间中两个值的接近程度。
每当我写一个函数来获得这个精度时,我做的第一件事就是再次得到 n。然后,我将在函数范围内对变量进行初始化,这样我们就可以进行迭代循环:
```
def categorical_acc(y, ŷ):
n = len(ŷ)
score = 0
```
对于循环,我将使用 zip()迭代器来同时遍历我们的两个数组。如果您想阅读关于这个迭代器的更多细节,它的细微差别,以及如何使用它,您可以随意查看我的另一篇文章,在这篇文章中,我详细讨论了 zip ():
</everything-you-need-to-know-about-zip-in-python-5da1416f3626> [## 关于 Python 中的 Zip,您需要了解的一切
towardsdatascience.com](/everything-you-need-to-know-about-zip-in-python-5da1416f3626)
无论如何,这里是循环。我们将检查每个列表中的值是否相等,如果相等,则增加分数:
```
for i, w in zip(ŷ, y):
if i == w
score += 1
```
最后,我们将分数除以 n,得到 n 的百分比,这将告诉我们与整个数组的长度相比,匹配值的百分比是多少。将它放入一个返回中,这是我们的最终结果:
```
def categorical_acc(y, ŷ):
n = len(ŷ)
score = 0
for i, w in zip(ŷ, y):
if i == w;
score += 1
return(score / n)
```
不幸的是,在这个例子中我们不能使用 Jenny 的数字,因为它是连续的,所以让我们使用 Numpy.random 模块生成两个数组:
```
x = [np.random.choice(["eggs", "bread", "ham"]) for c in range(1, 20)]
y = [np.random.choice(["eggs", "bread", "ham"]) for c in range(1, 20)]categorical_acc(x, y)
```

# 结论
正如我所承诺的,并且只是为了好玩,下面是应用于两个阵列的连续指标,它们不是 Jenny 的数字:
```
x = np.random.randn(50)
y = np.random.randn(50)**print("MSE:\t", mse(x, y))
print("MAE:\t", mae(x, y))
print("R2:\t", r2(x, y))**
```

这些指标在数据科学领域都非常有价值和有用。希望剖析它们并从头重新创建它们(在 NumPy 的帮助下)是一个很好的工具,有助于更好地理解度量标准及其工作方式。在大多数情况下,它们通常没有那么复杂,但肯定非常棒!感谢您的阅读,我希望这篇关于机器学习验证的文章读起来和写起来一样有趣!
# 用 python 编写 YAML 文件
> 原文:<https://towardsdatascience.com/writing-yaml-files-with-python-a6a7fc6ed6c3?source=collection_archive---------0----------------------->
## 使用 ruamel.yaml 注释配置文件
*本文期望读者对* [*python 编程语言*](https://docs.python.org/3/) *有一个基本的了解。*

https://pxhere.com/en/photo/492303
Yaml 文件已经存在了二十多年了。它们对于显示数据结构非常有用,并且是配置文件的一种流行格式。从 [AWS CloudFormation](https://aws.amazon.com/cloudformation/) 、[公共工作流语言](https://www.commonwl.org/user_guide/)到[家庭助手](https://www.home-assistant.io/) ⁴,yamls 是互联网配置文件的基本支柱。它们比其他格式(如 [JSON](https://en.wikipedia.org/wiki/JSON) )更容易阅读,支持注释并易于版本控制——试着在两个略有不同的 JSON 文件上运行`git merge`,你会明白我的意思。尽管如此,当涉及到用我们钟爱的语言 python 编写 yaml 文件时,我们的选择相当有限。只有 [PyYAML](https://pyyaml.org/wiki/PyYAMLDocumentation) 和 [ruamel.yaml](https://yaml.readthedocs.io/en/latest/) 在 [yaml 一致性测试矩阵](https://matrix.yaml.io/)中有注册表,其中只有 ruamel.yaml 支持处理注释。
如果用户可能需要在稍后阶段手动修改配置文件,那么能够将注释写入配置文件是非常有用的。另一个优势是 enum 支持,通过向 yaml 文件添加注释,用户可以看到某个设置的所有可用选项,而不必滚动文档来找到它们!更好的是,配置文件中的注释可以让用户包括到文档的链接,在那里他们可以阅读配置文件的那个部分!
因此,让我们开始通过 python 编写一些 yaml
## 一个示例 yaml 文件:
```
employees:
- name: Jeffrey Bezos
job title: CEO
annual salary (USD): 1000000000000
- name: John Smith
job title: factory worker
annual salary (USD): 20000
```
这个 yaml 文件相当于 python 中的一个字典,只有一个关键字*“*employees ”,包含两个元素的列表。
嵌套列表中的每个元素包含三个相同的关键字:“姓名”、“职位”和“年薪(美元)”。
yaml 文件很酷的一点是能够使用“#”符号对它们进行注释
```
employees:
# Start with CEO
- name: Jeffrey Bezos # Goes by Jeff
job title: CEO # ...entrepreneur, born in 1964...
annual salary (USD): 1000000000000 # This is too much
# List of factory workers below
- name: John Smith
job title: factory worker
annual salary (USD): 20000 # Probably deserves a raise
```
## 安装 ruamel.yaml
下面我们将尝试用 ruamel.yaml 将这个 yaml 文件加载到 python 中。如果你需要安装 ruamel.yaml,我们将使用版本`0.17.4`,这里有一些命令可以帮助你。
安装选项 1:
```
pip install ruamel.yaml==0.17.4
```
安装选项 2:
```
conda install -c conda-forge ruamel.yaml==0.17.4
```
## 将 ruamel 导入 python 并加载 yaml 文件
```
# Imports
from ruamel.yaml.main import round_trip_load as yaml_loademployees_dict = yaml_load("""
employees:
# Start with CEO
- name: Jeffrey Bezos # Goes by Jeff
job title: CEO # / Entrepreneur, born in 1964...
annual salary (USD): 1000000000000 # This is too much
# List of factory workers below
- name: John Smith
job title: factory worker
annual salary (USD): 20000 # Probably deserves a raise
""")print(employees_dict)
print(f"Type: {type(employees_dict}")
```
输出:
```
ordereddict([('employees', [ordereddict([('name', 'Jeffrey Bezos'), ('job title', 'CEO'), ('annual salary (USD)', 1000000000000)]), ordereddict([('name', 'John Smith'), ('job title', 'factory worker'), ('annual salary (USD)', 20000)])])])
Type: <class 'ruamel.yaml.comments.CommentedMap'>
```
酷!这很简单。现在让我们试着从头开始构建一个 yaml 文件!
# 从头开始构建 yaml 文件
## 让我们首先设置我们的导入和全局变量
```
# Regular imports
from copy import deepcopy
# Yaml loaders and dumpers
from ruamel.yaml.main import \
round_trip_load as yaml_load, \
round_trip_dump as yaml_dump
# Yaml commentary
from ruamel.yaml.comments import \
CommentedMap as OrderedDict, \
CommentedSeq as OrderedList
# For manual creation of tokens
from ruamel.yaml.tokens import CommentToken
from ruamel.yaml.error import CommentMark# Globals
# Number of spaces for an indent
INDENTATION = 2
# Used to reset comment objects
tsRESET_COMMENT_LIST = [None, [], None, None]
```
并使用`OrderedDict` 和`OrderedList`属性创建一个购物清单。
```
shopping_list = OrderedDict({
"Shopping List": OrderedDict({
"eggs": OrderedDict({
"type": "free range",
"brand": "Mr Tweedy",
"amount": 12
}),
"milk": OrderedDict({
"type": "pasteurised",
"litres": 1.5,
"brands": OrderedList([
"FarmFresh",
"FarmHouse gold",
"Daisy The Cow"
])
})
})
})# Dump the yaml file
print(yaml_dump(shopping_list, preseve_quotes=True))
```
输出:
```
Shopping List:
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
创建我们的数据结构是相当乏味的。我们希望写入 yaml 的数据结构也不太可能是这种格式。它更有可能是标准的字典和/或列表格式。没关系,我们可以用`dump`和`load`来代替,将标准字典和列表格式转换成我们的 OrderedList ( `CommentedMap`)和 OrderedList ( `CommentedSeq`)。
```
# Original object
shopping_list = {
"Shopping List": {
"eggs": {
"type": "free range",
"brand": "Mr Tweedy",
"amount": 12
},
"milk": {
"type": "pasteurised",
"litres": 1.5,
"brands": [
"FarmFresh",
"FarmHouse gold",
"Daisy The Cow"
]
}
}
}
# To yaml object
shopping_list = yaml_load(yaml_dump(shopping_list), preseve_quotes=True)# Show object type
print(type(shopping_list))
```
输出:
```
<class 'ruamel.yaml.comments.CommentedMap'>
```
完美!
## 向 yaml 文件添加标题
我们可以使用`yaml_set_start_comment`属性在地图对象上添加注释
```
shopping_list.yaml_set_start_comment("Shopping Lists for date: "
"23 Oct 2021")
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
## 在嵌套键前添加注释
假设我们真的不想忘记鸡蛋,让我们在鸡蛋键上方写上“请不要忘记鸡蛋!”。由于`shopping_list`对象的‘购物清单’属性也是一个`OrderedDict`或`OrderedList`,我们可以在`Shopping List`属性上使用`yaml_set_comment_before_after_key`方法。
```
shopping_list.get("Shopping List").\
yaml_set_comment_before_after_key(key="eggs",
before="Please don't forget "
"eggs!")
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Please don't forget eggs!
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
呃,这可不理想!那个评论应该缩进。让我们删除它,并确保下次使用`indent`参数。要删除评论,我们需要重置`OrderedDict`的`ca`属性中的*蛋*键,然后重试。
## 删除注释对象
```
shopping_list.get("Shopping List").ca.\
items["eggs"] = deepcopy(RESET_COMMENT_LIST)
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
让我们用`indent`参数(设置为 2 个空格)再试一次
```
object_depth = 1 # Shopping List
shopping_list.get("Shopping List").\
yaml_set_comment_before_after_key(
key="eggs",
before="Don't forget the eggs",
indent=object_depth*INDENTATION
)print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Don't forget the eggs
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
## 在键后添加注释
我们也可以在一个键后添加注释
```
object_depth = 2 # Shopping List -> Eggs
shopping_list.get("Shopping List").\
yaml_set_comment_before_after_key(
key="eggs",
after="Please don't forget eggs!",
after_indent=object_depth*INDENTATION
)
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Don't forget the eggs
eggs:
# Please don't forget eggs!
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
## 让我们给一个键添加一个行尾注释
礼貌地提醒一下,两升牛奶太多了!
```
shopping_list.get("Shopping List").\
get("milk").\
yaml_add_eol_comment(
key="litres",
comment="2 litres is too much milk!"
)print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Don't forget the eggs
eggs:
# Please don't forget eggs!
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5 # 2 litres is too much milk!
brands:
- FarmFresh
- FarmHouse gold
- Daisy The Cow
```
## 在列表中添加注释
请注意,*农家金*和*奶牛雏菊*是最后的选择。通过在列表的农舍金牌项目的索引上使用 before 参数。对于列表中的注释,仅支持 before 参数。
```
object_depth = 3 # Shopping List -> Milk -> Brands
shopping_list.get("Shopping List").\
get("milk").\
get("brands").\
yaml_set_comment_before_after_key(
key=1, # "Farmhouse gold" is the second item in the list
before="Last Resorts",
indent=object_depth*INDENTATION
)
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Don't forget the eggs
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5 # 2 litres is too much milk!
brands:
- FarmFresh
# Last Resorts
- FarmHouse gold
- Daisy The Cow
```
## 向列表中的项目添加行尾注释
```
object_depth = 3 # Shopping List -> Milk -> Brands
shopping_list.get("Shopping List").\
get("milk").\
get("brands").\
yaml_add_eol_comment(
key=1,
comment="Too creamy"
)
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
TypeError: 'CommentToken' object is not iterable
```
哦不!这是一个[已知的 bug](https://sourceforge.net/p/ruamel-yaml/tickets/404/) ,我们将不得不使用 brands 对象的`ca`属性将其添加到列表中。
## ca 属性的细分:
让我们先看看我们在处理什么:
```
print(shopping_list.get("Shopping List").get("milk").get("brands").ca)
```
输出:
```
Comment(
start=None,
items={
1: [None, [CommentToken('# Last Resorts\n', col: 6)], None, None]
})
```
在`CommentedSeq`中只使用了 items 属性值中的前两个元素。第一个元素是 eol 注释,而第二个元素是 before 注释列表。对于`CommentedMap`,第一个元素代表“开始注释”,第二个元素是之前注释,第三个是 eol 注释,第四个是之后注释。本文的*资源*部分对此进行了总结。
## 手动添加 CommentToken 对象
我们需要将项目的第一个元素设置为我们想要的注释。
手动添加一个`CommentToken`对象时,我们以`\ #`为前缀,以一个新的行字符`\n`结束。
我们用一个最小的`CommentMark`对象初始化`start_mark`参数,并将`end_mark`参数设置为`None`。
```
shopping_list.get("Shopping List").\
get("milk").\
get("brands").ca.\
items[1][0] = CommentToken(value=" # Too creamy\n",
start_mark=CommentMark(0),
end_mark=None)
print(yaml_dump(shopping_list,
indent=INDENTATION,
block_seq_indent=INDENTATION))
```
输出:
```
# Shopping Lists for date: 21 Oct 2021
Shopping List:
# Don't forget the eggs
eggs:
type: free range
brand: Mr Tweedy
amount: 12
milk:
type: pasteurised
litres: 1.5 # 2 litres is too much milk!
brands:
- FarmFresh
# Last Resorts
- FarmHouse gold # Too creamy
- Daisy The Cow
```
# 变通办法概述
由此可以看出 ruamel.yaml 还有一定的提升空间:
1. 在使用`yaml_set_comment_before_after_key`方法的 before 或 after 参数时,需要设置缩进。这也意味着在创建注释时需要知道嵌套有多深。
2. 如果在列表中的元素上已经存在 before 注释,则不能使用`yaml_add_eol_comment`方法。
3. 在我写这篇文章的时候,ruamel 的最新版本(0.17.16)有一个 bug,使得许多输出省略了许多注释。
4. ruamel.yaml 文档中没有关于将注释写入 yaml 文件的内容。
## 对我的发现的小小咆哮
互联网最重要的配置格式结构之一怎么会没有一个来自世界上最流行的编程语言之一的完全受支持的解析器呢?我确信一定有成千上万的 python 开发者和我有着同样的想法。
在任何人急于发表评论之前,你应该向开发商 have⁵ ⁶ ⁷.提出这个问题这样做,我可以假设为什么在我之前有这么多人从来没有烦恼过。ruamel.yaml 由 [SourceForge](https://sourceforge.net/projects/ruamel-yaml/) 托管,当你访问这个网站时,你会觉得你已经进入了 90 年代(从 UX 的背景来看,90 年代是一个糟糕的地方)。与 GitHub 不同,SourceForge 不允许你在创建后编辑问题。界面笨拙,搜索算法在搜索现有问题时过于敏感(什么都会出现!)而且通知服务是古老的——它不是一个通知标签,你会在一个新的评论被添加到你的问题(包括你自己的)时收到一封电子邮件,或者你可以订阅一个 RSS 提要。
虽然 PyYAML 似乎已经重新获得了 maintenance⁸,但是当前的开发人员仍然处于一个尴尬的境地,许多软件依赖于 PyYAML 而没有指定 version⁹.更新代码以合并/导入 ruamel.yaml 中完成的许多工作,如支持注释、YAML 1.2 格式或其他常规改进,会有破坏向后兼容性的风险,因此会导致许多用户代码中断。对于那些没有对依赖项进行版本控制的人来说,这是一个很好的教训,但是我不确定每个人都这样认为。
我个人不知道正确的解决方案是什么,像 PyYAML2 这样的新名称可以继承 ruamel.yaml 的工作,同时保持对使用 set PyYAML 的人的向后兼容性?更多支持 ruamel.yaml?
这篇文章绝不是对 PyYAML 或 ruamel.yaml 的维护者的攻击。我一点也不羡慕他们,我真诚地希望更多的支持在路上。我还完全允许复制/编辑这段代码,并将其用作在 python 中处理 yaml 的文档。至少,我希望这篇文章至少对那些希望将有限的、粗糙的注释写入 yaml 文件的开发人员有用,并避免其他人通过反复试验对源代码进行逆向工程。
## 资源:
stack overflow:[https://stack overflow . com/questions/40704916/how-to-insert-a-comment-line-to-YAML-in-python-using-ruamel-YAML](https://stackoverflow.com/questions/40704916/how-to-insert-a-comment-line-to-yaml-in-python-using-ruamel-yaml)
my binder . org:[https://my binder . org/v2/gist/Alexis wl/7 Fe 1 B3 a 90d 86313 c 9785992 BD 0 ce 6 e 19/HEAD](https://mybinder.org/v2/gist/alexiswl/7fe1b3a90d86313c9785992bd0ce6e19/HEAD)
ruamel.yaml 文档:[https://yaml.readthedocs.io/en/latest/](https://yaml.readthedocs.io/en/latest/)
注释标记对象结构:
* CommentedMap:
0:用`yaml_set_start_comment`方法设置的注释。
1:用`before`参数从`yaml_set_comment_before_after_key`方法设置的注释。
2:来自`yaml_set_eol_comment`
的注释集 3:来自带`after`参数的`yaml_set_comment_before_after_key`方法的注释集。
* CommentedSeq:
0:从`yaml_set_eol_comment`
设置的注释 1:从带有`before`参数的`yaml_set_comment_before_after_key`方法设置的注释。
2:未使用
3:未使用
## 参考资料:
[1]:[https://yaml.org/about.html](https://yaml.org/about.html)
【2】:[https://docs . AWS . Amazon . com/AWS cloudformation/latest/user guide/template-formats . html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-formats.html)
【3】:[https://www . commonwl . org/user _ guide/02-1st-example/index . html](https://www.commonwl.org/user_guide/02-1st-example/index.html)
【4】:[https://www.home-assistant.io/docs/configuration/yaml/](https://www.home-assistant.io/docs/configuration/yaml/)
【5】:[https://sourceforge.net/p/ruamel-yaml/tickets/400/](https://sourceforge.net/p/ruamel-yaml/tickets/400/)
# 用 Ray 编写您的第一个分布式 Python 应用程序
> 原文:<https://towardsdatascience.com/writing-your-first-distributed-python-application-with-ray-4248ebc07f41?source=collection_archive---------10----------------------->

Ray 让并行和分布式计算更像你所希望的那样工作
[Ray](https://docs.ray.io/en/master/) 是一个快速、简单的分布式执行框架,可以轻松扩展您的应用程序并利用最先进的机器学习库。使用 Ray,您可以将按顺序运行的 Python 代码转换成分布式应用程序,只需对代码进行最少的修改。
本教程的目标是探索以下内容:
* 为什么要用 Ray 并行化和分布
* 如何开始使用 Ray
* 分布式计算中的权衡(计算成本、内存、I/O 等)
# 为什么要用 Ray 并行化和分布式?
正如[之前的一篇文章指出的那样](/modern-parallel-and-distributed-python-a-quick-tutorial-on-ray-99f8d70369b8),并行和分布式计算是现代应用的主要部分。问题是,获取现有的 Python 代码并试图并行化或分发它可能意味着重写现有的代码,有时是从头开始。此外,现代应用程序具有现有模块如[多处理](https://docs.python.org/3/library/multiprocessing.html)所缺乏的要求。这些要求包括:
* 在多台机器上运行相同的代码
* 构建具有状态并能通信的微服务和参与者
* 优雅地处理机器故障和抢占
* 大型对象和数字数据的高效处理
Ray 库满足了这些要求,并允许您在不重写应用程序的情况下扩展应用程序。为了使并行和分布式计算变得简单,Ray 采用函数和类,并将它们作为任务和参与者转换到分布式环境中。本教程的其余部分将探讨这些概念,以及在构建并行和分布式应用程序时需要考虑的一些重要事项。

虽然本教程探索了 Ray 如何简化普通 Python 代码的并行化,但需要注意的是,Ray 及其生态系统也简化了对 [python 代码](https://www.anyscale.com/blog/parallelizing-python-code)以及现有库(如 [scikit-learn](https://medium.com/distributed-computing-with-ray/how-to-speed-up-scikit-learn-model-training-aaf17e2d1e1) 、 [XGBoost](https://www.anyscale.com/blog/distributed-xgboost-training-with-ray) 、 [LightGBM](https://www.anyscale.com/blog/introducing-distributed-lightgbm-training-with-ray) 、 [PyTorch](https://medium.com/pytorch/getting-started-with-distributed-machine-learning-with-pytorch-and-ray-fd83c98fdead) 等)的并行化。迈克尔·加拉尼克拍摄的图片。
# 如何开始使用 Ray
## 将 Python 函数转换为远程函数(Ray 任务)
可以通过 pip 安装 Ray。
```
pip install 'ray[default]'
```
让我们通过创建一个光线任务来开始我们的光线之旅。这可以通过用`@ray.remote`修饰一个普通的 Python 函数来实现。这将创建一个任务,可以跨笔记本电脑的 CPU 内核(或 Ray cluster)进行调度。
考虑下面两个生成斐波纳契数列的函数(整数数列的特征是前两个数字之后的每个数字都是前两个数字之和)。第一个是普通的 python 函数,第二个是 Ray 任务。
```
**import** os
**import** time
**import** ray
# Normal Python
**def** **fibonacci_local**(sequence_size):
fibonacci = []
**for** i **in** range(0, sequence_size):
**if** i < 2:
fibonacci.append(i)
**continue**
fibonacci.append(fibonacci[i-1]+fibonacci[i-2])
**return** sequence_size
# Ray task
@ray.remote
**def** **fibonacci_distributed**(sequence_size):
fibonacci = []
**for** i **in** range(0, sequence_size):
**if** i < 2:
fibonacci.append(i)
**continue**
fibonacci.append(fibonacci[i-1]+fibonacci[i-2])
**return** sequence_size
```
关于这两个函数,有一些事情需要注意。首先,除了`fibonacci_distributed`函数上的`@ray.remote`装饰器之外,它们是相同的。
第二个要注意的是小的返回值。它们返回的不是斐波那契数列本身,而是数列的大小,它是一个整数。这一点很重要,因为它可能会通过将分布式函数设计为需要或返回大量数据(参数)来降低其价值。工程师通常称之为分布式函数的输入/输出(IO)。
## 比较本地与远程性能
本节中的函数将允许我们比较本地和并行生成多个长 Fibonacci 序列需要多长时间。值得注意的是,下面的两个函数都利用了返回系统中 CPU 数量的`os.cpu_count()`。
```
os.cpu_count()
```

本教程中使用的机器有八个 CPU,这意味着下面的每个函数将生成 8 个斐波那契数列。
```
# Normal Python
**def** **run_local**(sequence_size):
start_time = time.time()
results = [fibonacci_local(sequence_size) **for** _ **in** range(os.cpu_count())]
duration = time.time() - start_time
print('Sequence size: {}, Local execution time: {}'.format(sequence_size, duration))
# Ray
**def** **run_remote**(sequence_size):
# Starting Ray
ray.init()
start_time = time.time()
results = ray.get([fibonacci_distributed.remote(sequence_size) **for** _ **in** range(os.cpu_count())])
duration = time.time() - start_time
print('Sequence size: {}, Remote execution time: {}'.format(sequence_size, duration))
```
在了解`run_local`和`run_remote`的代码如何工作之前,让我们运行这两个函数,看看在本地和远程生成多个 100000 的斐波那契数列需要多长时间。
```
run_local(100000)
run_remote(100000)
```

`run_remote`功能在多个 CPU 上并行执行计算,从而缩短了处理时间(1.76 秒对 4.20 秒)。
## 射线 API
为了更好地理解为什么`run_remote`更快,让我们简要回顾一下代码,并解释一下 Ray API 是如何工作的。

`ray.init()`命令启动所有相关的光线进程。默认情况下,Ray 为每个 CPU 内核创建一个工作进程。如果您想在一个集群上运行 Ray,您需要传递一个类似于`ray.init(address='insertAddressHere')`的集群地址。

```
fibonacci_distributed.remote(100000)
```

调用`fibonacci_distributed.remote(sequence_size)`立即返回一个未来,而不是函数的返回值。实际的功能执行将在后台进行。因为它立即返回,所以每个函数调用可以并行执行。这使得生成多个 100000 长的斐波那契数列花费的时间更少。

`ray.get`当任务完成时,从任务中检索结果值。
最后,需要注意的是,当调用`ray.init()`的进程终止时,Ray 运行时也会终止。注意,如果你尝试运行`ray.init()`不止一次,你可能会得到一个 RuntimeError(也许你不小心调用了 ray.init 两次?).这可以通过使用`ray.shutdown()`解决
```
# To explicitly stop or restart Ray, use the shutdown API
ray.shutdown()
```
## 射线仪表板
在您调用了`ray.init`函数之后,Ray 附带了一个可以在 http://127.0.0.1:8265 上找到的仪表板。
在[和](https://docs.ray.io/en/master/ray-dashboard.html#ray-dashboard)其他功能中,仪表盘可以让您:
* 了解光线内存利用率并调试内存错误。
* 查看每个参与者的资源使用情况、执行的任务、日志等。
* 查看集群指标。
* 杀死演员和侧写你的射线工作。
* 一目了然地查看错误和异常。
* 在单个窗格中查看多台计算机的日志。
* 参见[射线调](https://docs.ray.io/en/master/tune/index.html)工作岗位和试用信息。
下面的仪表板显示了运行`run_remote(200000)`后每个节点和每个工作人员的资源利用率。注意仪表板如何显示在每个工人中运行的函数 fibonacci_distributed。在分布式函数运行时观察它们是一个好主意。这样,如果您看到一个工人在做所有的工作,那么您可能没有正确地使用 ray.get 函数。此外,如果您看到您的总 CPU 利用率接近 100%,您可能做得太多了。

# 分布式计算中的权衡
本教程使用斐波那契数列,因为它们提供了几个调整计算和 IO 的选项。您可以通过增加和减少序列大小来改变每个函数调用所需的计算量。序列越大,生成序列所需的计算就越多,而序列越小,所需的计算就越少。如果你分配的计算量太小,那么 Ray 的开销将支配总的处理时间,并且你不会从分配我们的函数中得到任何价值。
分配功能时,IO 也是必不可少的。如果您修改这些函数来返回它们计算的序列,IO 将随着序列大小的增加而增加。在某种程度上,传输数据所需的时间将支配完成对分布式函数的多次调用所需的总时间。如果您将功能分布在一个集群上,这一点很重要。这需要使用网络,网络调用比本教程中使用的进程间通信开销更大。
因此,建议您尝试使用分布式斐波那契函数和局部斐波那契函数。尝试确定从远程功能中获益所需的最小序列大小。一旦你搞清楚了计算,试着看看 IO 对整体性能会有什么影响。无论您使用什么工具,分布式架构在不需要移动大量数据的情况下工作得最好。
幸运的是,Ray 的一个主要优点是能够远程维护整个对象。这有助于缓解 IO 问题。接下来让我们来看看。
# 作为参与者的远程对象
就像 Ray 将 Python 函数作为任务翻译到分布式设置一样,Ray 将 Python 类作为参与者翻译到分布式设置。Ray 提供了 actors,允许您并行化一个类的实例。就代码而言,您需要添加到 Python 类中的只是一个`@ray.remote`装饰器,使它成为一个参与者。当您创建该类的一个实例时,Ray 会创建一个新的 actor,它是一个在集群中运行并保存对象副本的进程。
因为它们是远程对象,所以它们可以保存数据,并且它们的方法可以操作这些数据。这有助于减少进程间的通信。如果您发现自己编写了太多返回数据的任务,而这些数据又被发送到其他任务,那么可以考虑使用 actor。
现在让我们看看下面的演员。
```
**from** collections **import** namedtuple
**import** csv
**import** tarfile
**import** time
**import** ray
@ray.remote
**class** **GSODActor**():
**def** **__init__**(self, year, high_temp):
self.high_temp = float(high_temp)
self.high_temp_count = None
self.rows = []
self.stations = None
self.year = year
**def** **get_row_count**(self):
**return** len(self.rows)
**def** **get_high_temp_count**(self):
**if** self.high_temp_count **is** None:
filtered = [l **for** l **in** self.rows **if** float(l.TEMP) >= self.high_temp]
self.high_temp_count = len(filtered)
**return** self.high_temp_count
**def** **get_station_count**(self):
**return** len(self.stations)
**def** **get_stations**(self):
**return** self.stations
**def** **get_high_temp_count**(self, stations):
filtered_rows = [l **for** l **in** self.rows **if** float(l.TEMP) >= self.high_temp **and** l.STATION **in** stations]
**return** len(filtered_rows)
**def** **load_data**(self):
file_name = self.year + '.tar.gz'
row = namedtuple('Row', ('STATION', 'DATE', 'LATITUDE', 'LONGITUDE', 'ELEVATION', 'NAME', 'TEMP', 'TEMP_ATTRIBUTES', 'DEWP',
'DEWP_ATTRIBUTES', 'SLP', 'SLP_ATTRIBUTES', 'STP', 'STP_ATTRIBUTES', 'VISIB', 'VISIB_ATTRIBUTES',
'WDSP', 'WDSP_ATTRIBUTES', 'MXSPD',
'GUST', 'MAX', 'MAX_ATTRIBUTES', 'MIN', 'MIN_ATTRIBUTES', 'PRCP',
'PRCP_ATTRIBUTES', 'SNDP', 'FRSHTT'))
tar = tarfile.open(file_name, 'r:gz')
**for** member **in** tar.getmembers():
member_handle = tar.extractfile(member)
byte_data = member_handle.read()
decoded_string = byte_data.decode()
lines = decoded_string.splitlines()
reader = csv.reader(lines, delimiter=',')
# Get all the rows in the member. Skip the header.
_ = next(reader)
file_rows = [row(*l) **for** l **in** reader]
self.rows += file_rows
self.stations = {l.STATION **for** l **in** self.rows}
```
上面的代码可用于加载和操作公共数据集中的数据,该数据集称为全球地表日摘要(GSOD)。该数据集由美国国家海洋和大气管理局(NOAA)管理,可以在他们的[网站](https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/)上免费获取。NOAA 目前维护着全世界 9,000 多个站点的数据,GSOD 数据集包含这些站点的每日汇总信息。从 1929 年到 2020 年,每年都有一个 gzip 文件。本教程只需要下载 [1980](https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/1980.tar.gz) 和 [2020](https://www.ncei.noaa.gov/data/global-summary-of-the-day/archive/2020.tar.gz) 的文件。
这个 actor 实验的目标是计算 1980 年和 2020 年有多少读数是 100 度或更高,并确定 2020 年是否比 1980 年有更多的极端温度。为了实现公平的比较,应该只考虑在 1980 年和 2020 年都存在的站。这个实验的逻辑是这样的:
* 加载 1980 年的数据。
* 加载 2020 年数据。
* 获取 1980 年存在的电台列表。
* 获取 2020 年存在的站点列表。
* 确定站点的交叉点。
* 获取 1980 年期间站点交叉点 100 度或更大的读数。
* 获取 2020 年期间站点交叉点 100 度或更大的读数。
* 打印结果。
问题是这个逻辑完全是顺序的;一件事只会接二连三地发生。有了 Ray,很多这种逻辑可以并行完成。
下表显示了一个更具并行性的逻辑。

以这种方式写出逻辑是一种很好的方式,可以确保以并行的方式执行所有的事情。下面的代码实现了这个逻辑。
```
# Code assumes you have the 1980.tar.gz and 2020.tar.gz files in your current working directory.
**def** **compare_years**(year1, year2, high_temp):
"""if you know that you need fewer than the default number
of workers, you can modify the num_cpus parameter"""
ray.init(num_cpus=2)
# Create actor processes
gsod_y1 = GSODActor.remote(year1, high_temp)
gsod_y2 = GSODActor.remote(year2, high_temp)
ray.get([gsod_y1.load_data.remote(), gsod_y2.load_data.remote()])
y1_stations, y2_stations = ray.get([gsod_y1.get_stations.remote(),
gsod_y2.get_stations.remote()])
intersection = set.intersection(y1_stations, y2_stations)
y1_count, y2_count = ray.get([gsod_y1.get_high_temp_count.remote(intersection),
gsod_y2.get_high_temp_count.remote(intersection)])
print('Number of stations in common: {}'.format(len(intersection)))
print('{} - High temp count for common stations: {}'.format(year1, y1_count))
print('{} - High temp count for common stations: {}'.format(year2, y2_count))
#Running the code below will output which year had more extreme temperatures
compare_years('1980', '2020', 100)
```

关于上面的代码,有几件重要的事情需要提及。首先,将`@ray.remote` decorator 放在类级别使得所有的类方法都可以被远程调用。第二,上面的代码利用了两个 actor 进程(`gsod_y1`和`gsod_y2`),它们可以并行执行方法(尽管每个 actor 一次只能执行一个方法)。这使得能够同时加载和处理 1980 年和 2020 年的数据。
# 结论
Ray 是一个快速、简单的分布式执行框架,可以轻松扩展您的应用并利用最先进的机器学习库。本教程展示了如何使用 Ray 轻松地获取现有的顺序运行的 Python 代码,并通过最少的代码更改将其转换为分布式应用程序。虽然这里的实验都是在同一台机器上进行的, [Ray 还可以让你轻松地在每一个主要的云提供商上扩展你的 Python 代码](/how-to-scale-python-on-every-major-cloud-provider-5e5df3e88274)。如果你有兴趣了解更多关于 Ray 的信息,请在 GitHub 上查看 [Ray 项目,在 twitter](https://github.com/ray-project/ray) 上关注 [@raydistributed,并注册](https://twitter.com/raydistributed) [Ray 简讯](https://anyscale.us5.list-manage.com/subscribe?u=524b25758d03ad7ec4f64105f&id=d94e960a03)。
*原载于* [*Anyscale 的博客*](https://www.anyscale.com/blog/writing-your-first-distributed-python-application-with-ray) *。*
# 书面交流:你需要的另一项数据科学技能
> 原文:<https://towardsdatascience.com/written-communication-the-other-data-science-skill-you-need-f89b2063923c?source=collection_archive---------22----------------------->
## 我的数据科学生涯中有 12 个例子

格伦·卡斯滕斯-彼得斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
没有一种数据科学技能像交流一样有价值。如果您能够在数据集上使用最新的 NLP transformer,或者您有一个新奇的可视化来以新的方式显示您的数据,那就太好了。这些都是了不起的成就!但是,如果您不能传达该转换器背后的结果的价值,该怎么办呢?如果你的观众不能理解你的视觉效果,那该怎么办?如果你不能传达这种价值,这些技术除了提供实践和潜在的未来经验之外就没什么用了。

照片由[莎伦·麦卡琴](https://unsplash.com/@sharonmccutcheon?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄
在这篇文章中,我将重点讨论书面交流。如果你还没有涉足这个领域,并且考虑到当前疫情的局限性,这是两者中比较容易练习的一个(如果你将来读到这篇文章,希望它已经结束了)。我将通过我自己的数据科学职业生涯中的 12 个例子来说明与您进行书面交流的必要性。其中一些你可以不用工作就开始练习。所以让我为你打开门,开始浏览它们吧!

Matthew T Rader 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# 非正式交流
这是任何涉及快速消息的通信,例如通过 Google Chat 或 Slack。这个看起来简单明了。但是如果你开始一个新的职位,一开始会很困难。如果我不够直接,我将不得不在附加信息中重新解释我的意思。如果不尽快纠正,这会浪费不必要的时间。
# 数据请求
并非所有的数据都能方便地为您所用。您将不得不从团队之外的分支机构的其他人那里获取数据。尤其是如果你在一个大型组织中工作。你需要清楚地表达这一要求,并带着尊重。如果是和队友,我通常会使用非正式的交流方式。除此之外,我会发一封电子邮件问候他们,数据请求,以及我为什么要这些数据。
# 代码注释
对于您选择的编码语言,应该遵循注释的约定。一般来说,你应该在你的代码块或者每个 Jupyter 笔记本单元上留下评论。这不仅仅是为了让别人理解你的代码。这是为了**未来的你**理解你的代码。我已经编写了需要每几个月重用一次或者每年重用一次的代码。在这段时间里,你会经历太多的事情,以至于你会忘记代码的大部分功能。注释对帮助你记住旧代码的目的很有帮助。
# Jupyter 笔记本中的笔记
评论并不能取代对你的分析或训练你的机器学习模型的过程进行笔记的需要。这不方便。我使用 Jupyter 笔记本的降价功能来:
* 笔记本标题
* 在笔记本中创建任务和回答的问题的简短摘要
* 基于任务创建部分
* 做笔记明确回答任务中的问题
# 分析报告
我不会为某一类读者写我的分析报告。我的同事中有数据科学技能比我强的,也有几乎没有技术技能的。我必须适应每个观众,你也应该。这意味着决定何时保留技术细节,何时扩展它们。利用你对他们的[学习](https://www.missouristate.edu/assets/busadv/p.30.pdf)和[工作](https://www.spencerjamesgroup.com/blog/4-employee-personalities-and-how-they-respond-in-the-workplace)风格的了解来帮助他们。
# 图表文本
不要创建没有标题或轴标签的图表!这些是帮助别人理解你的图表所讲述的故事所需要的最少的文字。这不仅对你的观众,而且对你未来的自己都有帮助。你解释得越少越好。同样的道理也适用于你在多大程度上依赖于回忆和参考你所需要的东西。

由[活动创作者](https://unsplash.com/@campaign_creators?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# 项目提案
在开始一个新项目之前,我们必须创建一个我们称之为目的和价值的声明。在此声明中,我们包括项目的潜在用户和描述。在最终评审之前,如有必要,该提案将经过修改。我发现,如果这些描述在开始时尽可能清晰,我将更快地完成审查过程,并且不必处理修订带来的延迟。
# 项目概览页面
对于所有项目,我们需要创建一个文档,提供项目概述以及补充资源的链接。组织本文档的文本是帮助任何人了解项目并理解项目目的的关键。这又是一个你在帮助未来的你的例子。我曾多次查阅这些链接的项目资源,以找到我在该项目中使用的数据源来帮助完成其他项目。
# 登记文件
我每周都和我的主管进行检查。这给了我们一个机会来讨论我做得怎么样,解决当前的问题,讨论我正在做的项目的进展,以及未来的问题/项目。每次开会前,我都会整理一份微软 Word 文档摘要,以特定的结构列出我想要讨论的所有内容。这是在入住期间与我的主管分享的。有了这份文件,我的主管可以很快看到我想要讨论的内容。它帮助我同时跟踪我想要讨论的项目,而没有从记忆中回忆一切和潜在遗漏某些东西的负担。
# 对非技术风险承担者的解释
正如我之前提到的,你将为那些没有数据科学背景的人写作。我们必须经常向非技术利益相关者解释数据、分析结果、产品特性和产品变更。这有时是通过电子邮件完成的。你必须能够以他们能够理解的方式提供这些解释。通常,我们试图在简短的回应和足够高层次的信息之间取得平衡,以解释利益相关者需要知道什么。再次迎合学习和工作方式的假设。
# 报告
演示文稿是你想把你的写作保持在一个极端最小的地方。话越少越好。您的观众会想阅读您幻灯片上的任何内容。如果有太多的东西让他们去读,他们会把注意力集中在他们正在读的东西上,并间接地屏蔽掉你在给定幻灯片上所说的内容。陈述的重点应该是你要说的内容。在这种情况下,不要让话语控制你的听众。

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [Austin Distel](https://unsplash.com/@austindistel?utm_source=medium&utm_medium=referral) 拍摄的照片
# 用户指南
我们为自己的产品创建用户指南。这些用户可能有不同的技术背景。我们可以在了解受众背景的基础上决定如何编写用户指南。例如,你不会像为数据管理员那样为教师编写用户指南。
# 如何练习书面交流?
即使你还没有进入这个领域,你仍然可以练习这些技巧。您可以:
* 在写下你要向观众展示的任何东西之前,先了解你的观众。这可以像一篇博客文章一样简单。
* 在你目前的职位上找一个你需要写分析的项目。然后与目标受众分享,检查他们的理解程度。
* 让一个家庭成员或朋友阅读你做的任何文章或分析。根据他们的技术理解写下分析或项目概述,然后让他们阅读并询问他们理解了多少以及为什么。
# 最后的想法
写作是一种技能,就像学习如何使用 Python 以及何时使用特定的机器学习模型一样。你需要坚持做下去才能看到进步。我也还在努力。我唯一的优势是我的教学背景,在那里我学会了根据学习风格来迎合特定的受众。但是在进入数据科学领域之前,您也将能够做到这一点并进行实践。你能行的!
感谢你阅读这篇文章。如果你想阅读类似的帖子,请查看我下面的更多内容:
</why-focus-is-key-for-your-data-science-journey-b62715b2a1c> </11-tips-for-you-from-my-data-science-journey-df884faa9f3> </a-potential-data-science-foundation-for-math-backgrounds-188b03b9f1ff>
如果你有任何问题,请随时在 Linkedin 和 Twitter 上发表评论或联系我们。对 Twitter 上的 DM 开放。
下次见!
约翰·德杰苏斯
# 聚类算法和主题建模终极指南
> 原文:<https://towardsdatascience.com/wthe-ultimate-guide-to-clustering-algorithms-and-topic-modeling-4f7757c115?source=collection_archive---------14----------------------->

戴维·巴洛在 [Unsplash](https://unsplash.com/s/photos/unsupervised-machine-learning?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
## 第 1 部分:K 均值初学者指南
聚类是最常用的**无监督机器学习**算法之一。您可以将聚类视为将未组织的数据点放入不同的类别,以便您可以了解更多关于数据结构的信息。聚类在从没有标签的数据中提取信息方面有多种应用。例如,公司根据客户的特征(如购买行为)对客户进行分类,以便更好地开展市场活动,制定定价策略以获取更多利润,等等。聚类算法也广泛用于自然语言处理(NLP)中,以从非结构化文本数据中提取信息,主题建模就是一个例子。
本系列文章旨在为读者提供两种常见但非常不同的聚类算法 **K 均值**和**潜在狄利克雷分配(LDA)**及其在主题建模中的应用:
> 第 1 部分:K-Means 初学者指南(本文)
>
> [第 2 部分:LDA 初学者指南](/the-ultimate-guide-to-clustering-algorithms-and-topic-modeling-3a65129df324)
>
> 第 3 部分:使用 K-Means 和 LDA 进行主题建模(即将推出)
在本文中,我将介绍应用 K-Means 算法的细节。
## 什么是 K-Means
K-Means 是最简单的聚类算法之一,用于检测无组织数据点中的常见模式。该算法通过基于相似性识别接近的数据点,将所有数据点分类为 K 个聚类。K-Means 简单地将相似性定义为在特征空间中测量的欧几里德距离。为了便于说明,使用下面的代码,我生成了三个二维的正态分布的点簇:
```
import numpy as np
import matplotlib.pyplot as plt#generate random data samples from known clusters
x = np.random.normal(0, 0.5, (100, 2)) + np.array([0, 1.2])
y = np.random.normal(0, 0.5, (100, 2)) + np.array([-0.7, -0.7])
z = np.random.normal(0, 0.5, (100, 2)) + np.array([0.7, -0.7])plt.scatter(*x.T)
plt.scatter(*y.T)
plt.scatter(*z.T)
plt.title(‘Data Example with Only Two Features’)
plt.xlabel(‘Feature 1’)
plt.ylabel(‘Feature 2’);
```
下图显示了*手动生成的*三组数据。由于样本数据只有两个特征,我们可以清楚地看到二维图形中哪些点彼此接近。在实际应用中,当特征空间增加到二维以上时,同样的直觉成立。

作者图片
为了应用 K-Means,研究人员首先需要确定聚类的数量。然后,该算法将把每个样本分配到离聚类中心的距离最小的聚类中。代码很简单:
```
from sklearn.cluster import KMeansdata = np.vstack((x,y,z))
km = KMeans(n_clusters=3)
km.fit(data)
```
在这里,由于我们生成了数据,我们知道有三个集群。当你对数据知之甚少的时候,在实际应用中呢?本文将在介绍 K-Means 算法的数学细节后讨论这个问题的答案。
## **K-Means 背后的数学**
K-Means 算法预先确定聚类数 K,然后分配一个聚类集合 C = {C1,C2,…Ck},使:

其中𝜇𝑘是群集 Ck 的点的中心:

该算法的工作原理如下:
1. 指定 K 个簇的数量。
2.用随机值初始化每个聚类的中心点𝜇𝑘 (k ∈ K)。
3.计算每个数据点到每个聚类中心点的平方欧几里德距离。

数据点和聚类中心之间的欧几里德距离
4.将 X_j 分配给欧氏距离平方最小的最近聚类 k:

5.通过取分配给聚类 k 的样本点的平均值来更新𝜇𝑘
6.重复步骤 3 至 5,直到收敛。
请注意,迭代步骤降低了目标函数,并且只有有限数量的点的可能分区,因此该算法保证收敛。然而,收敛的解决方案可能不是全局最优的。默认情况下,`K-Means`使用不同的质心种子运行聚类算法十次,并取得度量中的最佳结果。
## **K 均值指标**
如何确定像 K-means 这样的聚类模型的拟合优度?这个问题的答案对于在迭代步骤中找到最适合的模型是很重要的,并且在帮助研究人员决定聚类的数量方面起着重要的作用。聚类算法最常用的度量是**惯性**和**轮廓**。
**惯性**
惯性测量从每个数据点到其最终聚类中心的距离。对于每个集群,惯性由每个数据点 X_j ∈ Ck 和中心𝜇𝑘:之间的均方距离给出

对所有聚类的惯性求和后,总惯性用于比较不同 K 均值模型的性能:

由惯性的定义可知,我们需要选择 ***最小化*** 总惯性的模型。
**轮廓系数**
轮廓系数是可以在聚类算法中使用的另一个度量。对于每个数据点 X_j,它计算 X_j 和相同聚类中所有其他点之间的平均距离。我们将其定义为 a_j。然后,它会寻找 X_j 的下一个最近的聚类(对 X_j 进行分类的第二好的聚类),并计算 X_j 与该聚类中所有点之间的平均距离作为 b_j。点 X_j 的轮廓系数为:

从上面的等式中,我们可以看到轮廓系数在-1 和 1 之间,取决于 a_j 和 b_j 中哪一个更大:

如果 b_j 大于 a_j,说明模型已经将 X_j 聚类到最佳聚类中。与 a_j 相比,b_j 越大,集群越好。否则,当 a_j 较大时,意味着该点可能在另一个簇中。a_j 与 b_j 相比越大,值越接近-1。
轮廓系数给出了当计算聚类中所有数据点的平均值时每个聚类紧密程度的信息。当计算所有数据点的平均轮廓系数时,它测量模型性能。
## **如何选择合适的集群数?**
不幸的是,我们不能解析地解决这个问题,但是在确定 K 时遵循一些一般规则是有用的。K 的数目在数学上和实际上都是确定的。为了提供最佳模型,我们可以从 K 的不同选择中计算惯性,并选择最有效的一个。这时候肘部曲线就派上用场了。肘形曲线描绘了不同 K 的惯性。注意,随着 K 的增加,惯性总是减小。如果 K 等于总数据点的数量,惯性将为零(每个数据点是一个聚类)。我们可以使用肘曲线来检查下降速度,并选择在**肘点**处的 K,当过了这个点后,惯性下降得相当慢。
使用上面生成的数据点和下面的代码,我们可以绘制肘形曲线:
```
inertias = []for n_clusters in range(2, 15):
km = KMeans(n_clusters=n_clusters).fit(data)
inertias.append(km.inertia_)
plt.plot(range(2, 15), inertias, ‘k’)
plt.title(“Inertia vs Number of Clusters”)
plt.xlabel(“Number of clusters”)
plt.ylabel(“Inertia”);
```
情节是这样的:

肘部曲线:作者图片
该图显示惯性随着 K 的增加而不断减小。当 K = 3 时,惯性下降速度有一个转折点,这就是我们要寻找的**拐点**。请注意,当我们生成数据时,我们使用三种正态分布。因此,它与基于肘部曲线的结果相匹配。
我们还需要考虑使用肘形曲线决定的聚类数是否可行。我们应该选择易于解释和实际可行的 K。例如,如果您的公司只有资源(劳动力和资本)来将客户分为三类,那么您应该将 K 设置为 3,而不考虑肘曲线的建议。
## 在应用 K 均值之前
为了获得更好的模型性能和更可靠的结果,我们需要用至少两个步骤对特征进行预处理:缩放和降维。
**秤的特点**
由于 K-Means 使用数据点之间的欧氏距离来定义聚类,因此所有要素都应进行缩放,以便其单位具有可比性。否则,来自不可比单位的某些特征差异可能会使结果产生偏差。我们可以用 scikit-learn 的`StandardScaler` transformer 来缩放数据。它将每个要素以其平均值为中心,并通过除以该要素的标准差对其进行缩放。
**执行降维**
正如您可能注意到的,聚类算法在计算上很复杂,并且复杂性随着特征的数量而快速增加。因此,在应用 K-Means 聚类算法之前降低数据的维数是非常常见的。
**主成分分析(PCA)** 是一种广泛使用的算法,用于减少数据中的特征数量,同时保留尽可能多的信息。更多细节你可以参考[这份文件](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html)。
K-Means 是最简单和最流行的聚类算法,有多种使用案例。本文重点介绍它的数学细节、它使用的度量标准以及应用它时的建议。在接下来的文章中,我将介绍另一种聚类算法 LDA,以及 K-Means 和 LDA 在主题建模中的应用。
感谢您的阅读!这是我所有博客帖子的列表。如果你感兴趣,可以去看看。
<https://zzhu17.medium.com/my-blog-posts-gallery-ac6e01fe5cc3> <https://zzhu17.medium.com/membership>
# X% HR + Y%分析师=成功的 HR 分析师
> 原文:<https://towardsdatascience.com/x-hr-y-analyst-successful-hr-analyst-96db7c39c9c8?source=collection_archive---------15----------------------->
## 平衡人力和分析专业知识,成为有影响力的贡献者

[艾琳娜](https://unsplash.com/@miracleday)在 [unsplash](https://unsplash.com/) 上的照片
这篇文章的灵感来自一位数据爱好者对我的[上一篇文章](/into-the-world-of-people-analytics-18580ff37e29?sk=a1a535f307669b3182534191ffad5322)的评论,表明理解“人力资源”对于在人员分析中取得成功也是至关重要的。我想了想,是的,我完全同意!如果你对人员分析很感兴趣,但由于缺乏人力资源背景而止步不前,这篇文章根据个人经验提供了一些建议,帮助你树立正确的观点,平稳过渡,融入社会。这是一个学习的旅程,但绝对可行!
## 关于人力资源(人员)分析师的启示

照片由[杰里米](https://unsplash.com/@jeremybishop)在 [unsplash](https://unsplash.com/) 上拍摄
> 人员分析师来源
在深入研究之前,值得考虑的是,今天的人员分析师来自哪里?鉴于这是市场中的一个利基、新的和即将到来的领域,今天的人员分析师可能会从其他现有角色中产生,这取决于组织为解决其业务问题而寻求的特定专业知识等因素。因此,人员分析师是从其他现有角色中诞生的,如业务分析师、数据分析师,甚至是恰好擅长数据的核心人力资源专业人员。这里的核心人力资源专业人员只是属于人力资源组织的员工,嵌入在人力资源卓越中心(center of excellence,或者换句话说,人力资源职能部门)中,如招聘、业务合作等。,有解释数字的诀窍。
尽管人力资源分析方面有专门的学习途径/认证,但公司很少在招聘人力分析师的工作描述中强制要求这样做,这表明从其他领域转移技能是完全(仍然)可以接受的。这些转岗的人可能是最能适应应用他们的核心分析专业知识的人,可以立即使人力资源组织受益,从而打开这个领域的大门。不管分析师可能已经掌握了哪些技术知识,或许在人物分析中产生影响的最有价值的技能是提高讲故事的艺术。

照片由 [Suzy](https://www.pexels.com/@suzyhazelwood) 在[像素](https://www.pexels.com/)上拍摄
> 为什么讲故事会让你成为人物分析中的超级英雄
我们可能知道,在这种情况下,讲故事基本上是理解被分析的数字,从结果中产生一个故事,并以解决分析师要解决的业务问题的方式进行推销。相信我,在人物分析中;这并不容易。仅仅是因为在大多数时候向非技术型人力资源受众传达故事的技能。这并不奇怪,因为人员分析师通常不会向数据工程或数据科学等技术职能部门汇报。出于各种原因,他们通常会在人力资源部门内部进行报告,比如,首先,你要处理的是员工的敏感数据,这些数据只供人力资源部门查看。其次,您的项目干系人大多是来自不同 HR COEs 的 HR 专业人员。这就是人力资源领域的知识非常重要的地方。想象一下,通过支持他们的人力资源团队的眼睛来密切了解业务将是多么全面。这一点,再加上你的分析专长,将会产生最有影响力的讲故事的超能力。
## 从外向内向转变

迪伦在 [unsplash](https://unsplash.com/) 上的照片
> 它是如何发生在我身上的
在进入人力资源部门之前,我有工程教育和工程数据分析的专业背景。我有机会在不同的 Coe(招聘、全球流动和业务合作)中定位我的人力资源工作。所有这些都让我能够理解这些领域为企业解决了什么样的问题,以及分析的力量如何帮助做出数据驱动的决策。这需要时间,但是一旦你的观众开始感受到这种影响,转变就完成了。

由[拍摄的照片](https://unsplash.com/@krakenimages)在 [unsplash](https://unsplash.com/)
> 同样的方法如何帮助你
你可能像我一样,不是人力资源专业人员,但有分析方面的专业知识,或者相反,是核心人力资源专业人员,但没有分析方面的专业知识。无论哪种方式,如果你现在已经决定进入人员分析,那么这里有一些可能会有所帮助。这并不是什么新的建议——当一件事是新的,你需要额外努力去熟悉它,直到新的成为标准。为了有助于这种熟悉,我强烈建议将你自己嵌入(或密切合作)团队中,让你接触到缺失的专业知识,作为回报,这将使你成为一名有经验的人员分析师。只有在那里,这样做,与那些业务领先的人力资源团队一起解决业务问题,你才能真正看到你可以带来的增值,并真正成为一名“内部人员”
## 完成等式——X 和 Y 的理想值是多少?

由 unsplash 上的 Sanjiv 拍摄的照片
老实说,这里不可能有一个放之四海而皆准的标准。但我个人的信念是,要成为一名成功的人员分析师和有影响力的贡献者,就必须——Y > X,这意味着分析专业知识的分量更重。根据你从光谱的哪一端开始,非人力资源+核心分析师或核心人力资源+非分析师,Y 开始相应地更高或更低,并且可以调整。每个组织的需求、业务问题都可能不同,需要 X 或 Y 来主导或协调一致地最终解决手头的任务。因此,关键是要根据可用的变量进行思考,质疑未知,成为适应性强的“解决方案-变色龙”!
# x 射线图像分类和模型评估
> 原文:<https://towardsdatascience.com/x-ray-image-classification-and-model-evaluation-7810044c0ff5?source=collection_archive---------18----------------------->
## 基于张量流的胸部 x 光图像肺炎检测

图片来自 [Unsplash](https://unsplash.com/photos/DK8jXx1B-1c)
[Kaggle](https://www.kaggle.com/paultimothymooney/chest-xray-pneumonia) 拥有肺炎和正常病例胸部 x 光图像数据集的极好来源。正常 X 射线和受影响的 X 射线的图像之间存在显著差异。机器学习可以在确定疾病方面发挥关键作用,并显著缩短诊断时间,同时减少人工工作量。在本文中,我将遍历这个数据集,并以 90%的评估准确率对图像进行分类

作者图片
我被[在猫和狗之间的数据集上所做的工作所激励,并重用了数据集管道的代码块。首先,我们需要导入必要的包。](https://goodboychan.github.io/python/deep_learning/tensorflow-keras/vision/2020/10/16/01-Image-Classification-with-Cat-and-Dog.html)
初始数据集太大。我选择了一个精简集,对于正常情况有 1000 个图像,对于肺炎有 1000 个图像。将有三个目录:培训、验证和测试。验证和测试数据集对模型来说是全新的,我们不需要对这些数据集执行任何图像增强技术。
接下来导入几个样本。输出显示了两个目标病例的 X 射线图像。
我们将为数据集分配标签:0 代表正常,1 代表肺炎。这就是为什么我们会在模型编译中使用 SparseCategoricalCrossentropy()损失函数。该模型定义如下:
因为在每个目标文件夹中有相等数量的图像,所以通过将一个文件夹的长度乘以 2 来获得总的训练或验证或测试长度。下一部分是图像增强
我们将使用 train_datagen 和 validation_datagen 生成器以 20 个为一批来传输训练图像。
从[到这里](https://goodboychan.github.io/python/deep_learning/tensorflow-keras/vision/2020/10/16/01-Image-Classification-with-Cat-and-Dog.html)放置了一堆操作图像的函数。
这些功能会导致一些随机抖动,并有助于加载图像。下一个块将把标签分配给各个文件夹。如前所述,0 代表正常,1 代表肺炎。这适用于训练、验证和测试的所有三个文件夹。
每当在检查点观察到训练中的改进时,模型将被保存。
然后将使用该数据训练该模型。所有文件夹的长度都是预先获得的。
完成后,我们可以获得训练和验证损失和准确性。

作者图片

作者图片
显然,在 50 个时期之后,在数据集上的训练是极好的,因为准确度已经达到几乎 98%,但是验证没有改善太多。验证数据的准确度约为 75%。这种过度拟合可以通过删除模型中的数据点来避免。在这里,我将继续进行模型评估,该评估在测试数据集上显示了大约 90%的准确性。当然,所有这些数字都取决于图像,并且可能因验证和测试数据集而异。

作者图片
我们已经证明了在 DNN 使用张量流建立图像分类器。灰度胸部 x 光图像用于这种分类。初始训练数据集可以进一步扩展到包括所有图像。像我这样采用 core i-7 8550U 的本地 PC 平台需要 30 多分钟来完成 2000 幅图像数据集上 50 个时期的训练。支持 GPU 的 PC 将能够显著加快训练速度。
[Github 页面](https://mdsohelmahmood.github.io/2021/06/21/X-ray-image-classification.html)
# XAI 方法——简介
> 原文:<https://towardsdatascience.com/xai-methods-the-introduction-5b1b81427c9c?source=collection_archive---------19----------------------->
## 了解 XAI 方法
## 什么是 XAI 方法?可解释性与可解释性。模型可解释性的分类。有哪些归因方式?

图标来源: [*技术向量由乌克利亚克—www.freepik.com*创建 T3](https://www.freepik.com/vectors/technology)
# 可解释的人工智能
可解释的人工智能(XAI)是该领域最年轻和发展最快的分支之一。XAI 方法的目标是为人类可以理解的深度学习模型提供解释。这在医疗保健或安全等安全关键领域尤为重要。这些年来在文献中提出的方法通常承诺它们将为模型如何做出决策的问题提供明确的答案。
## 解释的权利
“解释权”是欧洲议会和理事会在[通用数据保护条例(GDPR)](https://op.europa.eu/en/publication-detail/-/publication/3e485e15-11bd-11e6-ba9a-01aa75ed71a1) 中使用的术语。这个术语经常在 XAI 方法中被提及,它需要一个数据控制器来解释这个机制是如何做出一个决定的。GDPR 的这一部分是为了防止使用人类无法解释的系统(如深度神经网络)而创建的。目标是避免此类系统中的歧视和道德/财务偏见。例如,我们可以使用自动信用评分系统。像这样的系统用于抵押贷款过程。法律禁止基于一系列特征歧视一个人,但这种歧视可能隐藏在一个暗箱系统中(即使该系统的创造者并不知情)。如果申请被银行拒绝,申请人可以要求银行解释拒绝的原因。这可能有助于在下一次申请前提高分数。
# 可解释性与可解释性
在机器学习的上下文中,没有一个关于可解释性和/或可解释性的正式定义,它经常被互换使用[【2】](https://arxiv.org/abs/1802.01933)。Arrieta 等人[【3】](https://arxiv.org/abs/1910.10045)区分两者,定义为:
> ***定义 1.1(可解释性)*** 模型上的被动特征是指人类观察者对模型内部决策过程的理解程度。
>
> ***定义 1.2(可解释)*** 一个模型的主动特征,与解释模型所采取的行动或程序的概念相关联,目的在于阐明其内部决策过程。
可解释人工智能(XAI)领域的名称指的是模型的特征,但是任何呈现给人类的表示(如输入属性)指的是模型的可解释性。
## 可解释性分类学

图 1:模型可解释性的分类。
有两种主要类型的模型:白盒模型和黑盒模型。第一种类型的可解释性被定义为内在的[【4】](http://www.cs.columbia.edu/~orb/papers/xai_survey_paper_2017.pdf)。这种类型的可解释性涵盖了所有具有可解释内部结构的模型。例如,决策树的结构被认为是可解释的,浅层神经网络的内部结构也是可解释的。这不适用于使用事后可解释性的深度神经网络。事后可解释性意味着我们试图解释模型的预测,而不解释该模型的确切内部机制。由于细胞神经网络的复杂性,事后可解释性是解释这类模型的唯一方法。
> ***备注。*** *这不是唯一的可解释性分类法。可解释性的结构可以用多种方式定义(根据目的、方法或应用程序)。*
## 与模型无关和特定于模型
如分类法所示(见图 1),事后可解释性分为模型不可知的和模型特定的[【5】](https://ieeexplore.ieee.org/document/8466590)。模型不可知方法是可以应用于任何黑盒模型的方法,而不需要考虑模型的内部结构。这些方法通常不太精确,但是因为它们只基于输入和输出来解释模型的行为。另一方面,特定于模型的方法与特定类型的模型相关联。“类型”的定义不严格,可以指整个领域,如 CNN 或 CNN 的特定架构。
# 归因方法
归因方法是一种事后方法(见*“模型不可知和模型特定”*)。顾名思义,归因方法将输入要素归因于给定的预测。它可以定义为:
> ***定义 2.1(归属方法)*** *给定一个输入向量* x ∈ R ^n *其中* n *n 代表维数,类* C *和模型* F : R ^n → R ^C *。归属方法定义为* A(F,x,C) : R ^n → R ^n *。所提供的解释对应于给定类别* C *和型号* F *的输入向量中元素的“重要性”。*
这个定义可以重写,以使通常的卷积神经网络的输入适合 m×n 输入矩阵:
> ***定义 2.2(归属方法— CNN)*** *给定一个输入矩阵* x ∈ R^{m×n} *其中* m × n *表示输入的维数,类* C *和模型* F : R ^{m×n} → R ^C *。归属方法定义为* A(F,x,C) : R ^{m×n} → R ^{m×n} *。所提供的解释对应于给定类别* C *和型号* F *的输入矩阵中元素的“重要性”。*
为了更好地可视化属性,我们可以查看[图 2](https://erdem.pl/2021/10/xai-methods-the-introduction/#figure-2) 为了预测一类 ibizan_hound,输入图像的每个像素都被赋予一个值,该值定义了其对预测的属性。具有较高属性的像素(特征)可以被认为在预测该类别时“更重要”。我们可以看到,属性值最高的像素是狗的头部和耳朵边缘的像素。

*图 2:为 ibizan_hound 类生成的引导式 GradCAM 的属性可视化。图片来源:斯坦福狗*[*【6】*](https://www.kaggle.com/jessicali9530/stanford-dogs-dataset)
就像可解释性与可解释性的情况一样(第*“可解释性与可解释性”*),在词汇上缺乏一致。归因方法通常被称为"*显著性方法"*,*特征相关性"*,*特征重要性"*,*热图"*,*神经元激活"*,*显著性遮罩"*。
# 测量 XAI 方法
## 定量和定性研究
研究的类型可分为定性或定量[【7】](http://repository.unmas.ac.id/medias/journal/EBK-00121.pdf)。定性类型的研究通常与非数字数据的观察和处理有关。与定性研究相反的是定量研究,它依赖于对收集数据的数值分析。机器学习模型的可解释性是一个以人为中心的研究领域。这就是为什么大多数 XAI 方法研究人员将他们的解决方案建立在定性测量而不是定量测量的基础上。
## 定性措施
在定性研究中,数据是通过观察、汇总或访谈收集的。这种数据非常主观,与提供数据的人有关。这种主观测量的一个例子如图 3 所示。如果对于相同的输入图像呈现两种属性,那么哪一种属性更好的决定可能会因人而异。当两个属性具有相似的“质量”时,这种可能性更大。

*图 3:相同输入数据的两种不同方法的属性比较。这两种属性都是用不同的 XAI 方法为同一模型生成的。图片来源:斯坦福狗*[*【6】*](https://www.kaggle.com/jessicali9530/stanford-dogs-dataset)
如果没有具体的背景和关于数据生产者的先验知识,就无法对定性计量的结果进行相互比较。即使有这些知识,比较结果可能也是困难的。同样的问题也适用于再现性,如果要重复测量,我们必须确保所有的参与者都会给出相同的答案。定性测量的另一个问题是它们的可扩展性。因为大多数方法依赖于人工输入,为了再次测量相同的方法或将其与另一种方法进行比较,我们必须重复工作两次。
## 量化措施
定量研究中的数据以数字形式存储。这个数字形式必须与以同样方式测量的其他数据具有可比性。每次测量都是可重复的,一旦完成,就可以再次重复使用。由于其可扩展性,这种类型的研究比定性研究具有巨大的优势。定性测量的问题是我们必须定义测量来返回有意义的结果。定义人类视觉感知的度量是一项困难的任务,度量本身应该是客观的,当试图度量像属性这样的复杂事物时,这就更加困难了。有了这样的衡量标准,我们就可以轻松地比较和重现实验。
## 敏感和不忠
如果你对定量方法感兴趣,我写过一篇关于不忠和敏感度度量的文章。两个最流行的指标已经在 [Captum 库](https://captum.ai/)中实现。该文章可从以下网址获得:
[用不忠和敏感来衡量 XAI 的方法](https://erdem.pl/2021/03/measuring-xai-methods-with-infidelity-and-sensitivity)
## 进一步阅读
我决定写一系列文章来解释目前实践中使用的最重要的 XAI 方法。以下是一个列表(将用新文章更新):
* [显著性——最早归因 XAI 方法之一](https://medium.com/@kemalpiro/xai-methods-saliency-ef3841eae910)
* [反卷积-使用 CNN 结构进行解释](https://medium.com/@kemalpiro/xai-methods-deconvolution-3599f5964db8)
* [引导反向传播-利用去卷积引导显著性](https://medium.com/@kemalpiro/xai-methods-guided-backpropagation-77645bd80995)
* [综合渐变——最流行的 XAI 方法](https://medium.com/@kemalpiro/xai-methods-integrated-gradients-6ee1fe4120d8)
* [引导式 GradCAM——引导 grad cam 输出以获得更好的解释](https://medium.com/@kemalpiro/xai-methods-guided-gradcam-e0ce20374703)
* [噪声隧道——如何提高其他方法的精度](https://medium.com/@kemalpiro/xai-methods-noise-tunnel-31ca197fb560)
1. [欧洲议会和理事会 2016 年 4 月 27 日关于在个人数据处理和此类数据自由流动方面保护自然人的 Reg (EU) 2016/679](https://op.europa.eu/en/publication-detail/-/publication/3e485e15-11bd-11e6-ba9a-01aa75ed71a1) ,并废除 Dir 95/46/EC(一般数据保护条例)2016。
2. R.吉多蒂、a .蒙雷亚莱、s .鲁杰里、f .图里尼、f .吉安诺蒂、d .佩德雷斯基。[黑盒模型解释方法综述。](https://arxiv.org/abs/1802.01933)美国计算机学会计算调查(CSUR),51(5):1–42,2018。
3. A.b .阿里埃塔、n .迪亚斯-罗德里格斯、j .德尔塞尔、a .本内托、s .塔比克、a .巴尔巴多、s .加西亚、s .吉尔-洛佩斯、d .莫利纳、r .本雅明、i in。可解释的人工智能(xai):面向负责任的人工智能的概念、分类法、机遇和挑战。信息融合,58:82–115,2020。
4. O.毕然棉花公司。机器学习中的解释和证明:综述。 IJCAI-17 可解释 AI 研讨会(XAI),第 8 卷,第 8–13 页,2017。
5. A.贝拉达·阿达迪。[窥视黑匣子:对可解释人工智能(xai)的调查。](https://ieeexplore.ieee.org/document/8466590) IEEE 访问,2018 年 6:52138–52160。
6. A.科斯拉,贾亚德瓦普拉卡什,姚,。斯坦福狗数据集。https://www.kaggle.com/jessicali9530/stanford-dogs-dataset2019 年[。访问时间:2021 年 10 月 1 日。](https://www.kaggle.com/jessicali9530/stanford-dogs-dataset)
7. J.w .克雷斯威尔。教育研究:计划、实施和定量评估。新泽西州上马鞍河,普伦蒂斯霍尔,2002 年。
*最初发布于*[*https://erdem . pl*](https://erdem.pl/2021/10/xai-methods-the-introduction)*。*
# XGBoost 部署在 GCP,采用谷歌人工智能平台
> 原文:<https://towardsdatascience.com/xgboost-deployment-on-gcp-with-google-ai-platform-ccf2e5b4d6ea?source=collection_archive---------22----------------------->
## 缩小概念验证和二元分类集成解决方案之间的差距
当我第一次听说大约只有 13%的数据科学项目进入生产阶段时,我非常失望。当时,以我在 Jupyter 笔记本上有限的机器学习经验,并不清楚 PoC 阶段之后会发生什么,以及如何发生。此外,ML 部署可以通过多种不同的方式实现——这取决于项目的目标、可用的工具以及这些工具的成本——因此很难知道选择哪种方式作为起点。幸运的是,最近我有机会参加了来自 [AICamp](https://medium.com/u/dd149458e5b5?source=post_page-----ccf2e5b4d6ea--------------------------------) 的课程“GCP 上的全栈 ML”,该课程涵盖了 Google AI 平台上的模型部署。因此,我决定开发这个项目,一个 Scikit-learn 分类 PoC,在 Google AI 平台上进行后期培训和部署,并由云功能触发。
# 问题是
我使用了由 [**UCI 的机器学习知识库**](https://archive.ics.uci.edu/ml/datasets/Online+Shoppers+Purchasing+Intention+Dataset#) **提供的**网购者购买意向数据集[1]。**在这里,数据以这样一种方式进行整合,即每行对应于来自不同用户的一个会话,有 12,330 个样本(会话)。“收入”(对或错)被用作我的目标列。基于点击流特征,如反弹率、访问页面类别和在这些页面上花费的时间(“产品相关”、“信息”等),我想评估两个模型的准确性性能:XGBoost 及其“配对”梯度提升。原始链接中提供了数据集中所有要素的详细描述。**

图 1:数据集中的列和数据类型的概述。图片作者。
从图 1 中可以看出,数据没有空值。
# 概念验证
最初,由于分类变量和布尔变量的存在,这些变量使用来自 *sklearn.preprocessing* 的 LabelEncoder 进行编码。依次检查特征之间的相关性:

图 2:特性关联。图片作者。
尽管与其余部分相比,图的左上部分显示了更高的相关性,但最高值出现在 product related _ Duration x product related 和 ExitRates x BounceRates 中。因此,我从特性列表中删除了 ProductRelated_Duration 和 ExitRates,它们是模型的输入,后来也是 GCP 培训的输入。
此外,由于目标类严重不平衡(10,422 个会话,收入=假,1,908 个会话为真),我采用了一种过采样算法,**合成少数过采样技术,** SMOTE —来解决这个问题。随后,我应用了训练/测试分割,保留了 20%的数据用于验证。
最后,我测试了两个分类器 XGboost 和 Gradient Boosting 的性能,比较了它们的准确性:

图 3:精度值。图片作者。
虽然由于训练和测试分数的差异,观察到一些过度拟合,但是 XGBoost 模型导致了更高的整体性能,这也可以从分类报告中推断出来:

图 4:分类报告。图片作者。
混淆矩阵的输出也显示了我们选择的分类器的平衡性能:

图 5: XGBoost 混淆矩阵。图片作者。
完整的代码(包括将数据转换为 GCP 要求的输入格式的部分,我将在下一节中介绍)如下所示。
所以让我们部署一个 XGBoost 模型吧!
# 部署
在这里,我将使用 Google AI 平台上内置的培训工作选项,因为 XGBoost 在那里可用。如果您想直接使用在 PoC 阶段开发的 Scikit-learn 模型,可以使用 *sklearn.externals.joblib* 导出它。更多信息请点击[链接](https://cloud.google.com/ai-platform/prediction/docs/exporting-for-prediction#joblib)。因此,开发阶段分为:
* 创建培训工作;
* 模型部署;
* 创建一个云函数来与外部应用程序交互。
如前所述,部分代码将修改后数据集的形状(在编码和特征选择之后)转换为 Google AI 平台接受的格式。目标列必须是第一列,并定义为 **string** 或 int **,**,而输入变量设置为 **float。输入的 csv 文件在本项目的资源库中可用,** [**此处**](https://github.com/lptais/Online-shoppers-XGBoost/blob/main/online_shoppers_gcp_input.csv) **。**
## **创建培训工作**
首先,需要将 csv 文件上传到存储桶。转到存储>创建存储桶,选择一个名称并定义您的区域:

图 6:桶的创建。图片作者。
然后在创建和上传文件中单击。之后,进入谷歌人工智能平台>工作,点击新的培训工作。在我们的例子中,由于 XGBoost 是作为内置算法提供的,因此在训练算法步骤中选择这个和后面的“XGBoost ”,然后单击下一步:

图 7:训练算法阶段。图片作者。
在**训练数据**步骤中,通过浏览按钮选择上传到桶中的文件。在“验证数据”中,我们将使用 PoC 中使用的相同分割(20%)。选择您的培训输出路径—可以是同一个存储桶—并单击 NEXT。

图 8:训练数据阶段。图片作者。
最重要的部分是第三个,**算法论证**。在这里,首先定义*目标*,在我们的例子中是**二元:逻辑**,因为它是一个分类问题, *eval_metric* 和 *booster* 。请注意,这种类型目标的输出是样本在正类或“真”类中的概率。将 *eval_metric* 设置为 **error** 以评估分类,并将 *gbtree* 设置为 **booster** ,这是默认设置。另外我选择了 HyperTune 三个参数: *num_boost_round* 、 *max_depth、*和 *eta* 。在 XGboost 的[文档](https://xgboost.readthedocs.io/en/latest/parameter.html)中可以看到每一个的详细描述。

图 9:算法参数阶段。图片作者。
然后,定义 HyperTune 目标以最小化错误——因为我们希望提高分类器的准确性——并将试验次数设为 3,允许提前停止以节省不必要的成本。

图 10:超调设置。图片作者。
在最后一个阶段**作业设置**,为作业定义一个名称,并为存储桶选择相同的区域。选择“扩展层=基本”,然后单击“完成”。很好,您的培训工作已经启动(:大约需要几分钟才能完成。
当作业完成时(您会看到一个绿色标记),在作业页面中,通过单击相应的作业 id 来检查优化的参数。
## **车型部署**
培训工作完成后,在工作详细信息屏幕中,选择一个成功的试用,然后单击部署模型:

图 11:模型作业详细信息页面。图片作者。

图 12:模型部署页面。图片作者。
键入模型名称,选择同一区域,然后单击确认。现在的最后一部分是给它分配一个版本:

图 13:模型部署页面。图片作者。
点击 SAVE,再次等待几分钟,您将拥有一个已部署的 ML 模型。
## **创建云函数**
云功能允许无服务器部署和通过 REST API 调用 ML 模型的可能性。要设置此部分,请转到 GCP 上的云函数,然后选择创建新函数。在这里,选择一个函数名并再次选择同一区域。将触发器保留为 HTTP。

图 14:云功能配置页面。图片作者。
点击 next,定义你的源代码的运行时(这里我选择了 Python 3.7),把入口点填成所用函数的同名。

图 15:云函数代码页。作者图片
图 16 所示的代码也可以在存储库中找到。使用云函数 HTTP 链接,可以使用外部应用程序(如 Postman)发送请求并获得预测:

图 16:样本邮递员响应。图片作者。
同样,重要的是要注意,输出是预测的概率,在我们的例子中,这个会话的收入=真。类似的测试也可以在 Google AI 平台中进行,在 Models>Test & Use 下,使用 JSON 格式的输入。
就是这个!现在,您有了一个基于云的部署模型,可以通过外部应用程序调用该模型并返回预测。
我希望你喜欢这篇文章,并希望它能启发你超越 Jupyter 笔记本电脑概念验证:
**参考文献:**
[1]萨卡尔,C.O .,波拉特,S.O .,卡特西奥卢,M. *等*利用多层感知器和 LSTM 递归神经网络实时预测网购者的购买意向。*神经计算&应用* **31,**6893–6908(2019)。[https://doi.org/10.1007/s00521-018-3523-0](https://doi.org/10.1007/s00521-018-3523-0)
# XGBoost:极端梯度增强—如何改进常规梯度增强?
> 原文:<https://towardsdatascience.com/xgboost-extreme-gradient-boosting-how-to-improve-on-regular-gradient-boosting-5c6acf66c70a?source=collection_archive---------3----------------------->
## 机器学习
## 详细了解这两种算法之间的差异,以及何时应该选择其中一种算法

XGBoost。图片由[作者](https://solclover.com/)提供。
# 介绍
如果你想成为一名成功的数据科学家,你需要了解各种机器学习算法之间的差异。
这个故事是研究不同算法如何工作的系列的一部分,并为您提供示例和 Python 代码,以帮助您踏上数据科学之旅。
# 内容
* XGBoost 所属的算法类别
* XGBoost 和梯度增强的基础知识
* XGBoost 和渐变增强构建树的方式不同
* 计算输出值
* XGBoost 优化
# XGBoost 属于哪一类算法?
极端梯度推进是一种基于树的算法,它位于机器学习的监督分支之下。虽然它可以用于分类和回归问题,但本文中的所有公式和示例都是指该算法用于**分类**。
> 旁注,由于神经网络独特的机器学习方法,我已经将它们归为一类。然而,它们可以用于解决广泛的问题,包括但不限于分类和回归。下图是**互动**所以一定要点击👇在不同的类别上对**进行放大并展示更多的**。
机器学习算法分类。由[作者](https://solclover.com/)创建的互动图表。
***如果你喜欢数据科学和机器学习*** *,请* [*订阅*](https://solclover.com/subscribe) *每当我发表一个新的故事,你都会收到一封电子邮件。*
# XGBoost 和梯度增强的基础知识
在深入细节之前,让我们回顾一下这些算法的基础。
* **基于树的算法**—XG boost 和梯度增强都使用决策树作为它们的基本估计器。
* **预测目标** —使用残差而不是实际的分类标签来构建树。因此,尽管我们关注分类问题,这些算法中的基本估计量是回归树而不是分类树。这是因为残差是连续的,而不是离散的。但是,与此同时,您将在下面看到的一些公式对于分类是独特的,所以请不要假设完全相同的公式适用于回归问题。
* **树深度** —两种算法都允许您控制树的最大大小,以最小化数据过度拟合的风险。
* **集成方法** —类似于随机森林或 AdaBoost,这些算法在这个过程中构建许多树。最后,最终的预测是基于所有的树。
* **学习率** —每棵树的价值由学习率决定。这使得算法在每一步都有更加渐进和稳定的改进。
* **流程图** —最后,这里简单说明一下 Gradient Boosting 和 XGBoost 使用的流程。

梯度增强和 XGBoost 算法的流程图。图片由[作者](https://solclover.com/)提供。
虽然这些算法还有更多的部分,但以上应该给了你足够的基本理解,所以我们可以在接下来的几节中深入了解更多的细节。
# XGBoost 和渐变增强构建树的方式不同
## 梯度推进
您会很高兴听到常规梯度提升使用标准方法来构建回归树,其中使用 MSE(均方误差)等典型指标或类似指标来确定树的最佳分割。
下面是 MSE 的公式:

该算法为每个可能的节点分裂计算 MSE,然后挑选具有最低 MSE 的一个作为在树中使用的一个。
如果你不熟悉这是如何工作的,我强烈建议你回顾一下我关于梯度增强树的故事中的深入解释:
</gradient-boosted-trees-for-classification-one-of-the-best-machine-learning-algorithms-35245dab03f2>
## XGBoost
与常规的梯度增强相反,XGBoost 使用自己的方法来构建树,其中相似性得分和增益决定最佳节点分裂。

* **剩余的**就是`actual (observed) value — predicted value`
* **先前概率**是在先前步骤中计算的事件的概率。假设每个观察值的初始概率为 0.5,用于构建第一棵树。对于任何后续的树,基于初始预测和来自所有先前树的预测,重新计算先前的概率,如过程图中所示。
* **λ**是正则化参数。增加λ不成比例地降低了小叶子(具有很少观测值的叶子)的影响,而对大叶子(具有很多观测值的叶子)只有很小的影响。
一旦我们有了每片叶子的相似性分数,我们就可以使用下面的公式计算增益:

然后选择具有最高增益**的节点分裂作为树的最佳分裂。**
> **重要:**XGBoost 还有一个**伽马超参数**,可以手动设置。它允许您删除增益最小的节点。修剪发生在`Gain — Gamma < 0.`
>
> 此外,请注意,较高的λ值会导致较低的相似性得分,从而导致较低的增益。因此,您可以结合使用 Gamma 和 Lamba,通过修剪小叶子并将它们与其他观察值结合来降低树对单个观察值的敏感度。
# 计算输出值
两种算法计算每片叶子的输出值的方式几乎相同,唯一的区别是 lambda 超参数。
**梯度增强**算法使用以下公式:

同时, **XGBoost** 使用:

## 例子
让我们用一个简单的例子把理论付诸实践。假设我们有一个包含 3 个观测值的数据集,我们使用“湿度 3pm”来预测明天是否会下雨。
下面是梯度推进算法如何构建它的第一棵树:

梯度增强示例。图片由[作者](https://solclover.com/)提供。
这是 XGBoost 算法的等价形式:

极端梯度推进的例子。图片由[作者](https://solclover.com/)提供。
我们可以看到,虽然计算结果有很大不同,但在这种情况下,两种算法都会为第一棵树选择相同的分割。
但是,树叶的输出值是不同的(即使 lambda 设置为 0 ),因为每个算法都从不同的初始预测开始。梯度增强使用整个数据集的事件概率作为其预测,而 XGBoost 总是从 0.5 开始(如果需要,可以手动更改)。
# XGBoost 优化
除了使用自己的方式构建和修剪树,XGBoost 还内置了几个优化功能,以便在处理大型数据集时加快训练速度。以下是几个主要的例子:
* **近似贪婪算法** —在寻找最佳节点分割时使用加权分位数,而不是评估每个可能的分割。
* **并行学习** —它可以将数据分割成更小的数据集,以并行运行流程。
* **稀疏感知分裂发现** —当您有一些缺失数据时,它通过将带有缺失值的观察值放入左叶来计算增益。然后,它通过将它们放入右边的叶子来做同样的事情,并选择产生更高增益的场景。
* **现金感知访问** — XGBoost 使用 CPU 的高速缓冲存储器来存储梯度,因此它可以更快地计算相似性得分。
[](https://solclover.com/membership)[](https://www.linkedin.com/in/saulius-dobilas/)
# Python 代码
现在让我们使用澳大利亚的天气数据用 Python 构建一个简单的 XGBoost 模型。
## 设置
我们将使用以下数据和库:
* 来自 Kaggle 的澳大利亚天气数据
* [Scikit-learn 库](https://scikit-learn.org/stable/index.html),用于将数据拆分为[训练测试](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html?highlight=train_test_split#sklearn.model_selection.train_test_split)样本和[模型评估](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html?highlight=classification_report#sklearn.metrics.classification_report)
* [XGBoost 库](https://xgboost.readthedocs.io/en/latest/index.html)用于构建模型
* 用于数据操作的[熊猫](https://pandas.pydata.org/docs/)和 [Numpy](https://numpy.org/)
让我们导入所有的库:
然后我们从 Kaggle 获取澳大利亚的天气数据,你可以按照这个链接下载:[https://www . ka ggle . com/jsphyg/weather-dataset-rattle-package](https://www.kaggle.com/jsphyg/weather-dataset-rattle-package)。
我们接收数据并推导出一些新的变量用于模型中。
数据看起来是这样的:

一小段 [Kaggle 的澳大利亚天气数据](https://www.kaggle.com/jsphyg/weather-dataset-rattle-package)做了一些修改。图片由[作者](https://solclover.medium.com/)提供。
## 模特培训
接下来,我们定义一个用于模型训练的函数,并运行模型以产生结果:
* 步骤 1 —将数据分为训练样本和测试样本
* 步骤 2-设置模型参数并训练(拟合)模型
* 步骤 3-使用我们的模型预测训练和测试数据上的类别标签
* 步骤 4-生成模型摘要统计数据
* 步骤 5-运行模型并显示结果
这些是上述函数返回的模型评估指标。

XGBoost 模型结果。图片来自[作者](https://solclover.medium.com/)。
# 结论
XGBoost 是 tin 上显示的内容,即常规梯度增强算法的更精确和优化的版本。在大多数情况下,这些算法产生的结果可能会非常相似。如果您处理大型数据集,那么极端梯度增强应该是更好的选择。
我希望这个故事能让你对这两种算法的主要区别有一个很好的了解,但是如果你有任何问题或建议,请随时联系我们。快乐造型!
干杯!👏
**索尔·多比拉斯**
***如果你已经花光了这个月的学习预算,下次请记得我。*** *我的个性化链接加入媒介是:*
<https://solclover.com/membership>
如果你喜欢这篇文章,你可能也会觉得有趣:
</adaboost-algorithm-remarkably-capable-but-with-one-interesting-limitation-cf95905bf8a0> </svm-classifier-and-rbf-kernel-how-to-make-better-models-in-python-73bb4914af5b>
# XGBoost:微调和优化模型的完整指南
> 原文:<https://towardsdatascience.com/xgboost-fine-tune-and-optimize-your-model-23d996fab663?source=collection_archive---------0----------------------->
## 如何调整 XGBoost 超参数并增强模型的性能?

照片由 [@spacex](https://unsplash.com/@spacex) 在 [Unsplash](https://unsplash.com/) 上拍摄
# XGBoost 为什么这么受欢迎?
XGBoost 最初是作为 2014 年的一个[研究项目开始的,它已经迅速成为过去几年最受欢迎的机器学习算法之一。](https://arxiv.org/pdf/1603.02754.pdf)
许多人认为它是最好的算法之一,并且由于它在回归和分类问题上的出色性能,在许多情况下推荐它作为首选。XGBoost 因赢得大量 Kaggle 比赛而闻名,现在用于许多行业应用程序,甚至在机器学习平台中实现,如 [BigQuery ML](/super-fast-machine-learning-to-production-with-bigquery-ml-53c43b3825a3) 。
如果您正在阅读这篇关于 XGBoost 超参数优化的文章,您可能对该算法很熟悉。但是为了更好地理解我们想要调优什么,让我们回顾一下!
# 第 1 部分:理解 XBGoost
XGBoost ( **极限梯度提升**)不仅仅是一个算法。这是一个完整的[开源库](https://github.com/dmlc/xgboost),被设计为梯度增强框架的优化实现。它侧重于速度、灵活性和模型性能。它的强大不仅仅来自于算法,还来自于所有的底层系统优化(并行化、缓存、硬件优化等等……)。
在大多数情况下,data scientist 将 XGBoost 与“基于树的学习者”一起使用,这意味着您的 XGBoost 模型是基于决策树的。但是即使它们不太受欢迎,你也可以将 XGboost 与其他基础学习者一起使用,比如线性模型或 Dart。因为这是目前最常见的情况,所以在本文的剩余部分,我们将集中讨论树。
> 到那时,你可能会有更多的问题。什么是决策树?什么是助推?梯度推进有什么区别?
> 别担心,我们会全部复述一遍!
## 什么是决策树和购物车?

推车:这个人玩电子游戏吗?—图片来自 XGBoost 文档
决策树是最简单的 ML 算法之一。
这是一种实现只包含条件语句的算法的方法。
XGBoost 使用一种叫做 CART 的决策树:分类和决策树。
* **分类树**:目标变量是分类的,该树用于识别目标变量可能属于的“类别”。
* **回归树**:目标变量是连续的,用树来预测其值。
CART 叶不仅包含最终决策值,还包含每个叶的实值分数,无论它们是用于分类还是回归。
## 什么是助推?
Boosting 只是一种使用集成学习原理的方法,但是是按顺序进行的。
如果你不熟悉集成学习,它是一个结合来自多个底层模型的决策,并使用投票技术来确定最终预测的过程。
随机森林和 Bagging 是两种著名的集成学习方法。

使用 Bagging 方法和多数投票策略的集成学习示例—图片由作者提供
Boosting 是一种集成学习,它使用前一个模型的结果作为下一个模型的输入。boosting 不是单独训练模型,而是按顺序训练模型,训练每个新模型来纠正以前模型的错误。在每次迭代(回合)中,正确预测的结果被赋予较低的权重,而错误预测的结果被赋予较高的权重。然后,它使用加权平均值得出最终结果。

使用加权平均策略的 Boosting 方法的集成学习示例—图片由作者提供
## 什么是梯度增强?
最后,梯度推进是一种利用[梯度下降](https://www.youtube.com/watch?v=sDv4f4s2SB8)算法将误差最小化的推进方法。简而言之,梯度下降是一种迭代优化算法,用于最小化损失函数。
损失函数量化了我们的预测与给定数据点的实际结果之间的差距。预测越好,损失函数的输出就越低。

损失函数示例:均方误差
当我们构建模型时,目标是最小化所有数据点的损失函数。例如,均方误差(MSE)是最常用的回归损失函数。
与经典增强相反,梯度增强不仅对错误预测的结果进行加权,而且还根据梯度调整这些权重——由损失函数中损失“下降最快”的方向给出。如果你想了解更多关于渐变提升的知识,可以看看[这个视频](https://www.youtube.com/watch?v=TyvYZ26alZs)。
正如我们在简介中所说,XGBoost 是这种梯度增强方法的优化实现!
## 那么,如何使用 XGBoost 呢?
有两种使用 XGBoost 的常见方法:
* [学习 API](https://xgboost.readthedocs.io/en/latest/python/python_api.html#module-xgboost.training) :是使用 XGBoost 的基本的、底层的方式。简单而强大,它包括一个内置的交叉验证方法。
```
import xgboost as xgb
X, y = #Import your data
dmatrix = xgb.DMatrix(data=x, label=y) #Learning API uses a dmatrixparams = {'objective':'reg:squarederror'}
cv_results = xgb.cv(dtrain=dmatrix,
params=params,
nfold=10,
metrics={'rmse'})print('RMSE: %.2f' % cv_results['test-rmse-mean'].min())
```
* [Scikit-Learn API](https://xgboost.readthedocs.io/en/latest/python/python_api.html#module-xgboost.sklearn) :它是 XGBoost 的 Scikit-Learn 包装器接口。它允许以 scikit-learn 兼容的方式使用 XGBoost,就像使用任何本机 scikit-learn 模型一样。
```
import xgboost as xgbX, y = # Import your dataxtrain, xtest, ytrain, ytest = train_test_split(X, y, test_size=0.2)xgbr = xgb.XGBRegressor(objective='reg:squarederror')xgbr.fit(xtrain, ytrain)
ypred = xgbr.predict(xtest)
mse = mean_squared_error(ytest, ypred)
print("RMSE: %.2f" % (mse**(1/2.0)))
```
*请注意,使用学习 API 时,您可以输入和访问评估指标,而使用 Scikit-learn API 时,您必须计算它。*
## **目标函数**
XGBoost 在多种情况下是一个很好的选择,包括回归和分类问题。基于问题和你希望你的模型如何学习,你将选择一个不同的[目标函数](https://xgboost.readthedocs.io/en/latest/parameter.html#learning-task-parameters)。
最常用的有:
* **reg:squarederror** :用于线性回归
* **reg:logistic** :用于 logistic 回归
* **二元:逻辑**:逻辑回归——输出概率
# 第 2 部分:超参数调优
## 为什么应该调整您的模型?
与优化模型相比,未优化模型的性能如何?值得努力吗?在深入研究 XGBoost 模型调优之前,让我们强调一下为什么**您必须调优您的模型**的原因。
作为演示,我们将使用来自 sklearn 的知名[波士顿房价数据集,并尝试预测房价。](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_boston.html)
在不进行超参数调整的情况下,如何执行我们的模型:
```
import xgboost as xgb
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
boston = load_boston()
X, y = boston.data, boston.target
dmatrix = xgb.DMatrix(data=x, label=y)params={'objective':'reg:squarederror'}cv_results = xgb.cv(dtrain=dmatrix, params=params, nfold=10, metrics={'rmse'}, as_pandas=True, seed=20)print('RMSE: %.2f' % cv_results['test-rmse-mean'].min()) ## Result : RMSE: 3.38
```
没有任何调整,我们得到了一个***3.38***的 RMSE。这并不坏,但是让我们看看它在几个调整过的超参数下会有怎样的表现:
```
import xgboost as xgb
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
boston = load_boston()
X, y = boston.data, boston.target
dmatrix = xgb.DMatrix(data=x, label=y)params={ 'objective':'reg:squarederror',
'max_depth': 6,
'colsample_bylevel':0.5,
'learning_rate':0.01,
'random_state':20}cv_results = xgb.cv(dtrain=dmatrix, params=params, nfold=10, metrics={'rmse'}, as_pandas=True, seed=20, num_boost_round=1000)print('RMSE: %.2f' % cv_results['test-rmse-mean'].min()) ## Result : RMSE: 2.69
```
经过一点点调整,我们现在得到了 2.69 的 ***RMSE。提高了 20%!我们可能会改进得更多。让我们看看如何!***
## 深入研究 XGBoost 超参数
超参数是模型外部的一种参数,在学习过程开始之前设置。它是可调的,可以直接影响模型的性能。
为了找出模型的最佳超参数,您可以使用经验法则,或者我们将在本文中讨论的特定方法。
在此之前,请注意,在使用 XGBoost 时,有几个参数可以优化。你可以在这里找到完整的列表,或者在 [Scikit-Learn API](https://xgboost.readthedocs.io/en/latest/python/python_api.html#module-xgboost.sklearn) 中使用的别名。
对于基于树的学习者,最常见的参数是:
* **max_depth** :每棵树的最大深度。更深的树可能会提高性能,但也会增加复杂性和溢出的机会。
*该值必须是大于 0 的整数。默认值为 6。*
* **learning_rate** :当你的模型朝着它的目标优化时,学习率决定了每次迭代的步长。低学习率使得计算更慢,并且需要更多轮次来实现与具有高学习率的模型相同的残差减少。但是它优化了达到最佳状态的机会。
*该值必须介于 0 和 1 之间。默认值为 0.3。*
* **n_estimators** :我们集合中的树的数量。相当于助推轮数。
*该值必须是大于 0 的整数。默认值为 100。* 注意:在标准库中,这被称为 **num_boost_round** 。
* **colsample_bytree** :表示每棵树随机抽取的列的分数。这可能会改善过度拟合。
*该值必须介于 0 和 1 之间。默认值为 1。*
* **子样本**:代表每棵树要抽样的观察值部分。较低的值可以防止过度拟合,但可能会导致拟合不足。
*该值必须介于 0 和 1 之间。默认值为 1。*
[**正则化参数**](/l1-and-l2-regularization-methods-ce25e7fc831c) **:**
* **alpha** (reg_alpha):权重上的 L1 正则化(Lasso 回归)。当处理大量要素时,它可能会提高速度性能。它可以是任何整数。*默认为 0。*
* **λ**(reg _ lambda):权重上的 L2 正则化(岭回归)。这可能有助于减少过度拟合。它可以是任何整数。*默认为 1。*
* **伽马**:伽马是一个伪正则化参数(拉格朗日乘数),取决于其他参数。伽玛越高,正则化程度越高。它可以是任何整数。*默认为 0。*
# 方法 1:直觉和合理的价值观
第一种方法是从合理的参数开始,并按部就班地进行。如果您理解了上面每个超参数的含义,您应该能够直观地设置一些值。
先说合理的价值观。通常是:
> **max _ depth**:3–10
> **n _ estimators**:100(大量观测值)到 1000(少量观测值)
> **learning _ rate**:0.01–0.3
> **col sample _ bytree**:0.5–1 **子样本**:0.6–1
然后,您可以专注于优化 **max_depth** 和 **n_estimators** 。
然后你可以跟着 **learning_rate** 一起玩,增加它来加速模型而不降低性能。如果它在不损失性能的情况下变得更快,您可以增加估计器的数量来尝试提高性能。
最后,您可以使用您的正则化参数,通常从 alpha 和 lambda 开始。对于 gamma,0 表示没有正则化,1-5 是常用值,而 10+被认为非常高。
# 方法 2:优化算法
寻找最佳超参数的第二种方法是通过优化算法。由于 XGBoost 以 Scikit-learn 兼容的方式提供,您可以使用 Scikit-learn 的超参数优化器函数!
最常见的两种是网格搜索和随机搜索。

[sci kit-学习超参数优化器](https://scikit-learn.org/stable/modules/classes.html#hyper-parameter-optimizers)
## 网格搜索
网格搜索是对指定参数值的每种组合的彻底搜索。如果为 **max_depth** 指定 2 个可能值,为 **n_estimators** 指定 3 个可能值,网格搜索将迭代 6 个可能组合:
> max_depth: [3,6],
> n_estimators:[100,200,300]
>
> 将导致以下可能性:
> 最大深度:3,n 估计量:100
> 最大深度:3,n 估计量:200
> 最大深度:3,n 估计量:300
> 最大深度:6,n 估计量:100
> 最大深度:6,n 估计量:200
> 最大深度:6,n 估计量:300
让我们使用来自 Scikit 的 **GridSearchCV()** 来学习调优我们的 XGBoost 模型!
*在下面的例子中,我们将使用 Kaggle* *上可用的* [*预期寿命数据集的处理版本。*](https://www.kaggle.com/kumarajarshi/life-expectancy-who)
```
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import GridSearchCVdata = pd.read_csv("life_expectancy_clean.csv")X, y = data[data.columns.tolist()[:-1]],
data[data.columns.tolist()[-1]]params = { 'max_depth': [3,6,10],
'learning_rate': [0.01, 0.05, 0.1],
'n_estimators': [100, 500, 1000],
'colsample_bytree': [0.3, 0.7]}xgbr = xgb.XGBRegressor(seed = 20)clf = GridSearchCV(estimator=xgbr,
param_grid=params,
scoring='neg_mean_squared_error',
verbose=1)clf.fit(X, y)print("Best parameters:", clf.best_params_)
print("Lowest RMSE: ", (-clf.best_score_)**(1/2.0))
```
* **估算器:** GridSearchCV 是 *sklearn.model_selection* 的一部分,与任何 scikit-learn 兼容的[估算器](https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html)一起工作。我们用 *xgb。XGBRegressor()* ,来自 XGBoost 的 Scikit-learn API。
* **param_grid:** GridSearchCV 接受一个要在输入中测试的参数列表。正如我们所说,网格搜索将测试每一个组合。
* **评分**:该指标将用于评估交叉验证模型的性能。在这种情况下, *neg_mean_squared_error* 用于替代 *mean_squared_error* 。由于技术原因,GridSearchCV 只是使用 MSE 的负版本——所以它使函数可以推广到其他指标,我们的目标是更高的分数而不是更低的分数。
* **详细:**控制详细程度。越高,消息越多。
更多可用参数,您可以在[文档](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html)中找到。
最后,基于负值的最低 RMSE*clf . best _ score _* 和最佳参数 *clf.best_params_*
```
Best parameters: {'colsample_bytree': 0.7, 'learning_rate': 0.05, 'max_depth': 6, 'n_estimators': 500}
```
## 随机搜索
随机搜索使用大范围(可能无限)的超参数值,并在这些值的组合上随机迭代指定次数。与遍历所有可能组合的网格搜索相反,随机搜索指定了遍历的次数。
如果您为 **max_depth** 输入 10 个可能值,为 **n_estimators** 输入 200 个可能值,并选择进行 10 次**迭代**:
> max_depth: np.arrange(1,10,1),
> n _ estimators:NP . arrange(100,400,2)
>
> 10 次迭代的随机可能性示例:
> 1:最大深度:1,n 估计量:110
> 2:最大深度:3,n 估计量:222
> 3:最大深度:3,n 估计量:306
> 4:最大深度:4,n 估计量:102
> 5:最大深度:1,n 估计量:398
> 6:最大深度:6,n 估计量:290 【T38
现在,让我们使用来自 Scikit 的**randomsearccv()**来学习调优我们的模型!
```
import pandas as pd
import numpy as np
import xgboost as xgb
from sklearn.model_selection import RandomizedSearchCVdata = pd.read_csv("life_expectancy_clean.csv")X, y = data[data.columns.tolist()[:-1]],
data[data.columns.tolist()[-1]]params = { 'max_depth': [3, 5, 6, 10, 15, 20],
'learning_rate': [0.01, 0.1, 0.2, 0.3],
'subsample': np.arange(0.5, 1.0, 0.1),
'colsample_bytree': np.arange(0.4, 1.0, 0.1),
'colsample_bylevel': np.arange(0.4, 1.0, 0.1),
'n_estimators': [100, 500, 1000]}xgbr = xgb.XGBRegressor(seed = 20)clf = RandomizedSearchCV(estimator=xgbr,
param_distributions=params,
scoring='neg_mean_squared_error',
n_iter=25,
verbose=1)clf.fit(X, y)print("Best parameters:", clf.best_params_)
print("Lowest RMSE: ", (-clf.best_score_)**(1/2.0))
```
* 大多数 *RandomizedSearchCV 的*参数与 *GridSearchCV 的*相似。
* **n_iter:** 被采样的参数组合数。越高,你测试的组合就越多。它权衡了解决方案的运行时间和质量。
对于 *GridSearchCV* ,我们以 *clf.best_score_* 的负值为基础,用 *clf.best_params_* 和最低 RMSE 打印最佳参数
# 结论
在本文中,我们解释了 XGBoost 如何操作,以便更好地理解如何调优它的超参数。正如我们所见,调优通常会导致模型性能的大幅提升。
使用我们的直觉来调整我们的模型有时可能就足够了。也值得尝试 GridSearch 和 RandomSearch 这样的优化算法。但是大多数时候,通过测试和直觉,结合算法和调整,你会得到更好的结果!
<https://davidjmartins.medium.com/membership>
# 您可能也会感兴趣…
</10-best-practices-to-write-readable-and-maintainable-sql-code-427f6bb98208>
# XGBoost 用于时间序列预测:不要盲目使用
> 原文:<https://towardsdatascience.com/xgboost-for-time-series-forecasting-dont-use-it-blindly-9ac24dc5dfa9?source=collection_archive---------1----------------------->
## 预测技术并不适用于所有的时间序列

来源:图片由[尼罗](https://pixabay.com/users/nile-598962/)发自 [Pixabay](https://pixabay.com/photos/hourglass-time-hours-clock-620397/)
当使用 ARIMA 模型对时间序列进行建模时,我们通常会特别注意季节性、趋势、合适的时间段等因素。
然而,当使用 XGBoost 这样的机器学习模型来预测时间序列时,所有的常识似乎都过时了。相反,我们只是以类似黑盒的方式将数据加载到模型中,并期望它神奇地给我们准确的输出。
时间序列分析鲜为人知的秘密——无论模型有多好,也不是所有的时间序列都可以预测。试图这样做往往会导致虚假或误导性的预测。
为了说明这一点,让我们看看 XGBoost(特别是 XGBRegressor)在预测 1)爱尔兰都柏林市议会市政办公室的用电模式和 2)曼哈顿山谷的季度公寓销售时是如何变化的。
# XGBRegressor 如何预测时间序列
XGBRegressor 使用许多梯度增强的[树](https://xgboost.readthedocs.io/en/latest/python/python_api.html)(在模型中称为 *n_estimators* )来预测因变量的值。这是通过组合决策树(每个决策树都是弱学习器)来形成组合的强学习器来实现的。
预测时间序列时,该模型使用所谓的回望期来预测未来的若干步。例如,如果使用 1 的回望周期,那么 *X_train* (或独立变量)使用时间序列的滞后值,相对于时间**t***【Y _ train】*的时间序列回归,以便预测未来值。
## 预测用电量
让我们以电力消耗预测为例来看看这是如何工作的。

来源:Jupyter 笔记本输出
正在讨论的数据集可从 data.gov.ie[获得。从该图中,我们可以看到,数据中可能存在一个短期的季节性因素,因为我们经常看到消费趋势的显著波动。](https://data.gov.ie/dataset/energy-consumption-gas-and-electricity-civic-offices-2009-2012/resource/6091c604-8c94-4b44-ac52-c1694e83d746)
让我们使用自相关函数来进一步研究。

来源:Jupyter 笔记本输出
根据该自相关函数,很明显,每个 **7** 滞后都有很强的相关性。直觉上,这是有道理的,因为我们预计对于商业建筑来说,消费将在工作日(最有可能是周一)达到高峰,在周末消费下降。
当用 XGBRegressor 预测这样的时间序列时,这意味着 **7** 的值可以用作回望期。
```
# Lookback period
lookback = 7
X_train, Y_train = create_dataset(train, lookback)
X_test, Y_test = create_dataset(test, lookback)
```
根据训练数据运行模型,并做出预测:
```
from xgboost import XGBRegressormodel = XGBRegressor(objective='reg:squarederror', n_estimators=1000)
model.fit(X_train, Y_train)
testpred = model.predict(X_test)
```
让我们计算 RMSE,并将其与测试平均值进行比较(前者的值与后者相比越低越好)。
```
>>> import math
>>> from math import sqrt
>>> test_mse = mean_squared_error(Y_test, testpred)
>>> rmse = sqrt(test_mse)
>>> print('RMSE: %f' % rmse)RMSE: 437.935136>>> np.mean(Y_test)3895.140625
```
我们看到,与平均值相比,RMSE 非常低(整体平均值的 11%),这意味着 XGBoost 在预测测试集的值方面做得非常好。如果您希望更详细地查看此示例,可点击[此处](/predicting-electricity-consumption-with-xgbregressor-a11b71104754)获得进一步的分析。
## 预测曼哈顿山谷公寓的销售
在上面的例子中,我们显然有一个每周的季节性因素,这意味着一个适当的回顾期可以用来做预测。
然而,有许多时间序列**没有**有季节性因素。这使得任何类型的模型都更难预测这样的时间序列——序列中缺乏周期性波动会在这方面引起重大问题。
这是 2003 年至 2015 年曼哈顿山谷季度公寓销售的直观概览。数据来源于[纽约公开数据](https://data.cityofnewyork.us/Housing-Development/NYC-Calendar-Sales-Archive-/uzf5-f8n2),曼哈顿山谷的公寓——电梯公寓——的销售价格是从 2003 年到 2015 年按季度汇总的。

来源:Jupyter 笔记本输出
从上面我们可以看到,在某些季度,销售往往会达到高峰,但这种情况似乎没有固定的频率。
让我们再来看一个自相关函数。

来源:Jupyter 笔记本输出
从自相关来看,似乎每 9 个滞后就有一个相关的小峰值,但这些峰值位于自相关函数的阴影区域内,因此在统计上不显著。
如果我们尝试使用 XGBRegressor 模型的回顾期 **9** 来预测季度销售额会怎么样?
指定了与前一示例中相同的模型:
```
from xgboost import XGBRegressormodel = XGBRegressor(objective='reg:squarederror', n_estimators=1000)
model.fit(X_train, Y_train)testpred = model.predict(X_test)
testpred
```
现在,让我们计算 RMSE,并将其与测试集中计算的平均值进行比较:
```
>>> test_mse = mean_squared_error(Y_test, testpred)
>>> rmse = sqrt(test_mse)
>>> print('RMSE: %f' % rmse)RMSE: 24508264.696280>>> np.mean(Y_test)47829860.5
```
我们可以看到,在这种情况下,RMSE 相当大——占测试集计算平均值的 50%。
这表明,该模型在预测曼哈顿山谷公寓的季度总销售额方面没有太大的预测能力。
鉴于似乎不存在季节性,我们缩短回望期如何?让我们尝试一个 **1** 的回顾周期,其中仅使用前一个值。
```
>>> test_mse = mean_squared_error(Y_test, testpred)
>>> rmse = sqrt(test_mse)
>>> print('RMSE: %f' % rmse)RMSE: 21323954.883488>>> np.mean(Y_test)35266600.64285714
```
整个测试集的平均值的大小已经减小,因为现在有更多的值包含在测试集中,这是更低的回顾期的结果。这在一定程度上消除了销售高峰的影响。然而,我们看到 RMSE 的大小并没有减少那么多,误差的大小现在占平均值总大小的 60%以上。
因此,使用 XGBRegressor(即使有不同的回顾期)在预测非季节性数据方面做得并不好。
# 结论
有许多类型的时间序列太不稳定,或者不适合直接预测。然而,像 XGBoost 这样的机器学习模型经常被以即插即用的方式处理,从而将数据输入到模型中,而不考虑数据本身是否适合分析。
因此,本文的主要观点是,无论您是使用 XGBoost 模型,还是其他任何模型,都要确保首先根据时间序列本身的优点对其进行分析。这意味着确定总体趋势以及是否存在季节性模式。
XGBoost 的魅力在于,人们可以潜在地使用该模型来预测一个时间序列,而不必了解该时间序列的技术组成部分——但事实并非如此。
非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。
*免责声明:本文是在“原样”的基础上编写的,没有担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。*
# 时间序列推断的 XGBoost:你需要一艘更大的船
> 原文:<https://towardsdatascience.com/xgboost-for-time-series-youre-gonna-need-a-bigger-boat-9d329efa6814?source=collection_archive---------8----------------------->

罗伯特·H 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄的照片
> **更新**:发现我关于渐变提升的新书[实用渐变提升](https://amzn.to/3GCBtNF)。这是用 python 中的许多例子对渐变增强的深入探究。
<https://amzn.to/3GCBtNF> [## 实用的渐变增强:深入探究 Python 中的渐变增强
### 这本书的梯度推进方法是为学生,学者,工程师和数据科学家谁希望…](https://amzn.to/3GCBtNF)
**更新**:发现我在这个问题上的后续,用线性树很好地解决了这个问题:
</xgboost-for-timeseries-lightgbm-is-a-bigger-boat-197864013e88>
XGBoost 是一个非常强大和通用的模型。它的应用范围很广,已经成功地应用于许多最大似然分类和回归问题。这里用代码[解释了基本的数学原理](/diy-xgboost-library-in-less-than-200-lines-of-python-69b6bf25e7d9)。
尽管它最初不是为处理时间序列而设计的,但许多数据科学家仍然在这种情况下使用它。他们这样做对吗?让我们看看数学告诉了我们这个用例什么。
# 时间序列的 XGBoost
例如,XGBoost 甚至被用于预测时间序列[这里的](https://machinelearningmastery.com/xgboost-for-time-series-forecasting/)和[这里的](/using-gradient-boosting-for-time-series-prediction-tasks-600fac66a5fc)。秘诀是输入与时间相关的特征:滞后、频率、小波系数、周期…
由于 XGBoost 非常擅长识别数据中的模式,如果您有足够的时态特征来描述您的数据集,它将提供非常不错的预测。
但是,XGBoost 缺少一个对时间序列绝对关键的基本特性。让我们分析一下这个模型背后的数学原理,以理解 XGBoost 要成为一个好的时间序列预测模型还缺少什么。
# XGBoost 的数学基础
在 XGBoost 文档中,有一篇非常说教的[文章](https://xgboost.readthedocs.io/en/latest/tutorials/model.html)详细解释了 XGBoost 模型是如何从数学公式中推导出来的。我强烈建议通读这篇论文,因为这对于真正理解超参数(如γ、α等)的作用至关重要
我还写了一篇解释性的[文章](/diy-xgboost-library-in-less-than-200-lines-of-python-69b6bf25e7d9),用 200 行代码重建了一个基本的 XGBoost 库。
众所周知,XGBoost 是一个基于树的模型。它可以堆叠任意多的树,每一棵额外的树都试图减少之前的树的误差。总体思路是将许多简单、弱的预测器结合起来,建立一个强预测器。
但是让我们关注 XGBoost 文档中最重要的公式:预测是如何计算的。这是一个非常简单的公式:

摘录自 XGBoost [文档](https://xgboost.readthedocs.io/en/latest/tutorials/model.html)。
其中估计 ***y_i*** 是预测, ***x_i*** 是特征向量, ***f_k(x_i)*** 是为每棵树计算的值, ***K*** 是树的总数。
正如您所看到的,XGBoost 模型本质上是一个关于每棵树的附加模型。让我们看一下 ***f_k*** 来了解树的分数是如何计算的,看看我们这里说的是什么样的函数。
XGBoost 文档再一次给了我们答案,这也很容易理解:

摘自 XGBoost [文档](https://xgboost.readthedocs.io/en/latest/tutorials/model.html)。
***q(x)*** 是将特征 ***x*** 归属于当前树 ***t*** 的特定叶子的函数。 ***w_q(x)*** 则是当前树的叶分数*和当前特征 ***x*** 。*
*总的来说,一旦你训练了你的模型,这是问题中最难的部分,预测简单地归结为根据特征为每棵树确定正确的叶子,并总结每个叶子的值。*
*现在让我们看看这种模型的具体结果是什么,以及对时间序列预测有什么影响。*
# *XGBoost 无法外推!!!*
*同样,XGBoost 是一个非常强大和高效的分类和回归工具,但是它缺少一个非常关键的特性:它不能外推!或者至少,它不能推断出比简单常数更复杂的东西。线性、二次或三次插值是不可能的。*
*正如我们在前面的公式中所看到的,XGBoost 预测只是基于树叶值的总和。这些值没有应用任何变换:没有缩放、没有对数、没有指数,什么都没有。*
*这意味着 XGBoost 只能对以前在训练历史中遇到的情况做出很好的预测。它不会捕捉趋势!*
*下面的几行代码非常有说服力,应该足以说明这种局限性,并让您相信 XGBoost 无法进行外推:*
*这几行代码使用 XGBoost 模型来预测一个非常基本的纯线性系统的值,该系统的输出与时间成正比。如下图所示,XGBoost 在插值时非常出色,正如您可以看到的对 0 到 10 之间的 ***t*** 的预测。*
**
*作者创造的情节。*
*但是当我们分析了潜在的数学模型后,在试图推断时,它完全失败了。事实上,如上所述,XGBoost 模型不能预测没有出现在它的训练中的事件。*
# *你为什么要为插值而烦恼呢?*
*不幸的是,时间序列,或者至少是那些值得关注的时间序列,通常是非平稳的。这意味着它们的统计特征——平均值、方差和标准差——会随着时间而变化。*
*准确预测这种时间序列需要模型不仅能捕捉时间的变化,还能进行外推。*
*我们可以研究两个例子来说明这一点。在第一个例子中,我们想估计在一个天空从不多云的特定地点每平方米接收到的太阳能数量,而不管是哪一天。有了几年的数据,XGboost 将能够做出非常合理的估计,因为接收到的能量数量本质上是一个几何问题,而且地球围绕太阳的运动几乎是完全周期性的。我们面对的是一个静止的系统。*
*另一方面,假设我们不再想预测太阳辐照度,而是想预测温度。像我们现在这样(都?)意识到,由于人类活动,地球正在经历全球变暖,一个多世纪以来,地球的平均温度一直在上升。见下图:*
**
*全球平均气温正在上升。摘自[伯克利地球](http://berkeleyearth.org/wp-content/uploads/2020/01/2019_Time_Series.png)*
*即使对于一个给定的位置,我们观察到季节性的影响,平均温度在时间上是不稳定的。构建一个 XGBoost 模型,拥有尽可能多的气象或气候特征,永远不会对未来产生好的估计。*
# *我们能黑掉 XGBoost 来克服这个吗?*
*对于某些模型,有时可以破解底层数学来扩展它们的应用范围。这篇[文章](/confidence-intervals-for-xgboost-cac2955a8fde)举例说明了如何使用自定义目标来计算置信区间。*
*例如,您可以使用简单的线性回归模型对非线性系统进行建模和预测,只需向它们提供非线性特征。因此,通过向线性模型提供风速的 7 次方,可以实现风力涡轮机发电的良好性能。*
*不幸的是,不可能调整 XGBoost 模型中用于预测的公式来引入外推支持。*
*将 XGBoost 强大的模式识别与外推法结合起来的一个选择是用一个负责这个的边模型来扩充 XGBoost。*
*另一种方法是对数据进行归一化处理,以消除不稳定的影响,回到稳定的情况。*
# *结论*
*XGBoost 和任何其他基于树的模型都不能在数学上执行任何大于 0 阶的外推。也就是说,它们只能外推一个常量值。当试图将这种模型应用于非平稳时间序列时,这是一个需要考虑的巨大限制。*
*然而,XGBoost 仍然是一个非常有吸引力的工具,可以在具有许多特性的复杂数据中显示结构。用它来预测时间序列可能是一个不错的选择,只要你的目标是固定的。如果不是这样,那么您需要预处理您的数据以确保它是正确的,或者考虑将 XGBoost 与另一个负责处理趋势的模型结合起来。*
# XGBoost:顺序很重要
> 原文:<https://towardsdatascience.com/xgboost-order-does-matter-60d8d0d5aa71?source=collection_archive---------11----------------------->
## 要素的顺序对 XGBoost 模型有什么影响,如何使用要素重要性来识别数据中的相关要素?

米克·豪普特在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
您可能会问自己-为什么我要使用要素重要性来查找数据中的相关要素?例如,查看变量对之间的皮尔逊相关性要简单得多。
没错。
看一看(皮尔逊)相关矩阵:

*作者图片*
*x1* 和 *x4* 相关性高是没有问题的,但是 *x3* 和 *x4* 呢?0.37 的相关性意味着什么?
数据科学家使用机器学习模型,如 XGBoost,将特征(X)映射到目标变量(Y)。理想情况下,我们希望映射尽可能类似于成对数据(X,Y)的真实生成器函数。我们使用标记数据和几个成功指标来衡量给定的学习映射与真实映射相比有多好。
XGBoost 是一个高性能的决策树梯度提升集成,广泛用于表格数据的分类和回归任务。像其他决策树算法一样,它由分裂组成,即迭代选择最能把数据分成两组的特征。该算法在每次迭代中为每个特性分配一个分数,并根据该分数选择最佳分割(要了解更多关于 XGBoost 的信息,我推荐[【1】](https://xgboost.readthedocs.io/en/latest/tutorials/model.html))。然而,如果在模型训练过程中,两个特征在给定的级别上具有相同的分数,会发生什么呢?算法会优先选择哪一个?原来在一些 XGBoost 实现中,首选特性会是第一个(与特性的插入顺序有关);然而,在其他实现中,随机选择两个特征中的一个。
在继续之前,我想说几句 XGBoost 的随机性。在建立树集合的过程中,一些决定可能是随机的:从数据中取样,为每棵树选择特征的子组,等等。使用默认参数运行 XGBoost,并且不进行并行计算,会产生一组完全确定的树。如果您将参数`subsample`的值更改为小于 1,您将获得随机行为,并且需要设置一个种子以使其可重现(使用`random_state`参数)。在这篇文章中,我使用了`subsample=1`来避免随机性,所以我们可以假设结果不是随机的。
这里有一个简单的例子:
我创建了一个简单的数据集,它有两个特征, *x1* 和 *x2* ,这两个特征是高度相关的(皮尔逊相关系数为 0.96),并生成了*目标(*真目标),仅作为 *x1* 的函数。
```
num_samples = 1000
x1 = (np.random.randn(num_samples)
+ 2 * np.random.randn(num_samples)
+ 3 * np.random.randn(num_samples) + 4)
x2 = x1 * 0.5 + np.power(x1, 2) * 0.06 + 0.07
df_simple = pd.DataFrame({'x1': x1, 'x2': x2})
df_simple['target'] **=** df_simple['x1'] ***** 0.08
```
使用默认参数训练 XGboost 模型,并查看特征重要性值(我使用了增益特征重要性类型。要阅读更多关于 XGBoost 类型的特性重要性,我推荐[【2】](/interpretable-machine-learning-with-xgboost-9ec80d148d27),我们可以看到 *x1* 是最重要的特性。太好了!
```
simple_model = xgb.XGBRegressor()
simple_model.fit(X_train, y_train)
...
x1 importance: 0.295
x2 importance: 0.0
```
现在,我们将用相同的参数训练一个 XGBoost 模型,只改变特征的插入顺序。 *x2* 得到了几乎所有的重要性。
不太好…
```
simple_model_reverse = xgb.XGBRegressor()
simple_model_reverse.fit(X_train[['x2', 'x1']], y_train)
...
x1 importance: 0.03
x2 importance: 0.118
```
显然,0.96 的相关性非常高。从一开始,我们就不应该包含这两个特性。
回到我们关于 0.37 相关性的问题,这里有另一个非常简单的例子:
数据集由 4 个特征组成,其中 *x3* 是 *x2* 的噪声变换, *x4* 是 *x1* 、 *x2* 和 *x3* 的非线性组合,而*目标*只是 *x1* 和 *x3* 的函数。
```
x1 = np.random.randn(num_samples)
x2 = np.random.uniform(0, 1, num_samples)
x3 = x2 * 2 - 0.4345 + np.random.randn(num_samples)
x4 = [x1[i] if x1[i] < 0.1 else
x2[np.random.choice(num_samples)]
+ x3[i] for i in range(num_samples)]
df = pd.DataFrame({'x1': x1, 'x2': x2, 'x3': x3, 'x4': x4})
df['target'] = (0.2 * df['x1']
+ 0.67 * np.sqrt(np.power((df['x1'] - df['x3']), 2))
+ df['x1'] * df['x3'])
```
为了模拟这个问题,我用相同的默认参数为 4 个特性的每个可能的排列(24 个不同的排列)重新构建了一个 XGBoost 模型。下图可以看到,MSE 是一致的。但是,与模型的性能一致性相反,特性重要性排序确实发生了变化。

(左)测试 MSE 分布|(右)特征重要性分布| *作者图片*
在 75%的排列中, *x4* 是最重要的特征,其次是 *x1* 或 *x3* ,但在另外 25%的排列中, *x1* 是最重要的特征。这里有两个问题:
1. 顺序不一致。
2. 目标只是一个 *x1* 和 *x3* 的算术表达式! *x4* 不是产生真正目标的方程式的一部分。
不同的特征排序在特征和目标变量之间产生不同的映射。原因可能是变量之间复杂的间接关系。但我为什么要在乎呢?
将特征重要性视为每个特征对**真**目标的贡献的良好近似值可能是不正确的。请务必记住,它仅反映每个要素对模型所做预测的贡献。有时候这正是我们需要的。但是,在其他情况下,我们想知道特征重要性值是解释模型还是数据([【3】](https://arxiv.org/abs/2006.16234))。
## 结论
一个特征可能与另一个特征不相关(线性地或以另一种方式)。然而,在训练过程中,在特征空间的某个子空间,它可能得到与另一个特征相同的分数,并被选择来分割数据。如果两个特征可以被模型互换使用,这意味着它们以某种方式相关,可能是通过一个混杂的特征。
注意功能顺序。如果您不确定,请尝试不同的顺序。使用您的领域知识和统计数据,如皮尔逊相关或交互图,来选择排序。批评特征重要性的输出。同样,用你的领域知识去理解另一个订单是否同样合理。
## 参考
[1] [XGBoost 教程—助推树简介](https://xgboost.readthedocs.io/en/latest/tutorials/model.html)
[2] [斯科特·伦德伯格](/interpretable-machine-learning-with-xgboost-9ec80d148d27)[用 XGBoost](https://medium.com/u/3a739af9ef3a?source=post_page-----60d8d0d5aa71--------------------------------) 进行可解释的机器学习
[3]陈,h .,Janizek,J. D .,Lundberg,s .,& Lee,S. I .,[忠于模型还是忠于数据?](https://arxiv.org/abs/2006.16234) (2020),arXiv 预印本 arXiv:2006.16234。
[4][XG boost](https://vishesh-gupta.medium.com/correlation-in-xgboost-8afa649bd066)中的相关性由[维谢什·古普塔](https://medium.com/u/866f05f2fd9?source=post_page-----60d8d0d5aa71--------------------------------)
[5] [特征重要性结果对特征顺序敏感](https://github.com/dmlc/xgboost/issues/3362)
# XGBoost 回归:把我当 10 岁解释给我听
> 原文:<https://towardsdatascience.com/xgboost-regression-explain-it-to-me-like-im-10-2cf324b0bbdb?source=collection_archive---------0----------------------->
## [入门](https://towardsdatascience.com/tagged/getting-started)
当我刚刚开始探索理解机器学习算法时,我会被所有的数学知识淹没。我发现,如果没有完全掌握直觉,就很难理解算法背后的数学。所以我会被那些将算法分解成更简单、更容易理解的步骤的资源所吸引。这就是我今天想要做的。用一种 10 岁孩子也能理解的方式解释 XGBoost 算法。开始了。
让我们从由五个人组成的训练数据集开始。我们记录了他们的年龄,是否有硕士学位,以及他们的工资(以千计)。我们的目标是使用 XGBoost 算法预测*的薪水*。

## 步骤 1:进行初步预测并计算残差
这个预测可以是任何东西。但是让我们假设我们最初的预测是我们想要预测的变量的平均值。

我们可以使用以下公式计算残差:

这里,我们的观察值是*薪水*列中的值,所有预测值都等于 70,因为这是我们选择的初始预测值。

## 步骤 2:构建 XGBoost 树
每棵树从一片叶子开始,所有的剩余部分都进入那片叶子。

现在我们需要计算这个叶子的相似性分数。

λ (lambda)是一个正则化参数,可降低预测对单个观测值的敏感度,并防止数据过度拟合(这是指模型完全符合训练数据集)。λ的默认值是 1,因此在本例中我们让λ = 1。

现在,我们应该看看,如果我们使用基于预测值的阈值将残差分成两组,我们是否能更好地对残差进行聚类,预测值是*年龄*和*硕士学位?。*分割*残差*基本上意味着我们正在向我们的树添加分支。
先来试试用*硕士* **劈叶子?**

然后计算上述分裂的左右叶的**相似性分数**:

现在我们需要量化叶子聚集相似残差*比根聚集相似残差*好多少。我们可以通过计算将*残差*分成两组的**增益**来做到这一点。如果**增益**为正,那么分裂是个好主意,否则就不是。

然后我们将这个**增益**与*年龄*的分裂进行比较。由于*年龄*是一个连续变量,寻找不同分裂的过程就有点复杂了。首先,我们按照*年龄*的升序排列我们数据集的行。然后我们计算*年龄*相邻值的平均值。

现在,我们使用四个平均值作为阈值来分割*残差*,并为每个分割计算**增益**。第一次拆分使用*年龄< 23.5* :

对于这种分裂,我们发现**相似性得分**和**增益**与我们发现*硕士学位的方式相同?*

对剩下的*年龄*劈叉做同样的事情:

出了一个*硕士学位?*分裂和四个*年龄*分裂,其中*硕士*分裂*具有最大的**增益**值,所以我们将其作为我们的初始分裂。现在我们可以通过拆分我们的*硕士学位来给树添加更多的分支?*叶子再次使用上述相同的过程。但是,只有这一次,我们用最初的*硕士学位?*叶作为我们的根节点,并尝试通过获得大于 0 的最大**增益**值来分割它们。*
让我们从左边的节点开始。对于这个节点,我们只考虑*硕士学位中值为‘Yes’的观测值?*因为只有那些观测值落在左节点。

因此,我们使用与之前相同的过程来计算*年龄*分割的**增益**,但是这次仅使用高亮显示的行中的*残差*。

由于只有*年龄< 25* 给我们一个正的**增益**,我们使用这个阈值分割左节点。移动到我们的右节点,我们只查看*硕士学位中带有‘否’值的值?*

我们在右侧节点中只有两个观察值,因此唯一可能的分割是*年龄< 24.5* ,因为这是突出显示的行中两个*年龄*值的平均值。

这种分割的**增益**为正,所以我们最终的树是:

## ***第三步:修剪树木***
修剪是我们避免过度拟合数据的另一种方法。为了做到这一点,我们从树的底部开始,一路向上,看看一个分割是否有效。为了建立有效性,我们使用γ (gamma)。如果 **Gain —** γ为正,那么我们保留该分割,否则,我们移除它。γ的默认值是 0,但是为了便于说明,让我们将γ设置为 50。根据之前的计算,我们知道了**增益**值:

因为**增益—** γ对于除了*年龄< 24.5* 之外的所有分裂都是正的,所以我们可以移除那个分支。所以生成的树是:

## ***第四步:计算叶子的输出值***
我们快到了!我们现在要做的就是在叶节点中计算一个值,因为我们不能让一个叶节点给我们多个输出。

这类似于计算**相似性得分**的公式,除了我们没有平方*残差*。使用公式和λ = 1,**鼓滚** 我们的最终树是:

## ***第五步:做出新的预测***
既然所有艰难的模型构建都已过去,我们就进入激动人心的部分,看看使用新模型后我们的预测有多大改进。我们可以使用这个公式进行预测:

XGBoost 学习率是ɛ (eta ),默认值是 0.3。所以我们第一次观察的预测值将是:

同样,我们可以计算其余的预测值:

## ***第六步:使用新的预测值计算残差***

我们看到新的*残差*比以前的小,这表明我们已经朝着正确的方向迈出了一小步。随着我们重复这个过程,我们的*残差*将变得越来越小,这表明我们的预测值越来越接近观察值。
## ***步骤 7:重复步骤 2–6***
现在我们只是一遍又一遍地重复相同的过程,建立一个新的树,进行预测,并在每次迭代中计算*残差*。我们这样做,直到*残差*非常小,或者我们达到了为我们的算法设置的最大迭代次数。如果我们在每次迭代中构建的树由 Tᵢ表示,其中 *i* 是当前迭代,那么计算预测的公式是:

仅此而已。感谢您的阅读,祝您接下来的算法之旅好运!
如果你想支持我的工作,可以考虑使用[我的链接注册一个媒体订阅](https://medium.com/@shreya.rao/membership)!(每月 5 美元,随时取消)
# Python 中 CPU 和 GPU 上的 Xgboost 回归训练
> 原文:<https://towardsdatascience.com/xgboost-regression-training-on-cpu-and-gpu-in-python-5a8187a43395?source=collection_archive---------20----------------------->
## 如何使用 GPU 解锁 Python 中 xgboost 模型的快速训练

GPU vs CPU 作者绘画
在本文中,我将介绍使用 GPU 而不是默认 CPU 来训练 xgboost 模型所需的步骤。
此外,还分析了矩阵的大小和某些超参数对训练速度的影响。
随意从这里克隆或派生所有代码:[https://github.com/Eligijus112/xgboost-regression-gpu](https://github.com/Eligijus112/xgboost-regression-gpu)。
为了在 GPU 上训练机器学习模型,你需要在你的机器上安装一个 **G** 图形 **P** 处理**U**nit——GPU-a 显卡。默认情况下,机器学习框架会在计算机内部搜索一个**C**entral**P**processing**U**nit——CPU。
我在训练中使用的机器内部:

在我的桌面内部;照片归作者所有
有问题的主要部分是:
***GPU —英伟达 GeForce RTX 3080***
***CPU — AMD 锐龙 7 5800X***
下一步是获得 Nvidia 提供的所有驱动程序,使软件能够联系 GPU 硬件,并使用那里的内存来优化机器学习系数。
我不会复制粘贴其他优秀文章中出现的相同代码和链接。要完整安装所有必需的软件包[,请遵循以下步骤](/installing-tensorflow-with-cuda-cudnn-and-gpu-support-on-windows-10-60693e46e781)。
一旦完成了上一节中的所有步骤,我们需要确保一切都正常工作。理想的地方是 anaconda 提示符命令行面板。在其中,键入:
```
**python** **import tensorflow as tf****tf.test.is_built_with_cuda()****tf.config.list_physical_devices()**
```

命令列表;作者图片
如果没有丢失任何东西并且安装正确,本地计算机上的命令应该会输出与图中非常相似的输出。主线是 TensorFlow 找到了我的显卡。从我的实践来看,这意味着 scikit-learn 机器学习框架也可以使用该卡。
现在我们有了能够使用 GPU 的软件,让我们来训练一些模型吧!
对于建模任务,我将加载包含 **1017209** 行和以下各列的数据:
**店铺** —店铺识别号
**日期** —店铺销售记录的日期
**星期几** —日期的星期几
**销售额** —该日期销售商品的收入(仅在 train_data.csv 中可用)
**ShopOpen** —布尔标志,表示商店在该日期是否营业(如果不营业,销售额应为 0)
**促销** —如果在该日期进行了任何促销,则为布尔标志
**法定假日** —日期是否为法定假日的因子变量
**学校假期** —日期是否为学校假期的因子变量
**商店类型** —描述商店类型的因素变量
**分类类型** —描述商店分类类型的因素变量

数据片段;作者图片
任务是使用所有其他特性对 **Sales (Y)** 变量进行建模。注意,所有其他特征都是分类的。
添加了关于一月中的某一天和一年中的某一月的两个附加特征后,我们可以检查数据集中有多少个唯一的分类值:

独特的分类水平;作者图片
Y 变量的分布:

y 变量直方图;作者图片
完整 Y 和 X 矩阵的最终尺寸:

尺寸;作者图片
X 矩阵有 1150 个特征和超过一百万行。测试计算速度的理想真实数据集!
建模类将是 python 中的 xgboost 回归实现:
初始化模型类的模板是:
```
**import xgboost as xgb** **model = xgb.XGBRegressor(**kwargs)**
```
回归的目标参数应该是: **'reg:squarederror '。**
***为了能够在 GPU 上进行训练,唯一需要做的就是传递一个参数 tree_method = 'gpu_hist '。***
CPU 和 GPU 字典示例:

CPU 和 GPU kwargs 作者图片
可能出现的第一个有趣的问题是,训练速度如何根据 CPU 和 GPU 训练的行数而不同?
我们现在将使用默认的超参数,并且只改变用来训练模型的行数。
正如我们所看到的,当添加更多行时,GPU 和 CPU 之间的差异迅速增加:

训练行数 vs 训练时间;作者图片
从图中可以清楚地看出,在训练 CPU 模型时增加行数会线性地增加训练时间,而增加 GPU 训练类型的行数会对数地增加训练时间。
```
**log(n) << n for big n.**
```
有趣的是,对于非常小的数据集,CPU 实际上表现得更好:

速度表 I;作者图片
这是因为调用 GPU API 进行计算比调用 CPU API 花费的时间更长。当然,实际计算在 GPU 上更快,但是请求的开销使得在小而简单的数据集上,CPU 可能是更好的选择。
完整的伪代码:
第二个有趣的问题是某些超参数值的变化如何影响训练时间?
为此,我们将设置一个 30000 行的随机样本,并且只改变两个超参数: **n_estimators** 和 **max_depth。**
带有拟合速度结果的表尾,按秒差排序:

速度表 II;作者图片
一个 3d 表面图,其中 Z 轴是速度差(正值表示 GPU 训练更快的秒数):

速度差的 3D 平面;作者图片
有趣的是,平面显示增加估计器的数量线性地增加训练时间的差异,增加 max_depth 参数没有线性效应。
代码片段:
总之,根据我的桌面规格,GPU 显著减少了大型 xgboost 模型的训练时间。
使用 GPU 启用 xgboost 模型的训练非常简单——只需将超参数 tree_method 设置为**“GPU _ hist”。**
# 带 AWS Sagemaker 2.20 的 Xgboost '调试器'
> 原文:<https://towardsdatascience.com/xgboost-with-aws-sagemaker-2-20-debugger-41429236f80d?source=collection_archive---------37----------------------->
## **用几行 Python 代码快速简单地获得完整的数据科学分析报告**

由[迈克尔·福塞特](https://unsplash.com/@michaelfousert?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/lego-paris?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
*文章合著者* *作者:* @bonnefoypy,Olexya 公司 CEO。
D 由于数据量巨大,使用自动化库寻找更快分析的技巧是成为独角兽数据科学家的一个关键优势。AWS sagemaker 提供了各种工具,用于在几行代码中开发机器和深度学习模型。对于任何数据科学家来说,机器学习的过程都是极具挑战性的,包括处理缺失数据、编码数据类型、计算特征之间的关联-相关性、塑造数据结构、选择正确的模型以及为推理进行经济高效的模型部署。在本文中,我们将回顾 AWS‘debugger’的最新版本(Sagemaker 2.20),该版本使用众所周知的 XGboost 算法,使用少量 python 代码行生成一流的数据科学分析报告。
在本文中,我们将分析成人糖尿病数据集,您可以通过以下代码阅读:
```
import pandas as pd
data = pd.read_csv('diabetes.csv')
data
X_display = data.drop('Diabetes', axis = 1)
y_display= data['Diabetic']display(X_display.describe())
hist = X_display.hist(bins=30, sharey=True, figsize=(20, 10))
```

**数据集描述(**图片由作者提供)
我们将数据分为训练集、验证集和测试集,并使用以下代码将其保存到我们的 AWS S3 数据存储中(有关完整的分析,请查看以下网址中的完整代码) :
```
import sagemaker, boto3, osbucket = sagemaker.Session().default_bucket()prefix = "demo-sagemaker-xgboost-diabetes -prediction"boto3.Session().resource('s3').Bucket(bucket).Object(os.path.join(prefix, 'data/train.csv')).upload_file('train.csv')boto3.Session().resource('s3').Bucket(bucket).Object(os.path.join(prefix, 'data/validation.csv')).upload_file('validation.csv')
```
输出:
```
AWS Region: eu-west-3RoleArn: arn:aws:iam::61xxxxxx :role/service-role/AmazonSageMaker-ExecutionRole-eu-west-3-philippe-bouaziz
```
AWS 地区:欧盟-西方-3
role arn:arn:AWS:iam::61 xxxxx:role/service-role/Amazon 管理人员-执行 role-eu-west-3-Philippe-bouaziz
```
from sagemaker.debugger import Rule, rule_configsfrom sagemaker.session import TrainingInputs3_output_location='s3://{}/{}/{}'.format(bucket, prefix, 'xgboost_model')container=sagemaker.image_uris.retrieve("xgboost", region, "1.2-1")print(container)
```
输出:
```
61xxxxxx.dkr.ecr.eu-west-3.amazonaws.com/sagemaker-xgboost:1.2-1
```
我们可以使用各种参数和规则轻松个性化我们的模型,如下面的代码所示:
```
xgb_model=sagemaker.estimator.Estimator(image_uri=container,role=role,instance_count=1,instance_type='ml.m5.large',volume_size=5,output_path=s3_output_location,sagemaker_session=sagemaker.Session(),rules=[Rule.sagemaker(rule_configs.create_xgboost_report())])
```
超参数的设置可以在一个步骤中完成:
```
xgb_model.set_hyperparameters(max_depth = 5,eta = 0.2,gamma = 4,min_child_weight = 6,subsample = 0.7,objective = "binary:logistic",num_round = 1000)
```
最后,我们可以轻松地拟合我们的模型:
```
xgb_model.fit({"train": train_input, "validation": validation_input}, wait=True)
```
输出:

**Xgboost 算法训练和验证错误(**图片由作者提供)
大多数数据科学家声称,真正的工作是从迭代这一步开始的,找到最佳超参数是部署最佳模型的关键。一份清晰的数据科学分析报告可能是成功结果的重要过渡。AWS sagemaker 调试器用如下所示的几行代码提供了两个报告,满足了这一需求:
```
from IPython.display import FileLink, FileLinksdisplay("Click link below to view the XGBoost Training report",FileLink("CreateXgboostReport/xgboost_report.html"))profiler_report_name = [rule["RuleConfigurationName"]for rule in xgb_model.latest_training_job.rule_job_summary()if "Profiler" in rule["RuleConfigurationName"]][0]profiler_report_namedisplay("Click link below to view the profiler report",FileLink(profiler_report_name+"/profiler-output/profiler-report.html"))xgb_model.model_data
```

作者图片
**XGboost profiler 报告**
profiler 报告显示每个工作线程(节点)的资源利用率统计信息,例如总 CPU 和 GPU 利用率,以及 CPU 和 GPU 上的内存利用率。该表还显示了数据的描述性统计,包括最小值和最大值以及 p99、p90 和 p50 百分位数。

**资源利用统计(**图片由作者提供)
报告的第二部分演示了在培训工作中使用的调试器内置规则的完整概要。最佳参数包括:批量大小、异常值、清晰显示表格中的数据加载器配置,表明数据流中未来的成本效益改进。

**调试器内置规则参数(**图片由作者提供)
该报告还分析了在训练实例上并行运行的数据加载进程的数量,并将其与核心总数进行了比较。如果该数量少于 70%,则可能会降低数据处理速度,从而导致更高的费用;如果该数量大于 200%,则内核总数可能会降低模型在 CPU 上的性能。

**数据加载流程报告**
更多详情参见本 [**github**](https://github.com/awslabs/sagemaker-debugger) 。
**XGBoost 培训报告**
此报告提供了 XGBoost 模型培训评估结果的摘要、模型性能的见解以及交互式图表。在创建报告的过程中,包括绘图在内的输出会自动保存到 S3,如以下代码所示:
```
# Parameterspath = "/opt/ml/processing/input/tensors"plot_step = 995s3_path = "s3://sagemaker-eu-west-3-xxxx/demo-sagemaker-xgboost-adult-diabetes-prediction/xgboost_model/sagemaker-xgboost-2021-06-04-08-56-51-612/debug-output"
```
该报告包括数据集的真实(目标)标签的直方图。您可以看到目标标签的**分布是否不平衡(用于分类),建议通过生成合成样本、重采样或收集更多数据来校正我们的数据集。该分布还显示了数据集的左偏度或右偏度(用于回归),建议使用 numpy.log 或 numpy。exp 来减少我们数据的偏斜度。**

**目标分布**(图片由作者提供)
本报告的另一个重要情节是**特征重要性**。该方法根据输入要素在预测标注变量时的有用程度为输入要素分配分数。对于我们的 Xgboost 算法,考虑了 3 个参数:
**权重**(或频率)是模型中出现的相对特征的百分比。
**增益**是解释每个特征相对重要性的最相关属性。
**覆盖率**通过特征测量观察值的数量。

**功能重要性图表(**图片由作者提供)
SageMaker 调试器还描述了 XGBoost 学习任务参数的损失值曲线,更多细节请参考此 [**链接**](https://xgboost.readthedocs.io/en/latest/parameter.html#learning-task-parameters) 。

**训练和验证损失曲线(**图片由作者提供)
这些损失曲线给了我们宝贵的见解。如果模型过度拟合,我们的模型会拟合太多的训练数据,这会导致对验证集的负面模型估计。如果模型不合适,则模型参数设置不正确。
对于 Xgboost,如果模型不合适,您可能希望通过增加树的深度(max_depth)、减少正则化参数(lambda,alpha)或者甚至减少 gamma 和 eta 参数来调整参数。如果模型过拟合,您可能希望减少每棵树的深度 max_depth,增加 min_child_weight,甚至增加其他有价值的参数,如 gamma 和 eta、lambda 和 alpha、subsample 和 colsample_bytree。
该报告的另一个关键要素是评估性能指标,如混淆矩阵、精确度、召回率、F 分数和操作特性曲线。如下图所示:

**分类器混淆矩阵(**图片由作者提供)
下面的混淆矩阵显示了分类结果。对角线单元上的真正计数和非对角线单元上的误分类预测数。
包括准确度、精确度、召回率和 F1 得分在内的性能模型得分指标的完整摘要还提供了使用 [**Scikit-learn 指标和评分 API**](https://scikit-learn.org/stable/modules/model_evaluation.html)的功能,如下所示:

**分类器性能报告(**图片由作者提供)
**接收器工作特性** **曲线**显示了 TPR 与 FPR 的比值以及用于评估二元分类的**曲线下面积(AUC)** 。如果 AUC 值接近 1,则模型性能最佳,否则,如果 AUC 小于 0.6,则意味着模型性能不佳,需要进一步调整参数,甚至改变模型算法。

**Roc 曲线(**图片由作者提供)
最后,报告显示了残差直方图的**分布。对于**回归**,直方图应显示一个值为零的良好模型训练的正态分布。对于**分类**,残差显示正确预测计数与错误预测计数的比率。**

**残差分布(**图片由作者提供)
总之,AWS sagemaker 调试器可从 panda 数据帧轻松生成配置文件报告,为找到模型最佳参数和模型训练的最佳技术条件提供重要见解,这是高效模型训练和部署的重要步骤。
本文是对 AWS Xgboost 调试器和另一个数据集的回顾,仅用于教学目的,所使用的代码已被修改(图表大小、颜色、质量、格式),仅用于设计目的。有关 SageMaker 调试器的更多信息,请参见 [**SageMaker 调试器 XGBoost、** **训练报告**](https://docs.aws.amazon.com/sagemaker/latest/dg/debugger-training-xgboost-report.html) 和 [**SageMaker 调试器剖析报告**](https://docs.aws.amazon.com/sagemaker/latest/dg/debugger-profiling-report.html) 。
# xgbse:改进 XGBoost 用于生存分析
> 原文:<https://towardsdatascience.com/xgbse-improving-xgboost-for-survival-analysis-393d47f1384a?source=collection_archive---------5----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 介绍 [**XGBoost 生存嵌入(xgbse)**](https://loft-br.github.io/xgboost-survival-embeddings/index.html) ,我们的生存分析包建立在 XGBoost 之上
> “在使用统计建模从数据中得出结论时,有两种文化。假设数据是由给定的随机数据模型生成的。另一种使用算法模型,并将数据机制视为未知。” *—利奥·布雷曼,* [*统计建模:两种文化*](https://projecteuclid.org/euclid.ss/1009213726)
[生存分析](https://en.wikipedia.org/wiki/Survival_analysis#:~:text=Survival%20analysis%20is%20a%20branch,and%20failure%20in%20mechanical%20systems.)是统计学的一个分支,涉及事件持续时间的建模,最重要的是提供工具来处理(而不是简单地丢弃)*删截的*数据,关于持续时间的部分但有用的信息。例如,如果一套公寓在市场上出售 90 天,我们不知道它实际上需要多长时间才能卖出去,但我们*知道*至少花了*90 天(其中 90 天是审查*的*时间)。生存分析对于广泛的应用是有用的,例如预测性维护、客户流失、信用风险、资产流动性风险以及许多其他应用。*
生存分析模型最常见的输出是生存曲线,它显示了一段时间内生存(事件未发生)的累积概率。其他模型可能输出*风险*,它是一个与曲线衰减速度成比例的数字,或者是一个*预期时间*,通过直接优化或对曲线进行数值积分获得。

当我们关心事件预测的时间时,生存曲线可以给我们很多洞察力。图片作者。
在 [Loft](https://a16z.com/2020/01/03/loft/) ,我们是生存分析模型的大用户,因为在巴西寻找新家的旅程相当漫长,沿途有几个里程碑(拜访、报价、契约、翻新等)。特别是,由于 Loft 经营着一家[ibuer](https://www.opendoor.com/w/guides/what-is-an-ibuyer)(利用自有资产购买、翻新和出售公寓),我们对出售公寓的时间建模很感兴趣,因为持有时间过长是我们面临的最大风险。产生这些估计值的模型用于做出价值百万美元的决策,因此对辨别性能和校准有严格的要求。
因此,我们投入了大量时间探索生存分析包。在我们的探索中,我们发现大多数实现都包含两种文化中的一种:
1. 具有良好统计特性的模型(如校准的生存曲线),但缺乏表达能力和计算效率
2. 高效且富于表现力的模型,但缺乏一些统计特性(仅产生风险或未校准的预期存活时间— XGBoost 适合此处)
考虑到这个场景,我们开始了一个旅程,将这两种文化结合到一个满足我们需求的包中:[**【xgbse】**](https://github.com/loft-br/xgboost-survival-embeddings)。现在我们正在开源它!
我们选择 XGBoost 作为我们的“基础模型”,因为它的实现高效且易于使用,并利用一些技巧为它添加了很好的统计属性。接下来我们将提供更多关于 XGBoost 缺点和这些技巧的细节。
## XGBoost 生存分析的缺点
XGBoost 实现提供了两种生存分析方法: [**Cox**](https://forecast.global/insight/underrated-in-ml-intro-to-survival-analysis/) 和 [**加速失效时间(AFT)**](https://xgboost.readthedocs.io/en/latest/tutorials/aft_survival_analysis.html) 。当涉及到根据风险(歧视)对个体进行排序时,两者都表现出竞争性能(通过 C-index 衡量,ROC AUC 相当于生存)同时又快如闪电。
然而,当涉及到其他期望的统计特性时,我们可以观察到缺点。具体来说,有三个值得关注的属性:
* **生存曲线的预测**而不是**点估计**
* **置信区间的估计**
* **校准后的**(无偏)预期存活时间
让我们以 AFT 实现为例(因为 Cox 只输出风险,而不输出时间或存活率)。AFT 模型输出的值应该被解释为每个样本的预期存活时间。为了计算这个时间,它假设时间和事件的基本分布,由`aft_loss_distribution`和`aft_loss_distribution_scale`超参数控制。由于他们控制了模型的固有假设,我们预计他们会彻底改变模型的输出。

解释`aft_loss_distribution`和`aft_loss_distribution_scale, the latter represented by the "z" variable in the formulas. Source: [XGBoost AFT documentation](https://xgboost.readthedocs.io/en/latest/tutorials/aft_survival_analysis.html).`影响的表格
为了证实这一点,让我们使用代谢数据集进行一个实验。我们将测试`aft_loss_distribution_scale`的不同值,同时保持`aft_loss_distribution`为`"normal"`,并检查这将如何影响模型性能。我们测试以下值:`[0.5, 1.0, 1.5]`。下面的要点给出了如何运行这个简单实验的粗略想法,但是你可以在这里找到完整的代码。结果非常有趣,如果不是惊人的话:
显示 XGBoost 为不同的超参数输出不同的平均预测时间的脚本,这不是一个理想的统计属性。完整代码[此处](https://github.com/loft-br/xgboost-survival-embeddings/blob/main/examples/how_xgbse_works.ipynb)。
```
aft_loss_distribution_scale: 1.5
C-index: 0.645
Average predicted survival time: 203 days
----
aft_loss_distribution_scale: 1.0
C-index: 0.648
Average predicted survival time: 165 days
----
aft_loss_distribution_scale: 0.5
C-index: 0.646
Average predicted survival time: 125 days
----
```
在所有这三种情况下,我们都可以建立 C 指数结果接近最先进水平的模型[8]。然而,我们的预测变化很大,两个模型显示平均预测存活时间相差 78 天。如果我们分析的是单一样本,这种差异或许是可以理解的。但是,我们正在查看完整的验证数据集,无论我们使用哪种模型,平均预测都应该相当稳定。
如果我们将这些结果与 Kaplan Meier 等无偏生存估计量一起绘制,我们可以检查到,对于`aft_loss_distribution_scale`中的`0.5`的每一步,我们在曲线中大约向右移动了十分之一。此外,我们没有看到完整的生存曲线:XGBoost 只输出时间点的事件预测(也没有置信区间)。

Vanilla XGBoost 输出对超参数过度敏感的预测,这阻止了它在对存活曲线校准敏感的应用上的使用。图片作者。
鉴于这些结果,我们提出了一个问题:“我们应该相信什么模型?”。虽然我们可以尝试优化一些误差指标,如 L1 损耗[9],但我们希望为依赖于稳定和校准的事件时间估计的应用提供更多保证。只有点估计,没有置信区间,没有“设计校准”,会损害信任,阻碍生存分析模型的生产。
## 通过将 XGBoost 用作特性转换器来改进它
虽然需要扩展以获得更好的统计属性,但 XGBoost 无疑是一个强大的工具。C-index 结果表明,该模型具有很好的区分性能,可以与最先进的技术相媲美。我们只需要调整我们如何使用它。
除了用于预测任务之外,梯度提升树(gbt)还可以用作输入数据的*特征转换器。集合中的树在区分目标的特征上执行分裂,在它们的结构中编码相关信息。特别地,集合中每棵树的终端节点(叶子)定义了一个非常漂亮的输入数据的<http://scikit-learn.org/stable/auto_examples/ensemble/plot_feature_transformation.html>****【嵌入】*** 。**
****
**我们可以从像 XGBoost 这样的森林模型中提取特征,将原始的特征空间转换为“叶子出现”嵌入。橙色节点表示集合中单个样本的路径。图片作者。**
**这种 ***树系综嵌入*** 具有非常方便的性质:**
1. ****稀疏和高维:**树处理非线性并将原始特征投射到稀疏的高维嵌入中,这有助于线性模型在其上训练时表现良好。例如,在嵌入上训练的逻辑回归(具有独热编码叶指数的特征)可以显示与实际集合相当的性能,具有概率校准的额外好处(参见[1]、[2]和[3])**
2. ****监督:**树也起到了噪音过滤器的作用,只通过具有预测能力的特征进行分割。因此,嵌入实际上具有比输入数据更低的固有维数(见[4])。这减轻了维数灾难,并允许在嵌入(使用汉明距离)上训练的 kNN 模型具有与实际集成相当的性能。有了邻近集,我们可以使用稳健的估计量,如 Kaplan-Meier 来进行预测。**
**这两个属性是我们构建 xgbse 的基础。接下来我们将展示如何充分利用它们。**
## **逻辑回归、时间窗、嵌入作为输入**
**我们的第一种方法`XGBSEDebiasedBCE`,灵感来自于[5]中的多任务逻辑回归方法、[6]中的 BCE(二元交叉熵)方法以及[1]、[2]和[3]中的概率校准思想。**
**它包括在潜在的 XGBoost 模型产生的嵌入基础上训练一组逻辑回归,每个回归在不同的用户定义的离散时间窗口预测存活率。分类器在审查时删除个体,目标是每个窗口的存活指标。**
**因为嵌入是稀疏的和高维的,所以线性模型实际上可以*提高*整体性能,同时还向其添加了良好的统计属性,例如概率校准(因为逻辑回归是通过设计来校准的)。**
****
**`In XGBSEDebiasedBCE we stack several logistic regressions on top of XGBoost, which is used as a feature transformer. Each logistic regression predicts survival at different user-defined time windows. The predictions are then composed to form a survival curve.` 图片作者。**
**由于删除了被审查的个体,这种简单的方法倾向于给出有偏差的生存曲线。因此,我们做了一些调整,使得逻辑回归估计了[卡普兰-迈耶公式](https://www.math.wustl.edu/~sawyer/handouts/greenwood.pdf)中的`di/ni`项(点概率),然后使用 KM 估计量来获得几乎无偏的生存曲线。**
**通过这种方式,我们可以从 XGBoost 中获得完整的生存曲线,以及稍加修改的置信区间(比如执行几轮 bootstrap)。逻辑回归模型的训练和评分是高效的,通过`joblib`并行执行,因此模型可以扩展到数十万或数百万个样本。**
## **Kaplan-Meier 关于最近邻**
**正如上一节所解释的,尽管 XGBoost 产生的嵌入是稀疏的和高维的,但它的固有维数实际上应该低于输入数据。这使得我们能够将 XGBoost“转换”成最近邻模型,其中我们使用汉明距离来定义与在系综终端节点处最常同时出现的元素相似的元素。然后,在每个邻集,我们可以用稳健的方法得到生存估计,如 Kaplan-Meier 估计。**
****
**在 XGBSEKaplanNeighbors 中,我们在作者的`of XGBoost, which is used as a feature transformer. We use hamming distance to build neighbor-sets. At each neighbor-set a KM estimator is executed to get a survival curve.` 图片上叠加了一个 kNN 模型。**
**我们建议使用`dart`作为助推器,以防止任何树在集合中支配方差,并打破叶共现相似性逻辑。我们构建了一个高性能的 KM 估计器实现,以矢量化的方式计算几条存活曲线,包括基于指数 Greenwood 公式的上下置信区间[7]。**
**然而,由于最近邻搜索,在训练(构建搜索索引)和评分(实际搜索)上,这种方法在数十万样本的规模上可能非常昂贵。**
## **一棵树,树叶上有卡普兰-迈耶**
**作为对`XGBSEKaplanNeighbors`的简化,我们还提供了一个单一的树实现。我们没有进行昂贵的最近邻搜索,而是通过 XGBoost 拟合一棵树,并计算其每片叶子的 KM 曲线。**
****
**我们使用 vanilla XGBoost 实现来拟合一棵树,并对它的每一片叶子执行一条 KM 曲线,而不是使用默认的预测。图片作者。**
**这是迄今为止最有效的实现,能够轻松扩展到数百万个示例。在拟合时,树被构建,并且所有 KM 曲线被预先计算,因此在评分时,简单的查询将足以获得模型的估计。**
**然而,由于我们拟合的是一棵树,预测能力可能会更差。这可能是一个明智的权衡,但我们也提供了`XGBSEBootstrapEstimator`,一个引导抽象,我们可以在其中安装一个`XGBSEKaplanTree`森林来提高准确性和减少差异。**
## **它解决问题了吗?**
**现在我们回到第一个例子,看看`XGBEmbedKaplanNeighbors`表现如何:**
```
**aft_loss_distribution_scale: 1.5
C-index: 0.640
Average probability of survival at [30, 90, 150] days: [0.9109, 0.6854, 0.528]
----
aft_loss_distribution_scale: 1.0
C-index: 0.644
Average probability of survival at [30, 90, 150] days: [0.9111, 0.6889, 0.5333]
----
aft_loss_distribution_scale: 0.5
C-index: 0.650
Average probability of survival at [30, 90, 150] days: [0.913, 0.6904, 0.5289]
----**
```
**根据 30 天、90 天和 150 天的平均存活概率来衡量,该模型显示出良好的结果,具有可比性的 C 指数结果和预测非常接近并且独立于`aft_loss_distribution_scale`选择。从视觉上看,平均模型预测与 Kaplan Meier 的比较产生了更好的结果:**
****
**XGBSE 输出一个完整的生存曲线,它比普通的 XGBoost 稳定得多,它的平均预测相当接近无偏的 KM 估计量。图片作者。**
**偏差小得多,完整的生存曲线和稳定性!虽然说这个问题已经解决太苛刻了,但我们相信我们可以建立一个强大的生存分析包,使“两种文化”更加接近。**
## **你自己试试吧!**
**XGBoost 生存嵌入在几个生存分析[基准](https://loft-br.github.io/xgboost-survival-embeddings/benchmarks/benchmarks.html)中显示了很好的结果,在大多数情况下胜过普通的 XGBoost 和参数方法。它可通过 pip 安装,并与 scikit-learn 兼容。还有更多的功能可以尝试,比如置信区间、生存曲线的外推、通过原型的可解释性、度量等等。我们致力于在未来改进 lib 并添加新内容。**
**更多详情,请查看 [github](https://github.com/loft-br/xgboost-survival-embeddings) 上的库、[文档](https://loft-br.github.io/xgboost-survival-embeddings/index.html)和[示例](https://github.com/loft-br/xgboost-survival-embeddings/tree/main/examples)!我们邀请社区做出贡献。请试用它,报告错误,并让我们知道你的想法!**
**由[达维·维埃拉](https://www.linkedin.com/in/davivieirab/)、[古伊列梅·马梅罗拉](https://www.linkedin.com/in/gdmarmerola/)、[加布里埃尔·希门尼斯](https://www.linkedin.com/in/gabriel-gimenez-b8b010140/)和[熊伟·埃斯蒂玛](https://www.linkedin.com/in/vrcestima/)拍摄。**
## **参考**
**[1] X. He、J. Pan、O. Jin、T. Xu、B. Liu、T. Xu、Y. Shi、A. Atallah、R. Herbrich、S. Bowers 和 J. Q. Candela。[预测脸书广告点击量的实践经验](https://research.fb.com/wp-content/uploads/2016/11/practical-lessons-from-predicting-clicks-on-ads-at-facebook.pdf) (2014)。第八届在线广告数据挖掘国际研讨会论文集(ADKDD'14) *。***
**[2] [用系综树进行特征变换](https://scikit-learn.org/stable/auto_examples/ensemble/plot_feature_transformation.html)。sci kit-在[https://scikit-learn.org/](https://scikit-learn.org/)学习文档。**
**[3] G .马尔梅罗拉。[基于树的模型的概率校准](https://gdmarmerola.github.io/probability-calibration/)。在[https://gdmarmerola.github.io/](https://gdmarmerola.github.io/)的个人博客。**
**[4] G .马尔梅罗拉。[使用 RFs 和 UMAP](https://gdmarmerola.github.io/umap-supervised-embeddings/) 监督维度缩减和大规模聚类。https://gdmarmerola.github.io/的[个人博客。](https://gdmarmerola.github.io/)**
**[5] C .于,r .格雷内尔,h .林,v .巴拉科斯*。* [将患者特异性癌症存活分布作为一系列因变量](http://www.cs.cornell.edu/~cnyu/papers/nips11_survival.pdf)进行学习。神经信息处理系统进展 24 (NIPS 2011)。**
**[6] H .克瓦梅。博根。[行政审查下的 Brier 评分:问题与解决](https://arxiv.org/pdf/1912.08581.pdf)。arXiv 预印本 arXiv:1912.08581。**
**[7]索耶。[生存分析中的格林伍德和指数格林伍德置信区间](https://www.math.wustl.edu/~sawyer/handouts/greenwood.pdf)。圣路易斯华盛顿大学网站上的讲义。**
**[8] H .克瓦梅。博根和我.谢尔。[使用神经网络和 Cox 回归进行事件时间预测](https://jmlr.org/papers/v20/18-424.html)。机器学习研究杂志,20(129):1–30,2019。**
**[9] H .海德尔、b .赫恩、s .戴维斯、r .格雷内尔。[建立和评估个体生存分布的有效方法](https://arxiv.org/pdf/1811.11347.pdf)。机器学习研究杂志 21(2020)1–63。**
# xRV 3:最终更新
> 原文:<https://towardsdatascience.com/xrv-the-final-update-cb7b09192ace?source=collection_archive---------47----------------------->
整个夏天,我都在这个博客上记录我试图为 MLB 球场创建一个球场水平运行值估计器,这将是我在可预见的未来的最后一次更新。本文介绍了我是如何做到这一点的,然后深入分析了我为提高我的球场水平预期跑分值指标所采取的步骤。最后,我提供了一些有趣的分析、图表和排行榜。
同样,我在这里向公众公开我的这个项目的所有可复制代码。
*关于 xRV 的兄弟指标 QOS+的信息在本文的底部。*
# 回顾:以前的迭代
## **1.0 版本:KNN 惨败**
今年 3 月,我写了我第一次尝试量化 2019 赛季 MLB 每场比赛的好坏。为此,我使用了一个名为 K-最近邻回归器的模型,其中该赛季的每个球场都被赋予了 2019 年以来 100 个最相似球场的平均运行值。当把每个投球的预测值加起来时,好的投手往往得分高,差的投手往往得分低。
[我就此写了一篇文章,](https://medium.com/something-tangible/pitch-quality-2-estimating-war-aeda0fc0b7e3)相信这是我的模型相当不错的证据。直到最近,在 Nick Wan 的 Twitch stream 上,我才知道几乎每一个音高标准(好的或坏的)都会很好地聚合。这一指标的最终 RMSE 为 0.22,与 2019 年的 pitch run 值的标准差相同。换句话说,这个模型一点也不好。
这可能是因为 KNN 对于这类问题不是一个非常健壮的模型类型,我做了零特征选择或模型验证,这是模型建立过程中两个非常重要的步骤。于是,9 月份又试了一次。
## **2.0 版本:专利克星**
在我的下一个版本的音高质量标准中,我确保一丝不苟地记录我的所有步骤,并公开代码以增加问责。以防这个模型和 1.0 版本一样糟糕,我想更快地得到反馈。在这个用 2020 年的数据创建的模型中,我转向使用随机森林,这是一种更适合这种非线性数据的非参数模型类型。每一个投球手都被 16 个模型中的一个预测,这些模型是针对该投球手的投球类型组(快速球或非快速球)和排分裂(RvR、RvL、LvL、LvR)的。
2.0 版是 1.0 版的客观改进,因为最终 RMSE 从 0.22 下降到约 0.14。作为一名分析师,我也有所提高,这一次是执行特性选择和验证,并且通常有一个更合理的模型构建过程。然而,代码效率很低,为了节省运行时间,我不得不将音高分成 2 组,而不是通常的 7 或 8 组。我这个项目的代码张贴在[上面](https://github.com/ethanmoore21/PitchQuality/blob/master/qop_3_rfs.R),所以你可以自己决定是否同意我的自我评估。
在制作 2.0 版本时,我学到了很多关于模型构建过程的知识,这也是这个项目对我的价值所在。尽管如此,我知道还有一些需要改进的地方,这在我的脑海里已经有一段时间了。进入 3.0 版本。
# 最终模型:版本 3.0
这个模型比前两个版本做了一些关键的改进。也就是说,我为六个音高类型组(FF,SI,FC,ch,SL,CU)中的每一个都创建了模型,而不是仅仅两个(FB,非 FB),这要归功于更高效的代码(函数!!!).能够为每个音高类型而不是宽泛的音高类型组选择特征极大地提高了 RMSE 模型的性能。
我将按照传统的数据科学工作流程对这一版本进行分解,涉及数据获取、数据清理和特征创建、特征选择、模型创建、模型验证、结果以及一点分析。
## 数据采集
我使用比尔·佩蒂的 [baseballr](http://billpetti.github.io/baseballr/) 包和 scrape_statcast_savant()函数获取了这个项目的所有数据,获得了 2020 赛季的每一场比赛。(其他季节的数据也可以,但我没有计算能力来预测多个季节。)这给了我一个超过 250,000 个观察值和每个观察值的大约 90 个变量的数据集。非常感谢[baseballsavant.com](https://baseballsavant.mlb.com/statcast_leaderboard)将这些数据公之于众,让这个项目成为可能。
## 数据清理和特征创建
创建音调级质量度量的最大问题是找到一个好的响应变量。我看到过一些类似的指标试图预测摇摆罢工的概率,或者试图预测 [CSW%](https://www.pitcherlist.com/csw-rate-an-intro-to-an-important-new-metric/) 。从一开始,我就想预测跑垒得分,即每一次投球的平均跑垒得分。不出所料,这个指标并不容易获得,所以我在[关于线性权重的 Fangraphs 词汇表帖子](https://library.fangraphs.com/principles/linear-weights/)的帮助下自己计算了一下。
这个项目的这一部分大部分是从我以前做的项目中截取的,我以前做的项目专注于推导本赛季每个投球的跑分值。在这里,我做了几个重要的选择,例如,把导致保送的投球视为具有球的价值(不是保送),把导致三振的投球视为具有好球的价值。这些选择是风格上的,可以挑剔或容易调整。
除了创建我的响应变量 Run 值之外,我还创建了几个名为 velo_diff、hmov_diff 和 vmov diff 的新变量,它们代表每个非快速球投球的速度、水平移动和垂直移动与投手快速球平均值的差异。这些变量只考虑了非快速球模型。
注意:在整个过程中,我使用六个音高类型组。这些是四缝线快速球(FF)、伸卡球(Si)、切球(FC)、变速球(包括标记为变速球 ch 和分裂球 FS 的球)、滑球(SL)和曲球(标记为曲球 CU 或指节曲线球 KC 的球)。从哲学上讲,使用 MLBAM 音高分类让我有点痛苦,但我认为在这种情况下是没问题的。其他投球类型,如关节球、投球和投球出局都被删除。
最后,我删除了重要列中缺少值的行(这只是几百个观察值),并翻转了一些左手投手的测量值,使其与右手投手的测量值在相同的范围内。
## 特征选择
作为棒球迷和分析师,我们知道,使一个球场有价值或没有价值的方面可能会因球场类型而有很大不同。因此,在决定在我们的模型中使用哪些功能时,将音高类型考虑在内非常重要。我创建了一个函数来告诉我哪些特性对预测运行值最重要(使用 Boruta 算法)。以下是非快速球的代码:
```
Boruta_PitchType <- Boruta(lin_weight ~ release_speed + release_pos_x_adj + release_extension + release_pos_z + pfx_x_adj + pfx_z + plate_x + plate_z + release_spin_rate + release_spin_direction + velo_diff + hmov_diff + vmov_diff, data = rr_data_sampled)
```
然后,我将我的数据帧分成六个,每个音高类型组一个,并对每个音高类型特定的数据帧运行该函数,存储结果以备后用。
```
#Fastballs- 4 Seam
ff_data <- season_mlb5%>%
filter(pitch_type == "FF")FF_features <- feature_selection(ff_data)
```
## 模型创建
现在我们知道了我们将使用哪些变量来模拟运行值,我们需要一个模型!该指标的 2.0 版使用随机森林,3.0 版也是如此。我尝试使用极端的梯度增强,但是最初的 XGBoost 模型并没有比 2.0 版本更好,所以我坚持使用随机森林。
我创建了一个函数,它接受一个数据框和一组要素作为输入,并拟合一个随机森林模型,返回模型本身作为输出。
```
rf_model <- function(df, features) {
features1 <- append(features, c("lin_weight"))
df1 <- df[,features1]
randomForest(lin_weight ~ ., data = df1, importance = T)
}
```
## 模型验证
接下来,我创建了一个函数来验证这些模型。该函数将输入数据集随机分为训练集(70%)和测试集(30%),拟合投手和击球手排(LvL、LvR、RvL、RvR)的每个组合的数据模型,将模型预测应用于测试集,并输出 RMSE。我对每个特定音高类型的数据帧运行了这个验证函数,总共创建了 24 个随机森林模型。
```
ff_w_preds_val <- validate(ff_data, FF_features)
```
每个音调类型特定数据帧的验证 RMSE 在 0.08 和 0.11 之间,这意味着每个游程值预测偏离其实际游程值平均 0.08 到 0.11 个游程。在理想情况下,我会在这里使用 K 重交叉验证,而不是每个模型只验证一次。然而,当我运行代码时,这些值几乎总是在这个范围内,交叉验证将花费比我愿意花费的更多的时间。
当我将所有的测试数据聚集回一个数据集时,我的总验证 RMSE 大约是 **0.10 次运行**,这与 2.0 版本的验证 RMSE 非常相似。看起来不错。
## 结果
这里的最后一步是在 2020 年将模型应用于所有球场。同样,我创建了一个函数来做这件事,大大降低了这一步的运行时间!我将每个音高类型特定的数据帧和来自特征选择步骤的相应特征列表传递到函数中,并获得最终预测。
```
ff_w_preds <- application(ff_data, FF_features)
```
预测的总聚合 RMSE 也大约为 **0.10 次**。2.0 版本的最终 RMSE 约为 0.14,因此相比之下,我对 3.0 版本的性能提升感到满意。最后,我将预测重新加入到原始数据框架中进行分析。
## 分析
除了排行榜,我马上会讲到,我可以用这些预测制作一些有趣的图表。第一个是简单的预测图(称为预期运行值或 xRV ),绘制在走向区域的十六进制图上:

很明显,蓝色意味着该区域的球场有一个对投手有利的负 xRV,而红色六边形有一个对进攻有利的正 xRV。看到好球带内的球对投手团队来说是典型的好球是很酷的,中中球比好球带外的球要好!
我把这个图分解成特定的投手和感兴趣的投手类型(感谢 patchwork 和 stat_summary_hex()函数!),喜欢变速球最好的投手…

…和弧线球…

…2020 年。对于像雅各布·德格罗姆这样的个人投手来说,情况也是如此。

**排行榜**
我知道一些读者只是想知道谁在这个指标上表现得好,所以这里有一些排行榜。我不认为我能把这些表格嵌入到 Medium 中,所以享受这些截图吧!在这里,我还包括一个称为 xRV+的 xRV 重新标度,其中 100 是该音高类型的平均值,每增加 10 点(110 xRV+)就是平均值以上的一个标准差。请记住,xRV 是一个速度统计,当汇总到投手或投球类型等级时,意味着单位是“每 100 次投球允许的得分”
**解读示例:**“尼克·特利的快速球的 xRV 为-1.43 分,所以我们预计他平均每 100 个快速球阻止了 1.43 分。”
*所有排行榜都是 min。150 个音高。*
2020 年最佳快速球:

2020 年最佳下沉球:

2020 年最佳刀具:

2020 年最大变化:

2020 年排行榜:

2020 年最佳曲线球:

## 限制
我了解到自我评估在研究领域和一般情况下非常重要,所以下面是我对这个模型不足之处的想法。
1. 有人告诉我,这个问题通常最好用极端梯度推进模型来解决。如果我有更多的时间,我会喜欢尝试这种方法,并与这个版本进行比较。可能是一个很好的改进机会。
2. 我有点担心这个模型有点过度拟合,因为最终的 RMSE 和验证的 RMSE 基本上是一样的。虽然我知道使用随机森林很难过度拟合,但我认为这种“切片和骰子”的方法(24 种不同的模型!)可能就是这么做的。
3. 我希望我能使用更多季节的数据。我的计算能力限制了这种可能性,但有一些方法我没有考虑到,直到为时已晚。我希望能够评估 xRV 每一季的“粘性”,以及这一指标从一年到下一年的预测性。这可以帮助我解决我对过度合身的怀疑!
4. 我认为一个单一的模型会更加简洁,运行速度更快,所以如果有人可以复制这种方法的结果,我会很乐意找到它。
5. 早些时候,我决定让这个模型“上下文无关”,在我创建的 response 列中不考虑比赛的比赛情况(例如,0–0 和 0–2 的全垒打具有相同的重量,满垒和空垒的全垒打具有相同的值)。我想知道我是否应该在某个时候将上下文折叠回去,使这个度量标准在现实生活中更适用。
总的来说,我认为这个项目有很大的改进空间。我不期望愿意或能够将这个项目进行得更深入,但希望通过写这篇文章并在这里提供我的代码,我可以成为下一个想尝试这个项目的人的出发点!
再次,我的这个项目的代码库可以在这里找到。
## 补充视频
我制作了一个视频,在那里我找到了 xRV 的一些最好和最差的投手投球和个人投球,并用 baseballsavant.com 以一种更有趣和非正式的形式做了一些现场分析。那个环节就是[这里的](https://youtu.be/Rcx8TsCjxBs)!
## 关于 QOS+的说明:
本赛季,Eno Sarris 在《The Athletic》上发表的几篇文章中使用了 xRV 的一个兄弟指标,名为 Quality of Stuff Plus (QOS+)。除了本文详细介绍的对 xRV 的改进,QOS+也做了后续的(和类似的)调整。这些指标之间有一些关键的差异,但最相关的一点是 QOS+在其预测中不考虑沥青的板位置。对于 QOS+的任何未来使用,预计这些变化将得到反映。
# 是的,人工智能会取代你的工作。
> 原文:<https://towardsdatascience.com/yes-ai-will-take-your-job-7edcb18a1ac0?source=collection_archive---------23----------------------->
## 意见
## 对不起,但这是真的。

马丁·克劳森在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
> *“骄傲的人总是俯视一切……只要你在俯视,你就看不到高于你的东西。”— C.S .刘易斯*
人类是骄傲的。
我们认为人类的智力是最高的,因此嘲笑人工智能,甚至嘲笑它令人难以置信的智力和创造力。
AI 可以[用任何语言](/will-gpt-3-kill-coding-630e4518c04d)编码,做出令人惊叹的艺术(我们会深入探讨),用特斯拉的全自动驾驶比我们开得更好。这些都是去年的进步。
认为人工智能最终不会比你做得更好是纯粹的骄傲,或者是对指数增长的无知。
# 指数改进
人工智能生成器——无论是文本、艺术、代码还是音乐——曾经是古怪、有趣的小工具,产生你在左边看到的东西(一个到处都是眼睛和狗脸的发烧梦怪物)。现在,你可以告诉 AI 画一个想法(例如,“鳄梨形状的扶手椅”),它会立即画出创造性的功能性艺术。
[文案。AI](http://copyist.ai) 是我发现的基于 OpenAI 的 GPT-3 的许多最新产品之一,它使用该技术来生成营销文案,包括广告、增长想法、文章大纲和 SEO 优化。上面右边的艺术是由 [DALL-E](https://openai.com/blog/dall-e/) 制作的,它还没有对公众开放(在 OpenAI 团队之外)。)然而,公开的是文本生成(比如文章标题、广告文案、营销理念等等。)我向 GPT 3 频道询问了关于“人工智能失业”的文章标题想法,以下是我立刻得到的结果:
* “机器人的崛起和工作岗位的减少:人工智能将如何影响经济”
* “机器会来抢你的工作”
* "当机器人取代人类时,劳动力会发生什么变化?"
无论你是一名程序员、插画师、广告文案、优步司机、工厂工人……或者,嗯,任何事情,不需要太多想象力就能看到人工智能在某个时候取代你的工作。
除了自傲,人类在理解指数增长方面也是出了名的糟糕。这就是为什么新冠肺炎让世界大部分地区感到惊讶,尽管专家们认为这一天即将到来。1 个病例变成 2 个,4 个,8 个,在你知道之前,将近 1 亿人(截至发稿时)已经或曾经患有新冠肺炎。
人工智能是指数增长的另一个例子,它从小到大,无足轻重,到目前在许多方面与人类不相上下或超过人类。很快,人工智能将比我们聪明几十亿倍。
# 长期思考
对于指数增长函数,很难想象长期的未来会是什么样子——但这是不可避免的,所以我们至少应该考虑一下。
许多人认为在短期或中期内,人工智能*创造就业*可能会实际发生,但当考虑更大的时间范围时,这真的无关紧要(不仅仅是工厂自动化的人工智能——而是我们上面探索的一些内容,如设计、营销和编码)。
从 1990 年到 2007 年,工厂自动化已经导致美国失去了 [40 万个](https://time.com/5876604/machines-jobs-coronavirus/)工作岗位。到 2025 年,将有 200 多万个制造业岗位被自动化淘汰。
但美国不是一个制造业经济体,而且很长一段时间以来都不是。不到 1300 万美国人受雇于制造业,而大约 6000 万美国人是知识工作者——在设计、营销、编码等领域。
在全球范围内,预计到 2030 年,自动化将取代多达 [8 亿个工作岗位](https://www.mckinsey.com/featured-insights/future-of-work/jobs-lost-jobs-gained-what-the-future-of-work-will-mean-for-jobs-skills-and-wages),但即使是这些估计也在很大程度上没有考虑到最近的进步,如 GPT-3、DALL-E,以及对这些模型进行许多数量级改进的必然性。
# 解决异议
考虑到人们对人工智能优越性和人工智能导致的失业的许多反对意见,这是很有趣的。我已经提到骄傲和无知导致许多人批评 AI,但是让我们仔细看看。
在一篇名为《[最新的 AI 会杀死编码吗?](/will-gpt-3-kill-coding-630e4518c04d)”其中一条顶级评论是这样说的:
> *“程序员需要提供一个例子。”*
这是一个无法理解指数增长的例子,因为 AI 自然会改进到不需要例子来创建代码的地步。事实上,*自从 6 个月前的那条评论*以来,它已经走到了那一步。GPT-3 " [指令系列](https://www.reddit.com/r/GPT3/comments/kiym80/a_new_gpt3_modelengine_called_the_instructseries/)"支持生成文本,包括代码,无需给出示例。
另一条评论是这样说的:
> *“你知道你对你想要的行为的描述具体到足以生成一个工作软件吗?码。”*
这不是真的。这都是我告诉 GPT-3(没有给出代码示例)制作一个工作网站的功能描述:
> *创建一个工作的 HTML 网站,在一个写着“怀疑 X”的按钮上方写着“这个网站是在没有程序员的情况下创建的,兄弟”,当点击该按钮时,会创建一个弹出窗口,显示“它确实是”*
下面是 GPT-3 在点击上述请求的“提交”后立即生成的代码:
```
<html><head><title>this website was made without a programmer, bro</title></head><body><h1>this website was made without a programmer, bro</h1><button onclick="alert('it really was')">X to doubt</button></body></html>
```
是的,这个网站是有效的。这是一个有趣的例子,但这很容易被生产成更大的东西。

下面是这篇文章的另一个流行评论:
> “GPT-3 是有史以来最大的查找表。”
回想一下之前的鳄梨椅——它不存在于互联网上,所以不能通过查找来检索。它重新产生了。类似地,我之前生成的文章标题“机器人的崛起和工作岗位的减少:人工智能将如何影响经济”,在谷歌上返回 0 个结果。我刚刚在上面做的网站并不存在,除了在我的脑海里。
另一个评论通过攻击特斯拉进行了一次稻草人式的辩论:
> *“这就是为什么特斯拉汽车仍然会撞上箱式卡车。”*
现实世界的统计数据显示,特斯拉已经[远比人类安全](https://www.claimsjournal.com/news/national/2018/10/08/287154.htm)。
我们可以一整天都在批评 GPT-3,甚至更广泛地批评艾。在波士顿动力公司的视频中,机器人随着“你爱我吗?,“你会看到类似“这是 CGI”的评论
人类真的非常骄傲。我很抱歉,但是 AI 会接替你的工作。
# 是的,XGBoost 很酷,但是你听说过 CatBoost 吗?
> 原文:<https://towardsdatascience.com/yes-xgboost-is-cool-but-have-you-heard-of-catboost-1930ebacd36d?source=collection_archive---------28----------------------->
## 对这个现代梯度推进库的介绍

在 [Unsplash](https://unsplash.com/s/photos/cat?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上由 [Manja Vitolic](https://unsplash.com/@madhatterzone?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
如果您曾经做过数据科学家,参加过 Kaggle 竞赛,甚至在互联网上浏览过数据科学文章,那么您很有可能听说过 XGBoost。即使在今天,它也经常是许多从事一般机器学习任务的 Kagglers 和数据科学家的首选算法。
虽然 XGBoost 很受欢迎,但它确实有一些局限性,我在下面的文章中提到了这一点。
</why-xgboost-cant-solve-all-your-problems-b5003a62d12a>
您可能听说过 XGBoost,但是您听说过 CatBoost 吗?CatBoost 是 Yandex 的研究人员创建的另一个开源梯度增强库。虽然它可能比 XGBoost 慢,但它仍然有几个有趣的特性,可以作为替代方案使用,或者包含在 XGBoost 的集成模型中。对于一些基准数据集,CatBoost 甚至优于 XGBoost。
**在本文中,我将把这个框架与 XGBoost 进行比较,并演示如何在一个简单的数据集上训练 CatBoost 模型。**
# CatBoost 和 XGBoost 有什么不同?
和 XGBoost 一样,CatBoost 也是一个梯度提升框架。但是,CatBoost 有几个与 XGBoost 不同的特性,如下所示:
* **CatBoost 是梯度增强的一种不同实现,它利用了一种称为*有序增强*的概念,这在** [**CatBoost 论文**](https://arxiv.org/pdf/1706.09516.pdf) **中有详细介绍。**
* **因为 CatBoost 具有不同的梯度增强实现,所以它有可能在某些任务上胜过其他实现。**
* **CatBoost 具有用于交叉验证和网格搜索的可视化小部件,可在 Jupyter 笔记本中查看。**
* **CatBoost 中的** [**池模块**](https://catboost.ai/docs/concepts/python-reference_pool.html) **支持分类和文本特征的预处理。**
要获得完整的特性列表,请务必查看 [CatBoost 文档页面](https://catboost.ai/docs/concepts/about.html)。虽然 CatBoost 确实有额外的特性,但是这个实现的主要缺点是它通常比 XGBoost 慢。但是如果你愿意牺牲速度,这种权衡在某些情况下可能是合理的。
# 装置
要安装带有 pip 的 CatBoost,只需运行下面列出的命令。
```
pip install catboost
```
或者,您也可以使用以下命令将 CatBoost 与 Conda 一起安装。
```
conda config --add channels conda-forge
conda install catboost
```
# 使用 CatBoost 分类
在本教程中,我将演示如何使用通过 Scikit-learn 生成的简单数据集,通过 CatBoost 训练分类模型。你可以在 [GitHub](https://github.com/AmolMavuduru/CatBoostTutorial) 上找到这个教程的[完整代码](https://github.com/AmolMavuduru/CatBoostTutorial)。
## 导入库
在下面的代码中,我从 CatBoost 导入了 Numpy 和 Pandas 等基本库以及一些模块。
```
import numpy as np
import pandas as pd
from catboost import CatBoostClassifier, Pool, cv
```
## 创建数据集
在下面的代码中,我使用 Scikit-learn 中的 make_classification 函数创建了一个数据集。
```
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=50000,
n_features=20,
n_informative=15,
n_redundant=5,
n_clusters_per_class=5,
class_sep=0.7,
flip_y=0.03,
n_classes=2)
```
接下来,我们可以使用下面的代码将数据集分成训练集和测试集。
```
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
```
## 训练模型
CatBoost 有一个非常简单的 Scikit-learn 风格的 API 用于训练模型。我们可以实例化一个 CatBoostClassifier 对象,并根据训练数据对其进行训练,如下面的代码所示。请注意,迭代参数对应于提升迭代的次数(或树的数量)。
```
model = CatBoostClassifier(iterations=100,
depth=2,
learning_rate=1,
loss_function='Logloss',
verbose=True)model.fit(X_train, y_train)
```
当 verbose 参数设置为 True 时,训练模型会将每次迭代中的训练损失写入标准输出。

通过训练模型生成的标准输出日志。
请注意总时间以及剩余时间是如何写入标准输出的。
## 计算特征统计
我们还可以使用 calc_feature_statistics 函数从训练数据集中计算详细的要素统计数据,如下所示。
```
model.calc_feature_statistics(X_train, y_train, feature=1, plot=True)
```
请注意,feature 参数指示要为哪个特性计算统计数据。该参数可以是索引的整数、指定功能名称的字符串或指定多个功能的字符串或整数列表。

用 CatBoost 生成的特征统计图。
上图有助于我们理解模型在根据不同箱中的特征值预测目标时的行为。这些条柱对应于指定特征的不同值范围,并在 CatBoost 模型中创建决策树时使用。
## 获取功能重要性
我们还可以使用经过训练的 CatBoost 模型来计算特征重要性。为此,我们首先必须获取训练数据,并使用池模块将其转换为预处理的 CatBoost 数据集。之后,我们可以简单地使用 get_feature_importance 函数,如下所示。
```
train_data = Pool(data=X_train, label=y_train)model.get_feature_importance(train_data)
```
该函数返回一个 Numpy 数组的要素重要性,如下所示。
```
array([3.01594829, 7.75329451, 5.20064972, 4.43992429, 4.30243392,
8.32023227, 9.08359773, 2.73403973, 7.11605088, 2.31413571,
7.76344028, 1.95471762, 6.66177812, 7.78073865, 1.63636954,
4.66399329, 4.33191962, 1.836554 , 1.96756493, 7.12261691])
```
## 交叉验证
为了使用 CatBoost 执行交叉验证,我们需要完成以下步骤:
1. 使用池模块创建预处理数据集。
2. 为 CatBoost 模型创建一个参数字典。
3. 使用 cv 函数为模型生成交叉验证分数。
请注意,Pool 模块还包括用于预处理文本和分类特征的可选参数,但是由于我们数据集中的所有特征都是数值型的,所以在本例中我不需要使用这些参数。
```
cv_dataset = Pool(data=X_train,
label=y_train)params = {"iterations": 100,
"depth": 2,
"loss_function": "Logloss",
"verbose": False}scores = cv(cv_dataset,
params,
fold_count=5,
plot="True")
```
在 plot 参数设置为 True 的情况下运行上面的代码,可以得到一个很酷的小部件,如下图所示。

CatBoost 交叉验证小部件。
在左侧,我们可以看到每个折叠的交叉验证结果,在右侧,我们可以看到一个图表,其中包含模型的平均学习曲线以及标准偏差。x 轴包含迭代次数,y 轴对应于验证损失值。
## 网格搜索
我们还可以执行网格搜索,其中库比较不同超参数组合的性能,以找到最佳模型,如下所示。
```
model = CatBoostClassifier(loss_function='Logloss')grid = {'learning_rate': [0.03, 0.1],
'depth': [4, 6, 10]}grid_search_result = model.grid_search(grid,
X=X_train,
y=y_train,
cv=3,
plot=True)
```
运行上面的代码会产生下面 GIF 中演示的小部件。

CatBoost 网格搜索小部件。
我们可以通过选择 **params** 属性来访问网格搜索中的最佳参数。
```
print(grid_search_result['params'])
```
上面的 print 语句为我们提供了网格搜索中的最佳参数,如下所示。
```
{'depth': 10, 'learning_rate': 0.1}
```
## 测试模型
我们可以通过运行 predict 函数从经过训练的 CatBoost 模型中生成预测。
```
model.predict(X_test)
```
运行上面的 predict 函数会产生一个 Numpy 类标签数组,如下所示。
```
array([0, 1, 0, ..., 0, 1, 1])
```
如果我们想要评估模型在测试数据上的性能,我们可以使用 score 函数,如下所示。
```
model.score(X_test, y_test)
```
运行上面的函数产生了以下输出。
```
0.906
```
基于以上结果,我们可以看到该模型达到了 90.6%的测试准确率。
## 保存模型
您还可以将 CatBoost 保存为各种格式,例如 PMML,如下面的代码所示。
```
model.save_model(
"catboost.pmml",
format="pmml",
export_parameters={
'pmml_copyright': 'my copyright (c)',
'pmml_description': 'test model for BinaryClassification',
'pmml_model_version': '1'
}
)
```
# 摘要
CatBoost 是一个更新的梯度增强框架,具有额外的特性,值得考虑作为 XGBoost 的潜在替代方案。它可能没有 XGBoost 那么快,但是它确实有一些有用的特性,并且有可能在某些任务上超过 XGBoost,因为它是梯度增强的改进实现。
像往常一样,你可以在 [GitHub](https://github.com/AmolMavuduru/CatBoostTutorial) 上找到本文使用的代码。
# 加入我的邮件列表
你想在数据科学和机器学习方面变得更好吗?您想了解数据科学和机器学习社区的最新图书馆、开发和研究吗?
加入我的[邮件列表](https://mailchi.mp/e8dd82679724/amols-data-science-blog),获取我的数据科学内容的更新。当你[注册](https://mailchi.mp/e8dd82679724/amols-data-science-blog)的时候,你还会得到我免费的**解决机器学习问题的逐步指南**!也可以在 [Twitter](https://twitter.com/amolmavuduru1) 关注我,了解内容更新。
当你这么做的时候,考虑加入媒体社区,阅读成千上万其他作家的文章。
# 来源
1. 长度普罗霍伦科娃,古塞夫等。艾尔, [CatBoost:具有分类特征的无偏增强](https://arxiv.org/pdf/1706.09516.pdf),(2019),arXiv.org。
# 是的,你可以在 M1 的 MacBooks 上运行 PyTorch,具体操作如下
> 原文:<https://towardsdatascience.com/yes-you-can-run-pytorch-natively-on-m1-macbooks-and-heres-how-35d2eaa07a83?source=collection_archive---------1----------------------->
## 在 M1 苹果电脑上安装 PyTorch 并训练你的第一个神经网络——一个完整的分步指南

照片由[伊戈尔·莱皮林](https://unsplash.com/@ilepilin?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/torch?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄
M1 macbook 已经不再是新的了。不知何故,安装 Python 的深度学习库仍然不是一个简单的过程。至少有了 TensorFlow。PyTorch 不一样。
今天您将学习如何在您的 M1 机器上安装和运行 PyTorch。你用哪台 M1 电脑(Air、Pro、Mini 或 iMac)都没什么区别。
让我们开始吧。
# 步骤 1 —安装和配置 Miniforge
我花了很多时间为数据科学配置 M1 Mac 电脑。它从来没有一点瑕疵。直到我发现了这个选项。根据网速的不同,完全设置需要 5 到 10 分钟。
首先,你需要安装[自制软件](https://brew.sh/)。这是一个用于 Mac 的软件包管理器,您可以通过从终端执行以下行来安装它:
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
请记住——如果你正在安装一台新的 M1 Mac,很可能你不会安装自制软件所需的 XCode 构建工具。终端会通知您这些是否丢失,并询问您是否要安装它们。
一旦安装了 XCode 构建工具和 Homebrew,您就可以重启终端并安装 Miniforge:
```
brew install miniforge
```
这是几百 MB 的下载,所以需要一些时间来完成。完成后,初始化 Z 外壳(zsh)的 conda:
```
conda init zsh
```
最后,重启终端。**就是这样!** Miniforge 现已安装,您可以开始创建虚拟环境了。接下来我们就这样做吧。
# 步骤 2 —创建虚拟环境
以下终端命令将基于 Python 3.8 创建一个名为`pytorch_env`的新虚拟环境:
```
conda create --name pytorch_env python=3.8
```
创建后,使用以下命令激活它:
```
conda activate pytorch_env
```
您应该会看到类似这样的内容:

图 1 —激活 PyTorch 环境(图片由作者提供)
接下来让我们安装库。
# 步骤 3 —安装 PyTorch
你可以在 Anaconda 的网站上找到一个本地 PyTorch 包。您应该在 Platform 下查找`osx-arm64`,它会告诉您它是 M1 兼容的:

图 2 —蟒蛇身上的 PyTorch 包(图片由作者提供)
点击软件包将为我们提供安装说明,如下所示:

图 3 — PyTorch 安装说明(图片由作者提供)
您可以使用以下命令安装 PyTorch 和 Torchvision(针对数据集):
```
conda install -c pytorch pytorch torchvision
```
就这样——py torch 现在安装好了!接下来让我们测试这该死的东西。
# 步骤 4 —测试
让我们保持简单,不要从头开始写任何东西。PyTorch 的 GitHub 页面附带了许多例子,其中之一是用于训练手写数字分类器的脚本( [link](https://github.com/pytorch/examples/blob/master/mnist/main.py) )。
只需下载脚本并从终端运行它:
```
python main.py
```
您将看到以下输出:

图片 4 — PyTorch 模型培训(图片由作者提供)
一切按预期运行!让我们打开活动监视器来验证 Python 是否正在本机运行:

图 5——Mac 上的活动监视器(图片由作者提供)
如果你在“Kind”下看到“Apple”,这意味着程序是在 M1 芯片上本地运行的,而不是在 Rosetta 仿真器下。
检查!
# 最后的想法
现在,PyTorch 已经成功安装在 M1 机器上。你不能对 TensorFlow 遵循同样的步骤,因为一旦你开始训练模型,Python 就会崩溃。如果你想让我也涵盖 TensorFlow 安装,请告诉我。
您必须承认,原生安装 PyTorch 比您预期的要容易。当然,它不会像在 GPU 上运行得那么快,但它足以让你开始学习和尝试。
感谢阅读。
*喜欢这篇文章吗?成为* [*中等会员*](https://medium.com/@radecicdario/membership) *继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。*
<https://medium.com/@radecicdario/membership>
# 了解更多信息
* [2021 年学习数据科学的前 5 本书](/top-5-books-to-learn-data-science-in-2020-f43153851f14)
* [如何使用 Cron 调度 Python 脚本——您需要的唯一指南](/how-to-schedule-python-scripts-with-cron-the-only-guide-youll-ever-need-deea2df63b4e)
* [Dask 延迟—如何轻松并行化您的 Python 代码](/dask-delayed-how-to-parallelize-your-python-code-with-ease-19382e159849)
* [如何使用 Python 创建 PDF 报告—基本指南](/how-to-create-pdf-reports-with-python-the-essential-guide-c08dd3ebf2ee)
* [即使没有大学文凭也要在 2021 年成为数据科学家](/become-a-data-scientist-in-2021-even-without-a-college-degree-e43fa934e55)
# 保持联系
* 关注我在[媒体](https://medium.com/@radecicdario)上的更多类似的故事
* 注册我的[简讯](https://mailchi.mp/46a3d2989d9b/bdssubscribe)
* 在 [LinkedIn](https://www.linkedin.com/in/darioradecic/) 上连接
# 又一个最大的神经网络——但是为什么呢?
> 原文:<https://towardsdatascience.com/yet-another-largest-neural-network-but-why-f48d231972a9?source=collection_archive---------13----------------------->
## 人工智能|新闻|观点
## GPT 3 号的成功不断带来回报,但是我们真的还需要这样做吗?

纳丁·沙巴纳在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
它又发生了。微软和英伟达已经建立了一个新的最大的密集语言模型——是前冠军 GPT-3 的三倍。然而,与 GPT-3 相比,这个新模型没有引起任何骚动,无论是在媒体还是在人工智能社区。这是有原因的。
我以前多次谈到过大型语言模型(LLM),这是一个通用术语,用于用大量数据预先训练的基于大规模转换器的神经网络。我以前的文章有明确的技术语气,回答了像它们为什么存在,它们有什么能力,或者它们有什么局限性这样的问题。直到现在,我还没有努力后退一步,尝试在我们社会的大背景下理解 LLM,以及它们如何与人工智能和技术的其他方面相互关联。
今天,我将从另一个角度来解决一些我们需要分析以避免盲目行走的关键问题:我们真的需要另一个最大的神经网络吗?这些模型的目的是什么,人工智能社区期望从中获得什么?无止尽地遵循缩放模型的道路会有什么后果——特别是对于人工智能社区,以及整个世界?如果我们给人工智能和人工智能的其他方法更多的空间,不是更好吗(AGI)?
# 大型语言模型——划分人工智能社区
自从 2017 年[的变形金刚架构](https://arxiv.org/abs/1706.03762)的流行,以及 2018 年的 BERT [和 2020 年](https://arxiv.org/abs/1810.04805)的 GPT-3 [的成功,LLM 的趋势已经无处不在。在 Google、OpenAI 和其他公司证明了这些模型的有效性和实用性之后,世界各地的研究人员和开发人员开始复制他们的方法。LLM 现在如此普及,以至于我们几乎把它当成了深度学习的同义词。](https://arxiv.org/abs/2005.14165)
最近,几十名斯坦福大学的研究人员将法学硕士命名为“基础模型”。他们认为,这些越来越大的神经网络构成了“构建人工智能系统的新兴范式。”但并不是所有的 AI 专家都认同这个花哨的标题。
伯克利的计算机科学教授 Jitendra Malik 说“基金会这个词是非常错误的。”他补充说,“我们在这些模型中使用的语言是没有根据的,有这种虚假,没有真正的理解。”佐治亚理工学院[的教授 Mark Riedl 在 Twitter 上说](https://twitter.com/mark_riedl/status/1428398136939192328)“将非常大的预训练神经语言模型作为‘基础’模型是一个辉煌的……公关噱头。”
虽然他们承认这些模型的有效性,但他们批评这些模型缺乏理论背景。事实证明,LLM 在语言甚至[视觉任务](https://arxiv.org/abs/2010.11929)方面比以前的模型做得更好,但他们缺少对语言和世界的深刻理解,而这是任何被称为“基础”的东西所需要的。
长期深度学习评论家加里·马库斯(Gary Marcus)在[中就该主题进行了彻底的论证](https://thegradient.pub/has-ai-found-a-new-foundation/#:~:text=In%20the%20final,models%20at%20all%3F),称“将‘预训练语言模型’重新称为基础模型是一种误导……该报告称,毫无疑问,‘我们并不完全了解基础模型所提供的基础的性质或质量’,但为什么要大言不惭地称它们为基础模型呢?”
但是“基础模型”并不是人工智能社区给予 LLM 的唯一头衔。在一篇日期为 2021 年 3 月的论文中,艾米丽·m·本德、蒂姆尼特·格布鲁和其他人将这些模型称为“随机鹦鹉”。他们分析了与 LLM 相关的不同风险和成本,并得出结论,研究人员在从事这些研究时应该小心谨慎。
这一段最好地抓住了他们的立场:“从互联网摄取的训练数据编码霸权世界观的趋势,LMs 放大训练数据中的偏见和其他问题的趋势,以及研究人员和其他人将 LM 驱动的性能提升误认为实际自然语言理解的趋势……呈现了现实世界的伤害风险。”
无论是“基础模型”还是“随机鹦鹉”,很明显 LLM 在人工智能的当前状态中发挥着巨大的作用。专家们持有不同的观点,也没有明确的前进方向。我们不知道他们的技术极限,我们不知道我们是否能够保持目前的进展速度,我们也不知道他们对社会和环境影响的潜在程度。
# 威震天-图灵 NGL 530 b-另一个 GPT-3
几天前 Nvidia [在其博客中发布了](https://developer.nvidia.com/blog/using-deepspeed-and-megatron-to-train-megatron-turing-nlg-530b-the-worlds-largest-and-most-powerful-generative-language-model/)消息。它与微软联手带来了“世界上最大和最强大的生成语言模型。”威震天-图灵 NGL 530B (MT-NGL)现在是最大的密集神经网络(最大的稀疏神经网络仍然是[武道 2.0](/gpt-3-scared-you-meet-wu-dao-2-0-a-monster-of-1-75-trillion-parameters-832cd83db484) ),拥有 5300 亿个参数——比 GPT-3 大三倍——被创建来“推进自然语言生成的人工智能的艺术状态。”
根据这篇博文,MT-NGL 在零镜头、一镜头和少量镜头设置方面超过了 GPT-3——这表明了该模型在面临新任务之前看到的例子数量。它还提高了一些语言生成任务的准确性,如补全、阅读理解或常识推理。
为了训练如此庞大的模型,这些公司不得不寻找新的解决方案来解决老问题,即硬件-软件限制和与长训练时间相关的成本。他们将超级计算机英伟达塞勒涅和微软 Azure NDv4 的能力与最先进的人工智能训练软件结合起来——Megatron-ML 和 DeepSpeed 。
# 为什么我们一直在建造 LLM?
MT-NGL 是一个梦幻般的模型,但它听起来并不令人印象深刻,不是吗?GPT 3 号是一个新奇的东西,一个意想不到的突破。现在,我们已经厌倦了听到 LLM 打破了下一个障碍——当这个障碍只是向前迈出的一小步,却带来了一连串的后果。微软和 Nvidia [似乎担心](https://developer.nvidia.com/blog/using-deepspeed-and-megatron-to-train-megatron-turing-nlg-530b-the-worlds-largest-and-most-powerful-generative-language-model/#:~:text=Our%20observations%20with%20MT-NLG%20are%20that%20the%20model%20picks%20up%20stereotypes%20and%20biases%20from%20the%20data%20on%20which%20it%20is%20trained.%20Microsoft%20and%20NVIDIA%20are%20committed%20to%20working%20on%20addressing%20this%20problem.%20We%20encourage%20continued%20research%20to%20help%20in%20quantifying%20the%20bias%20of%20the%20model.)他们系统的潜在问题:
“我们对 MT-NLG 的观察是,该模型从其训练的数据中提取了刻板印象和偏见。微软和 NVIDIA 致力于解决这个问题。我们鼓励继续研究,以帮助量化模型的偏差。”那很好,但是他们把它当作下游要做的事情。他们强调的事实是,模型是最大和最强大的语言生成器。偏见可以事后解决。
这不仅仅是偏见。MT-NGL 的训练方式与 GPT-3 类似——从互联网上获取大量公开文本。后者被证明能够[传播误传](https://www.wired.com/story/ai-write-disinformation-dupe-human-readers/)。如果 MT-NGL 的语言专长更强,预计它制造假新闻的能力也会更强。此外,尽管软件和硬件都有所突破,但培训这些 LLM 的成本仍然非常高。微软和英伟达是大型科技公司,所以他们买得起,但其他公司的准入门槛不断提高。让我们不要忘记训练人工智能系统对环境造成的损害t。有倡议用可再生能源替代能源,但[这还不够](https://theconversation.com/it-takes-a-lot-of-energy-for-machines-to-learn-heres-why-ai-is-so-power-hungry-151825#:~:text=Unless%20we%20switch%20to%20100%25%20renewable%20energy%20sources%2C%20AI%20progress%20may%20stand%20at%20odds%20with%20the%20goals%20of%20cutting%20greenhouse%20emissions%20and%20slowing%20down%20climate%20change.)。
LLM 是目前照亮人工智能研究的明星,但人们开始更多地关注硬币的黑暗面。这些模型真的有必要吗?它们值得付出社会、环境和经济成本吗?如果有的话,技术的目的是什么?它们是通往 AGI 的最明智的途径吗?还是仅仅是我们仅有的一条途径?
不管后果如何,只有一个合理的动机来继续构建 LLMs 相信比例假说。这个假设说,如果我们继续训练越来越大的神经网络,这些网络被证明工作得相当好,更复杂的行为将会自然地出现这意味着没有必要在系统中硬编码复杂的认知功能,比如推理。因此,那些相信缩放假说的人在其中看到了一条通往人类级别的人工智能的直接路径和解决我们所有问题的方法。
信徒们的思路是这样的:“如果我们沿着伸缩假说的路径,我们将到达 AGI,它可以解决我们的大部分问题——如果不是全部的话。它会造成多种形式的损害(社会、环境、经济、政治),但最终产生的好处将远远超过附带的后果。目的证明手段是正当的。"
然而,没有多少人工智能专家认为扩展神经网络是通往 AGI 的合理途径——至少在没有[理论](https://www.technologyreview.com/2021/03/03/1020247/artificial-intelligence-brain-neuroscience-jeff-hawkins/)、[算法](https://www.gwern.net/Scaling-hypothesis#:~:text=DeepMind21%20holds%20what%20we%20might%20call%20the%20%E2%80%9Cweak%20scaling%20hypothesis%E2%80%9D%3A%20they%20believe%20that%20AGI%20will%20require%20us%20to%20%E2%80%9Cfind%20the%20right%20algorithms%E2%80%9D)或[硬件](https://www.nature.com/articles/s41928-020-0457-1)突破的情况下。我们不需要盲目地走一条看起来(部分)可行的路,因为我们只有这条路。如果我们继续研究,我们可以找到一种方法来推动人工智能的进步,而不需要哀叹伤害——而不是像大技术公司那样认为请求原谅比获得许可更容易。
我们需要改变我们做一些事情的方式——比如直接在神经形态芯片中建立神经网络,而不是虚拟地创建它们。而且,更关键的是,我们需要改变我们在更普遍的意义上对待人工智能的方式——包容、安全、负责、可解释、有道德的人工智能才是重要的,即使它会减缓进展。正如 Peter Norvig [最近写的](https://hai.stanford.edu/news/peter-norvig-todays-most-pressing-questions-ai-are-human-centered),“今天人工智能最紧迫的问题是以人为中心的。”
我们有一些人工智能系统工作得很好。让我们对此感到满意,暂时把注意力放在人类身上。最终,人工智能和我们所做的一切最终都是为了改善人类的整体福祉。还能为什么?
*如果你喜欢这篇文章,可以考虑订阅我的免费每周简讯*<https://mindsoftomorrow.ck.page>**!每周都有关于人工智能的新闻、研究和见解!**
**您也可以直接支持我的工作,使用我的推荐链接* [*这里*](https://albertoromgar.medium.com/membership) *成为中级会员,获得无限权限!:)**
# 又一个显示美国种族不平等的阴谋!
> 原文:<https://towardsdatascience.com/yet-one-more-plot-to-show-uss-race-inequality-c12fa166aee0?source=collection_archive---------49----------------------->

来源:Arafath Hossain
## 关于如何使用 R 制作动画情节的简短教程;并用它来传达美国种族不平等的一个延续方面的旅程。
# 背景
上周的 [TidyTuesday](https://github.com/rfordatascience/tidytuesday/) 全是关于美国的财富不平等。自从我在一所大学工作以来,我已经看到学生的成功在不同种族之间是如何变化的。回顾他们的社会经济背景,很明显隐藏在他们种族背后的财富在他们的成功中扮演了重要角色。这使得学生债务数据成为我研究的一个有趣案例。
这就是结果:一个动画图表将向你展示美国种族在学生债务积累方面的历史差异,以及这种差异是如何日益恶化的。
# 数据
如前所述,数据来自 TidyTuesday。下面是关于数据的[细节](https://github.com/rfordatascience/tidytuesday/blob/master/data/2021/2021-02-09/readme.md)。
```
# libraries
# devtools::install_github("thomasp85/transformr")
library(ggplot2)
library(tidyverse)
library(ggtext)
library(gganimate)
library(extrafont)
library(knitr)
library(kableExtra)# data
student_debt <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-02-09/student_debt.csv')# glimpse of the data
student_debt %>%
head(5) %>%
knitr::kable(caption = "Top 5 lines of the data") %>%
kable_classic(full_width = F, html_font = "Cambria")
```

这些数据的一个优点是它已经被清理了!要了解更多关于清洁步骤的信息,请点击此处的文档。以下是数据的简短描述:

## 制作动画情节(一次一个情节)
我会用`gganimate`包来出剧情。在使用`gganimate`之前,我们将首先使用来自 r 的强大的数据 viz 包`ggplot`制作一个我们喜欢的图形。在下面的代码块和后续段落中,我将一步一步地告诉你如何做。
# 静态线图
让我们从一个非常简单的线图开始,它将显示不同种族学生债务的年度趋势。这个情节将作为骨架,在此基础上,我们将继续添加新的层和附加的铃铛和哨子,使它更好,更有意义。
```
student_debt %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race)
) +
geom_line()
```

一些直接的改进点是:
* 添加标题并标记轴,
* 用不同的颜色分隔线条,
* x 轴和 y 轴上的默认值意义不大。分解 x 和 y 轴的值可能会使它们更有意义。
```
student_debt %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race)
) +
geom_line(aes(color = race)) +
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
labs(title="Average Family Student Loan",
x = NULL, color = NULL, y = "Average Loan Debt")
```

相当大的进步!
# 具有额外洞察力的情节
我们有一个尚未使用但非常重要的数据可供我们使用— `loan_debt_pct`:有学生贷款债务的家庭的百分比。我们可以通过使用新添加的点将这种洞察力添加到我们的情节中!
```
student_debt %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race)
) +
geom_line(aes(color = race)) +
geom_point(aes(
size = loan_debt_pct)
) +
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
labs(title="Average Family Student Loan",
color = NULL,
x = NULL, y = "Average Loan Debt",
subtitle ="Point sizes represent % of families with student loans")
```

现在让我们再做一些改进:
* 不同线上的点的颜色代表相应的种族。让我们用和线条一样的颜色来说明这一点,
* 在这样做的同时,让我们也将颜色从自动生成的`ggplot`颜色更改为我们自定义的颜色(例如,黑人用黑色,白人用白色,西班牙人用棕色)。
* race 变量的图例顺序也与图表上的线条顺序不一致。让我们也重新排列一下传说,
* 尺寸图例也需要一些修改。因为仅仅从一个角度来测量圆圈的大小是很困难的,所以让我们把这些图例去掉,而在图上标出实际的百分比。
```
student_debt %>%
mutate(race = fct_relevel(race, "Black", "White", "Hispanic")) %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race,
color = race)
) +
geom_line(aes()) +
geom_point(aes(
size = loan_debt_pct),
show.legend = FALSE) +
geom_text(aes(
label = paste0(round(loan_debt_pct * 100, 2), "%")
),
show.legend = FALSE,
hjust = 1, vjust = 0) +
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
scale_color_manual(values = c("White" = "#ffffff", "Black" = "#787575", "Hispanic" = "#f5bf42")) +
labs(title="Average Family Student Loan",
color = NULL,
x = NULL, y = "Average Loan Debt",
subtitle ="Point sizes represent % of families with student loans")
```

# 使其在视觉上吸引人
我们的图表几乎准备好了;内容方面。但是视觉上可以好很多。默认 ggplot 背景上的颜色混合不好。为了使它更好,我修改了 ggplot 中的`theme_dark()`,并为这个图创建了一个定制的。由于本教程不是关于 ggplot 主题的,所以我在这里不多讨论主题。这将是未来另一个教程的一部分。但是如果你有兴趣玩 ggplot 主题,我推荐一个不错的[教程](https://rpubs.com/mclaire19/ggplot2-custom-themes)。
在图的迭代中,我们将进行以下更改:
* 将默认主题更改为较暗的主题,
* 调整线条样式以匹配深色主题,
* 为了使百分点文本不那么混乱,我们也让它们只出现在最近三年。
```
student_debt %>%
mutate(race = fct_relevel(race, "Black", "White", "Hispanic")) %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race,
color = race)
) +
geom_line(aes(),
size = 1,
linetype = "dashed") +
geom_point(aes(
size = loan_debt_pct),
show.legend = FALSE) +
geom_text(aes(
label = ifelse(year >= 2010,
paste0(round(loan_debt_pct * 100), "%"),
"")
),
show.legend = FALSE,
size = 4,
hjust = 1, vjust = 0) +
theme_race() +
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
scale_color_manual(values = c("White" = "#ffffff", "Black" = "#787575", "Hispanic" = "#f5bf42")) +
labs(title="Average Family Student Loan",
color = NULL,
x = NULL, y = "Average Loan Debt",
subtitle ="Point sizes represent % of families with student loans")
```

# 让剧情动起来吧!
我认为我们已经处于足够好的状态,可以继续制作动画情节了。使用`gganimate`制作图表动画非常简单。我们将添加大约 2 行代码来实现它。
```
student_debt %>%
mutate(race = fct_relevel(race, "Black", "White", "Hispanic")) %>%
ggplot(
aes(x = year,
y=loan_debt,
group = race,
color = race)
) +
geom_line(aes(),
size = 1,
linetype = "dashed") +
geom_point(aes(
size = loan_debt_pct),
show.legend = FALSE) +
geom_text(aes(
label = ifelse(year >= 2010,
paste0(round(loan_debt_pct * 100), "%"),
"")
),
show.legend = FALSE,
size = 4,
hjust = 1, vjust = 0) +
theme_race() +
transition_reveal(year) +
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
scale_color_manual(values = c("White" = "#ffffff", "Black" = "#787575", "Hispanic" = "#f5bf42")) +
labs(title="Average Family Student Loan",
color = NULL,
x = NULL, y = "Average Loan Debt",
subtitle ="Point sizes represent % of families with student loans")
```

在我们的动画中,有两个方面需要改进,以使视觉效果对读者更友好:
* 一旦新的数据点被填充,点和文本就会消失。让他们留下来,
* 动画运行和回收太快。让我们慢一点,让它在完成一个循环后停顿更长时间,
* 让我们在图表顶部添加一个计数器,它将随着图表的移动显示当前年份,
* 作为最后的润色,让我们也为标签添加一些清晰度,并添加一个源信用。
```
student_debt %>%
mutate(race = fct_relevel(race, "Black", "White", "Hispanic")) %>%
ggplot(
aes(x = year,
y=loan_debt,
color = race)
) +
geom_line(aes(),
size = 1,
linetype = "dashed") +
geom_point(aes(
size = loan_debt_pct,
group = seq_along(year)),
show.legend = FALSE) +
geom_text(aes(
label = ifelse(year >= 2010,
paste0(round(loan_debt_pct * 100), "%"),
""),
group = seq_along(year)
),
show.legend = FALSE,
size = 4,
hjust = 1, vjust = 0) +
theme_race() +
transition_reveal(as.integer(year)) + # as.integer(year) makes the year showing in subtitle as integer.
scale_x_continuous(breaks = seq(1989, 2016, 3)) +
scale_y_continuous(breaks = seq(500, 15000, 1500),
labels = scales::dollar) +
scale_color_manual(values = c("White" = "#ffffff", "Black" = "#787575", "Hispanic" = "#f5bf42")) +
labs(title="Average Student Loan Taken by the US Families",
x = NULL, color = NULL,
y = "Average Loan Debt \n($ normalized to 2016 dollars)",
caption = "Source: Urban Institute, and the US Census,\n2017 | Arafath Hossain",
subtitle ="Point sizes represent % of families with student loans \nYear: {frame_along}") -> plot
```
# 最终视觉
所以,在所有这些更新之后,这里是我们的最后一个视频,它带我们踏上了美国种族不平等的一个永久化的方面的旅程。
```
plot %>%
animate(fps = 8, end_pause = 12, height = 6, width = 10, units = "in", res = 150)
```

情节动画可以是一个很好的工具来传达你的信息,当它呼吁一个特定的趋势随着时间的推移。我很高兴在这个地块上工作,希望你也能在你的项目中发现它的乐趣和用处!
# 感谢阅读!
**想了解更多关于如何高效绘制数据的信息吗?试试这个教程:**
</is-that-red-wine-good-enough-49326a4c08e4>
# 阿拉法特·侯赛因
* ***如果你喜欢这个,*** [***跟我上中等***](https://medium.com/@curious-joe) ***更多***
* ***让我们连线上***[***LinkedIn***](https://www.linkedin.com/in/arafath-hossain/)
# yolov 4–5D:yolov 4 的自动驾驶增强版
> 原文:<https://towardsdatascience.com/yolov4-5d-an-enhancement-of-yolov4-for-autonomous-driving-2827a566be4a?source=collection_archive---------18----------------------->
## 你只看一次,但有五个刻度
在过去的几年中,目标检测已经成为深度学习和模式识别研究中最热门的话题,并且已经成为所有计算机视觉研究人员必须知道的问题。如果你读这篇文章是因为你知道文章标题中有趣的是什么,我相信你有一些物体检测的背景,所以我想忽略解释基本的东西,如什么是物体检测和有多少种物体检测器,答案可以很容易地在数百万个来源中找到,通过在谷歌或任何搜索网站上键入非常简单的关键字。但至少,我可以从总结一系列 YOLO 算法开始,到目前为止,这些算法一直是对象检测的图标,也是最有吸引力的基线方法,其他方法都是基于它进行改进的。
2015 年,Joseph Redmon 和他的合著者推出了 YOLO 的第一个版本,该版本在实时物体检测方面取得了突破。 [YOLOv1](https://arxiv.org/abs/1506.02640) 是一个一阶段的物体检测器,与当时的两阶段方法相比,推理速度快,精度可接受。 [YOLOv2](https://arxiv.org/abs/1612.08242) ,也称为 YOLO9000,是一年后提出的,通过应用锚箱的概念来提高检测精度。2016 年, [YOLOv3](https://arxiv.org/abs/1804.02767) 进行了进一步的改进,采用了新的骨干网络 Darknet53,并使用特征金字塔网络(FPN)作为模型颈部,能够检测三种不同比例的对象。从下一个版本 [YOLOv4](https://arxiv.org/abs/2004.10934) 开始,Joseph 宣布由于一些个人原因停止进行这个项目,并将 YOLO 项目的主导特权交给了 Alexey Bochkovskiy,Alexey 在 2020 年引入了 YOLOv4。YOLOv4 通过使用新的主干 CSPDarknet53 (CSP 代表跨阶段部分),添加空间金字塔池(SPP),路径聚合网络(PAN),并引入镶嵌数据增强方法,提高了前任 YOLOv3 的性能。你可以通过[官网](https://pjreddie.com/darknet/)或者 github 回购[暗网](https://github.com/AlexeyAB/darknet)看一下 YOLO 项目。

YOLOV4 的网络架构(图见[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)
目前,YOLOv4 已经是 YOLO 系列中最先进的型号(实际上存在一个名为 YOLOv5 的版本,但由于一些原因,该版本没有被确认为官方版本,这可以在这篇[文章](https://blog.roboflow.com/yolov4-versus-yolov5/)中找到)。但是 YOLOv4 仍然没有针对所有场景进行优化;也就是说,在具有许多小物体的场景的情况下,YOLOv4 仍然在努力,并且不是真正准确的,例如,在自动驾驶场景中,在道路上存在许多小而远的物体,如行人、车辆、交通标志等。如题,这篇帖子介绍了[YOLOv4–5D](http://ieeexplore.ieee.org/document/9374990),yolov 4 针对自动驾驶场景的改进。
yolov 4–5D 的新功能:
* 主干:CSPDarknet53_dcn
* 脖子:PAN++的
* 头部:增加 2 个大规模图层
* 网络修剪

yolov 4–5D 的网络架构(图见[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)
# ***1。主干:cspdarknet 53 _ DCN***
CSPDarknet53 是 YOLOv4 的主干,yolov 4 是第一个将跨阶段部分(CSP)结构集成到主干或特征提取器中的模型。yolov 4–5D 中引入的修改主干通过用可变形卷积网络(DCN)替换几层中的常规卷积来重新设计,并表示为 CSPDarknet53_dcn。具体而言,为了平衡效率和效果,最后一级中的 3×3 卷积层被替换为 DCN。DCN 的显著特点是它使用一个可学习的偏移值来描述目标特征的方向,这样,感受野就不局限于一个固定的范围,而是可以灵活地适应目标几何形状的变化。此外,DCN 只是轻微地影响了模型中的参数数量。具有上述特征的 DCN 被整合到 yolov 4-5D 的骨架中,形成 CSPDarknet53_dcn。

可变形卷积(图见[文件](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)
# 2.脖子:PAN++的
与 YOLOv4 不同,yolov 4 使用 PAN 作为模型颈部的一部分(与 SPP 一起)。在 yolov 4–5D 中,使用 PAN++并将其设计为特征融合模块。PAN++被应用来平衡主干中低层特征的语义信息和高层特征的位置信息。整个网络设计为输出 5 种不同尺度的检测,有利于小目标检测。

PAN ++ in yolov 4–5D(图改编自[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)
# 3.头部:增加 2 个大规模图层
如上所述,再增加 2 个大尺度探测层的目的是增强探测小物体的能力。

yolov 4–5D 增加了两个大规模的更好的小物体检测(红框中)(图改编自[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)
# 4.网络修剪
批量标准化中的稀疏比例因子的概念被用于通道修剪,以修剪 yolov 4-5D 的主干。因为比例参数γ是一个可学习的因子,它能够表示通道的重要性。设置一个小的剪枝阈值,一般为 0.1。哪个通道的γ低于 0.1 将被修剪。
# 5.结果
下表显示了 YOLOv4–5D 与 yolov 4 的性能对比:

在 BDD 和 KITTY 数据集上,yolov 4–5D vs yolov 4([论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)中的表格)
YOLOv4–5D 将 yolov 4 的性能提高了一个显著的差距。在 BDD 数据集上,IoU 0.5 的整体 mAP 从 65.90%提高到 70.13%,提高了 4.23%。在 KITTY 数据集上,YOLOv4-5D 以 87.02%的 mAP 产生了更高的检测性能,与原始 yolov 4 的 85.34% mAP 相比,差距为 1.68%。
下表显示了 yolov 4–5D 与其他先进方法的进一步性能比较:

yolov 4–5D 与 BDD 验证数据的其他方法(在[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)中的表格)

yolov 4–5D 与其他方法对 KITTY 验证数据的比较(见[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)中的表格)
最后,通过模型剪枝,yolov 4–5D 的推理速度在保持准确性的同时显著提高了 31.3%。

修剪过的 yolov 4–5D 性能(在[论文](https://www.semanticscholar.org/paper/YOLOv4-5D%3A-An-Effective-and-Efficient-Object-for-Cai-Luan/0e582215eaf70ca498d1656dd6e372b3ea3e9966)中的表格)
# 结论
在这篇文章中,我介绍了 YOLOv4-5D,这是 yolov 4 的一个改进,用于自动驾驶场景中的物体检测。YOLOv4–5D 表现出比 yolov 4 更高的性能,在 BDD 数据集上 mAP 提高了 4.23%,在 KITTY 数据集上 mAP 提高了 1.68%。此外,yolov 4–5D 的修剪版本在保持相同精度的情况下,仅用 98.1MB 的内存进一步提高了 31.3%的推理速度。
欢迎读者访问我的脸书粉丝页,这是关于机器学习的分享:[投入机器学习](https://www.facebook.com/diveintomachinelearning)。我的另一篇关于用 Darknet 和 Tensorflow-Keras 执行 YOLOv4 物体检测的帖子也可以在[这里](/darkeras-execute-yolov3-yolov4-object-detection-on-keras-with-darknet-pre-trained-weights-5e8428b959e2)找到。
感谢您抽出时间!
# 通过支持 CUDA 的 OpenCV 提高 YOLOv4 性能
> 原文:<https://towardsdatascience.com/yolov4-with-cuda-powered-opencv-dnn-2fef48ea3984?source=collection_archive---------6----------------------->
## 用 CUDA 11.2 和 cuDNN8.1.0 构建 OpenCV,获得更快的 YOLOv4 DNN 推理 fps。

[阿卡什拉伊](https://unsplash.com/@raiakash?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/busy-roads?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片|作者检测
YOLO(You-Only-Look-Once 的缩写)无疑是在 COCO 数据集上训练的最好的物体检测器之一。YOLOv4 是最新的迭代,具有很大的准确性和性能的权衡,使自己成为最先进的物体探测器之一。在智能视频分析管道中采用任何对象检测器的典型机制包括使用 Tensorflow 或 PyTorch 等能够在 NVIDIA GPU 上运行的库来加速模型推断。OpenCV 用于图像/视频流输入、预处理和后处理视觉效果。如果我告诉你,OpenCV 现在能够利用 NVIDIA CUDA 的优点,通过 DNN 模块原生运行 YOLOv4,会怎么样?在这篇博客中,我将带你用 CUDA 和 cuDNN 构建 OpenCV,用 DNN 模块加速 YOLOv4 推理。
# 介绍
我认识的大多数发烧友都有支持 GPU 的设备。我的目标一直是让 GPU 加速成为主流。谁不喜欢开得更快呢?我用过 OpenCV 4.5.1,CUDA 11.2 和 cuDNN 8.1.0,让这个球滚动起来,让推论更容易!首先,您需要设置 CUDA,然后安装 cuDNN,最后构建 OpenCV。此外,博客分为几个部分,以便更容易跟踪!
# CUDA 11.2 和 cuDNN 8.1.0 安装
最有可能导致计算机无法启动的部分。开个玩笑!做好每一件事,这应该是轻而易举的。
## **安装 CUDA 11.2**
基于您的平台,从从 [CUDA 库](https://developer.nvidia.com/cuda-downloads)下载 deb 文件开始。

作者图片| CUDA 平台选择
一旦您选择了合适的平台,将会为您提供安装命令。如果你的平台和我的相似,你可以安装如下—
```
wget [https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin](https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin)sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600wget [https://developer.download.nvidia.com/compute/cuda/11.2.1/local_installers/cuda-repo-ubuntu2004-11-2-local_11.2.1-460.32.03-1_amd64.deb](https://developer.download.nvidia.com/compute/cuda/11.2.1/local_installers/cuda-repo-ubuntu2004-11-2-local_11.2.1-460.32.03-1_amd64.deb)sudo dpkg -i cuda-repo-ubuntu2004-11-2-local_11.2.1-460.32.03-1_amd64.debsudo apt-key add /var/cuda-repo-ubuntu2004-11-2-local/7fa2af80.pubsudo apt updatesudo apt -y install cudasudo reboot
```
如果做得正确,当您运行`nvidia-smi`时,您应该得到以下输出

图片作者| nvidia-smi
最后,在您的。巴沙尔或者。zshrc
```
# CUDA
export CUDA=11.2
export PATH=/usr/local/cuda-$CUDA/bin${PATH:+:${PATH}}
export CUDA_PATH=/usr/local/cuda-$CUDA
export CUDA_HOME=/usr/local/cuda-$CUDA
export LIBRARY_PATH=$CUDA_HOME/lib64:$LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/cuda-$CUDA/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH
export NVCC=/usr/local/cuda-$CUDA/bin/nvcc
export CFLAGS="-I$CUDA_HOME/include $CFLAGS"
```
不要忘记跟进`source ~/.bashrc`或`source ~/.zshrc`
## 安装 cuDNN 8.1.0
为此,你需要有一个 NVIDIA 的帐户,所以请确保你登录。一旦你这样做,头[在这里](https://developer.nvidia.com/rdp/cudnn-download),并下载标记的文件。

作者图片| cuDNN 精选
下载完 deb 文件后,运行以下命令—
```
sudo dpkg -i libcudnn8_8.1.0.77-1+cuda11.2_amd64.deb
sudo dpkg -i libcudnn8-dev_8.1.0.77-1+cuda11.2_amd64.deb
```
这标志着 NVIDIA CUDA 和 cuDNN 安装完成!
# 从源代码构建 OpenCV 4.5.1
有趣的是,这让我很兴奋!本节将帮助您使用 CUDA、GStreamer 和 FFMPEG 从源代码构建 OpenCV!要执行的命令有一长串,所以开始吧。
首先,安装 python 开发者包—
```
sudo apt install python3-dev python3-pip python3-testresources
```
接下来,让我们安装构建 OpenCV 所需的依赖项
```
sudo apt install build-essential cmake pkg-config unzip yasm git checkinstall
sudo apt install libjpeg-dev libpng-dev libtiff-dev
sudo apt install libavcodec-dev libavformat-dev libswscale-dev libavresample-dev
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
sudo apt install libxvidcore-dev x264 libx264-dev libfaac-dev libmp3lame-dev libtheora-dev
sudo apt install libfaac-dev libmp3lame-dev libvorbis-dev
sudo apt install libopencore-amrnb-dev libopencore-amrwb-dev
sudo apt-get install libgtk-3-dev
sudo apt-get install libtbb-dev
sudo apt-get install libatlas-base-dev gfortran
sudo apt-get install libprotobuf-dev protobuf-compiler
sudo apt-get install libgoogle-glog-dev libgflags-dev
sudo apt-get install libgphoto2-dev libeigen3-dev libhdf5-dev doxygen
```
Numpy 是这个版本的一个重要 python 包。使用 pip 安装它—
```
pip3 install numpy
```
现在,您应该已经为构建做好了一切准备。运行以下命令下载并提取源代码—
```
mkdir opencvbuild && cd opencvbuild
wget -O opencv.zip https://github.com/opencv/opencv/archive/4.5.1.zip
wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.5.1.zip
unzip opencv.zip
unzip opencv_contrib.zip
mv opencv-4.5.1 opencv
mv opencv_contrib-4.5.1 opencv_contrib
```
让我们准备食谱吧!
```
cd opencv
mkdir build && cd build
```
确保根据您的 GPU 更改`CUDA_ARCH_BIN`。
```
cmake \
-D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_C_COMPILER=/usr/bin/gcc-7 \
-D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_PYTHON_EXAMPLES=ON \
-D INSTALL_C_EXAMPLES=ON -D WITH_TBB=ON -D WITH_CUDA=ON -D WITH_CUDNN=ON \
-D OPENCV_DNN_CUDA=ON -D CUDA_ARCH_BIN=7.5 -D BUILD_opencv_cudacodec=OFF \
-D ENABLE_FAST_MATH=1 -D CUDA_FAST_MATH=1 -D WITH_CUBLAS=1 \
-D WITH_V4L=ON -D WITH_QT=OFF -D WITH_OPENGL=ON -D WITH_GSTREAMER=ON \
-D WITH_FFMPEG=ON -D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_PC_FILE_NAME=opencv4.pc -D OPENCV_ENABLE_NONFREE=ON \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
-D PYTHON_DEFAULT_EXECUTABLE=$(which python3) -D BUILD_EXAMPLES=ON ..
```
你应该会看到一个类似这样的成功的构建—

作者图片
确保 CUDA 被检测到并且构建路径是准确的。如果一切正常,继续执行下面的命令来启动构建—
```
make -j$(nproc)
sudo make install
```
要检查您是否成功构建了 OpenCV,请运行以下命令—
```
pkg-config --libs --cflags opencv4
```
成功安装后,它应该会给你这样的输出—

作者图片| OpenCV 成功构建
很高兴看到你能走到这一步!现在,您应该已经准备好运行示例应用程序了。
# 运行应用程序
继续克隆这个存储库并获取权重。从安装 git-lfs 开始
```
sudo apt install git git-lfs
```
用模型文件克隆存储库
```
# Using HTTPS
git clone https://github.com/aj-ames/YOLOv4-OpenCV-CUDA-DNN.git
# Using SSH
git clone git@github.com:aj-ames/YOLOv4-OpenCV-CUDA-DNN.gitcd YOLOv4-OpenCV-CUDA-DNN/git lfs install
git lfs pull
```
您可以在图像、视频摄像头或 RTSP 输入上运行该应用程序。
```
# Image
python3 dnn_infernece.py --image images/example.jpg --use_gpu# Video
python3 dnn_inference.py --stream video.mp4 --use_gpu
# RTSP
python3 dnn_inference.py --stream rtsp://192.168.1.1:554/stream --use_gpu
# Webcam
python3 dnn_inference.py --stream webcam --use_gpu
```
页(page 的缩写)S —移除`--use-gpu`标志以禁用 GPU。适得其反不是吗?
# 极客的一些基准!
如果收益不可观,我们就不会这样做。相信我,没错!在 GPU 上运行让我的 FPS 提高了 10–15 倍!
我在两种配置上进行了测试
1. 英特尔酷睿 i5 7300 HQ+NVIDIA GeForce GTX 1050 ti
2. 英特尔至强 E5–1650 v4+英伟达特斯拉 T4
我会让数字说话的!
```
| Device | FPS | Device | FPS |
| :------------: | :----------: | :------------: | :----------: |
| Core i5 7300HQ | 2.1 | GTX 1050 Ti | 20.1 |
| Xeon E5-1650 | 3.5 | Tesla T4 | 42.3 |
```
# 外卖
GPU 加速正在渗透到多个库和应用中,使用户能够以前所未有的速度运行更繁重的工作负载!计算机视觉曾经是一项并非所有人都能获得的技术,但随着神经网络的改进和硬件计算能力的提高,这一差距已经显著缩小。随着人工智能比以往任何时候都发展得更快,我们面临着大量的硬件灵活性!💪
# YOLOv5,自定义数据集上的端到端对象检测器项目
> 原文:<https://towardsdatascience.com/yolov5-end-to-end-object-detector-project-on-custom-dataset-5d9cc2c95921?source=collection_archive---------5----------------------->
## YOLOv5,请检测蘑菇!
这可能是你给一个在森林里行走的无人机的命令。那太棒了!我们将在这里使用的技术是如此之轻,我敢肯定这不是一个幻想。

《草坪上的蘑菇》图片来自[公开图片数据集 V6](https://storage.googleapis.com/openimages/web/visualizer/index.html?set=train&type=detection&c=%2Fm%2F052sf&id=c44d38c06d38c4e5) 作者:[詹姆斯·鲍威](https://www.flickr.com/people/jamesrbowe/) ( [许可](https://creativecommons.org/licenses/by/2.0/))
在我的[上一篇文章](/computer-vision-lets-classify-mushrooms-6b3abe1561eb)中,我通过 Tensorflow 库使用 CNN 对蘑菇进行了分类。我使用了 Kaggle 上可用的[真菌竞争数据集](https://www.kaggle.com/c/fungi-challenge-fgvc-2018)。该数据集的许多图像包含具有丰富背景的多个对象。我问自己,如果对单个蘑菇或一群蘑菇的裁剪图像进行训练,也许我的模型可以得到改进。这也将有助于我的不平衡数据集。接下来是最先进的图像检测技术。
如果你关注人工智能新闻,你肯定听说过 YOLO。如果不是,你可能会问自己这是不是“你只能活一次”的哲学把戏。所以请放心,它不是。这个巧妙的缩写是“你只看一次”。请留下来,我们将定制这个华丽的工具来检测蘑菇。
如果你对这些都不熟悉,你可以在这里找到一篇关于物体检测的惊人文章。
## 在我们开始之前,先讲一个关于 YOLOv5 出生争议的小故事
YOLO 最初是由约瑟夫·雷德蒙在 2016 年 5 月创作的。这是实时物体检测的一次革命。请注意,这是约瑟夫雷德蒙来了这个如此好的名字。该代码已更新与阿里法尔哈迪的 3 个最初版本的关联。在那里,先驱们通过 Twitter 祝福阿列克谢·博奇科夫斯基(Alexey Bochkovskiy)领导 YOLO 学会。约瑟夫·雷德蒙的推文声明这不再是他的了。Alexey Bochkovskiy 在 2020 年 4 月推出了 YOLOv4。
YOLOv4 之后很多天,YOLOv5 被 Glenn Jocher 发布。关于这个名字有很大的争议,因为这个基于 Pytorch 的新模型与最初的 YOLO 毫无关系。这不是 YOLO 的新版本。这在社会上引起了很大的争议。你可以从阿列克谢·博奇科夫斯基[这里](https://github.com/AlexeyAB/darknet/issues/5920)以及[这里](https://blog.roboflow.com/yolov4-versus-yolov5/)的回应中读到它。无论如何,这个名字很棒,它是一个很棒的工具。我不知道 Alexey Bochkovskiy 将如何命名他的代码的新版本,或者他是否会制作一个新版本。不如不叫 YOLOv5,我们叫它 YOLOP“你只看一次 Pytorch”?我喜欢 YOLOP!
反正说了这么多,还是看看怎么定制它来检测我的蘑菇吧。
这里的想法是在蘑菇的标记数据集上训练 YOLOv5,保存宝贵的权重,并在我的 Kaggle 数据集或任何蘑菇图像上使用它们进行检测。
首先,您可以在 python 环境中的 Jupyter 笔记本上或者直接在终端控制台窗口上尝试下面的命令。我两个都做了,在终端控制台窗口中做更简单,但是如果你想打包并重试,我发现将代码封装在 Jupyter 笔记本下的函数中更容易。如果你想在电脑上不安装任何东西的情况下尝试一下,你可以直接在谷歌 colab 笔记本上尝试。多酷啊!
## **下载带边界框的标注数据集**
***选择数据集***
我们需要的是一个数据集,它能给每张图片提供我们最喜欢的物体的位置:蘑菇。因此,该模型将能够在看不见的图像上预测/检测蘑菇的位置。
我尝试了多个开放数据集,发现谷歌开放图像数据集是最容易使用的。[网站](https://storage.googleapis.com/openimages/web/visualizer/index.html?set=train&type=detection&c=%2Fm%2F052sf)允许显示带有检测框的图像。对我来说很有吸引力也很幸运,他们有蘑菇课。

[打开图像数据集 V6 的首页](https://storage.googleapis.com/openimages/web/visualizer/index.html?set=train&type=detection&c=%2Fm%2F052sf)类蘑菇
***下载数据集***
我选择使用开源工具包 [OIDv4_ToolKit](https://github.com/EscVM/OIDv4_ToolKit) 。随意使用 OID V6 推荐的[五十一](http://fiftyone.ai/)开源工具或者直接手动下载。
在这里,我要求蘑菇类的 6000 个图像,但训练集上可用的最大图像数是 1782。

我的终端控制台的图像
现在,我们有了带有边框的图像。
看看目前为止我在本地下载了什么。

我的终端控制台的图像
这些图像现在可以在蘑菇文件夹中找到,其中包含一个标签文件夹。

我的蘑菇文件夹截图
再走两步,我们就完成了数据集。首先,我们需要把所有的标签和图片放在同一个根目录下。轻松点。其次,我们需要将标签/边界框文件转换成正确的格式。txt 文件中存储的注释是坐标(XMin,YMin,XMax,YMax)。请注意,我们需要在 0 和 1 之间缩放的坐标。为了做到这一步,我知道你可以使用一些标签应用程序。但是我喜欢自己做!
所以首先我们修改 OIDv4_ToolKit/classes.txt 文件,只放蘑菇类。之后,我试着从 AIGuyCode 中取出 [convert_annotations.py](https://github.com/theAIGuysCode/OIDv4_ToolKit/blob/master/convert_annotations.py) 。并没有马上见效。因此,我将代码 convert_annotations.py 放在笔记本中,并对我的文件路径进行了硬编码。不知道为什么不行。但是代码很好,而且是开源的。
看看这里的!
让我们看看结果:

蘑菇文件夹的图像:注释和图像在同一个根目录下
让我们检查注释中的差异
以前

在...之后

现在我们准备好了!我们的图像数据集具有正确格式的边界框。现在就去拿模型吧!
## 安装 YOLOv5
YOLOv5 可用[这里](https://github.com/ultralytics/yolov5)。让我们克隆它。
我们需要该软件包附带的所有要求。为此,您可以使用 [requirement.txt](https://github.com/ultralytics/yolov5/blob/develop/requirements.txt) 文件,或者使用 pip 命令,如果您想更紧密地控制您对环境所做的事情。
请查看我在 [github](https://github.com/AmelNozieres/Mushrooms_YOLOv5) 上的 YOLOv5 文件夹。
我们需要添加一个 Yaml 文件,以便 YOLOv5 能够找到这个类。这里我在数据文件夹里加了 [*蘑菇. yaml*](https://github.com/AmelNozieres/Mushrooms_YOLOv5/blob/main/data/Mushrooms.yaml) 。请注意,您应该在这里输入我们之前用 OIDv4_ToolKit 修改的蘑菇文件夹的路径。
## 训练 YOLOv5 型
在 YOLOv5 的下载包中,我们有该型号的 [4 个版本](https://github.com/AmelNozieres/Mushrooms_YOLOv5/tree/main/models):yolov 5s,YOLOv5m,YOLOv5l 和,YOLOv5x。我会用小的,它是 YOLOv5s。你猜对了,字母 s、m、l 和 x 代表型号尺寸。
在训练命令中,为选定的模型提供以下参数:
* **img:** 输入图像尺寸
* **批量:**批量大小
* **时期:**训练时期的数量
* **数据:**我们之前创建的 yaml 文件
* **cfg:** 这里选的型号我用的是小的
* **权重:**一个自定义的权重路径如果为空,它将被保存在 yolov 5/runs/train/yolov 5 _ results/weights 中
* **名称:**结果名称
* **设备:** 0 使用 GPU
* **缓存:**缓存图像以加快训练速度
这就是运行它所需要的一切

让我们看看结果:

results.png 存储在 yolov5/runs/train 文件夹中
所以在 yolov5n 文件夹里,你会得到你需要的所有报道。
我们来预测一下!
使用这个命令,您可以使用训练步骤中存储的权重来检测一个给出其路径的图像或所有测试图像。

很好,不是吗?
这也适用于视频和实时,所以在无人机的视频上编码应该很棒。
当我开始这个项目时,我不能在同一个地方找到所有的信息,所以这篇文章是我的经验的反馈,我希望是更完整的可能。如果你有任何问题,请在评论中告诉我。我当然找到了一些我忘记放在这里的其他技巧。希望这有助于您编写自己的检测器。如果你这样做,请让我知道你的项目。
编码快乐!
PS:我把我的代码放在 Github 上。
# NVIDIA Jetson Nano 上的 Yolov5 对象检测
> 原文:<https://towardsdatascience.com/yolov5-object-detection-on-nvidia-jetson-nano-148cfa21a024?source=collection_archive---------4----------------------->
## 使用 Jetson nano 开发套件进行 IMX477 CSI 摄像机配置和 Yolov5 物体检测的分步指南。

作者图片
本文介绍了在 NVIDIA Jetson Nano 上使用 Yolov5 和 openCV 进行 CSI 摄像机安装、软件和硬件设置以及对象检测的一个简单易行的过程。该项目使用 [**CSI-Camera**](https://github.com/JetsonHacksNano/CSI-Camera) 创建流水线并捕获帧,使用 [**Yolov5**](https://github.com/ultralytics/yolov5) 检测对象,在 Jetson 开发套件上实现了完整的可执行代码。查看视频说明的[**CodePlay jetson nano YouTube 播放列表**](https://www.youtube.com/watch?v=-A_CDLtQig4&list=PLZIi3Od9VUwW49q6T1VjShktoOgrDi3O4&index=3) 和[**jetson yolo Github**](https://github.com/amirhosseinh77/JetsonYolo)。
# 要求
杰特森纳米最常用的相机之一是树莓 Pi 相机模块 V2,但如果你需要更高的分辨率呢?最近,我试图在一个项目中使用 Waveshare IMX477 CSI 摄像机,但在将其连接到电路板时遇到了问题。最后,在尝试了几种不同的方法后,我想出了一个简单的流程,并决定与他人分享。本文由几个部分组成,包括硬件、驱动程序和 python 库安装,最后是 Yolov5。这些步骤对于使用 Jetson Nano 板上的摄像头进行物体检测都是必不可少的。
## 摄像机设置
将摄像机安装在载板上的 MIPI-CSI 摄像机连接器中。拉起摄像头端口的塑料边。推入摄像头色带,确保摄像头色带上的针脚朝向 Jetson Nano 模块。向下推塑料接头。您可以使用 [**Arducam 摄像机设置**](https://www.arducam.com/docs/camera-for-jetson-nano/native-jetson-cameras-imx219-imx477/imx477/) **指南**了解更多信息。

作者图片
## 相机驱动程序
默认情况下,NVIDIA JetPack 支持几种不同传感器的摄像头,其中最著名的是 Raspberry Pi camera v2。但是如果你使用的是另一种类型的相机,你需要安装一个传感器驱动程序。在这个项目中使用了一个带有 imx 477–160 传感器的 12.3 MP 摄像头,需要一个**附加驱动器**来连接。Arducam 为带有 IMX477 传感器的摄像机提供了一个易于绝缘的 [**IMX477 驱动器**](https://www.arducam.com/docs/camera-for-jetson-nano/native-jetson-cameras-imx219-imx477/imx477-how-to-install-the-driver/) 。(确保访问网站并使用最新的命令。)
下载自动安装脚本:
```
**cd ~****wget** [**https://github.com/ArduCAM/MIPI_Camera/releases/download/v0.0.3/install_full.sh**](https://github.com/ArduCAM/MIPI_Camera/releases/download/v0.0.3/install_full.sh)
```
安装驱动程序:
```
**chmod +x install_full.sh****./install_full.sh -m imx477**
```
最后,输入 y 重新启动板。
使用以下命令检查摄像机是否被正确识别。
```
**ls /dev/video0**
```
您可以使用[**JetsonHacks**](https://github.com/JetsonHacksNano/CSI-Camera/blob/master/simple_camera.py)**python 代码通过 **OpenCV** 从相机中捕捉帧。**
****
**作者图片**
## **火炬&火炬视觉**
**Yolov5 模型是在 Pytorch 框架中实现的。PyTorch 是一个基于 Torch 库的开源机器学习库,用于计算机视觉和自然语言处理应用。这里有一个在 Jetson 开发套件上安装 PyTorch 的完整指南。**
# **推理**
****在 Jetson nano 上克隆** JetsonYolo 存储库。**
```
**git clone** [**https://github.com/amirhosseinh77/JetsonYolo.git**](https://github.com/amirhosseinh77/JetsonYolo.git)
```
****
**作者图片**
## **下载 Yolov5 型号**
**根据模型尺寸、所需速度和精度选择所需的模型。您可以在**资产**部分的 中找到可用的型号 [**。使用下面的命令下载模型,并将其移动到 **weights** 文件夹。**](https://github.com/ultralytics/yolov5/releases)**
```
**cd weights****wget** [**https://github.com/ultralytics/yolov5/releases/download/v5.0/yolov5s.pt**](https://github.com/ultralytics/yolov5/releases/download/v5.0/yolov5s.pt)
```
**运行`**JetsonYolo.py**`用摄像机检测物体。**
```
**python3 JetsonYolo.py**
```
****
**作者图片**
# **结论**
**本文重点介绍如何使用 IMX477 摄像头捕捉帧并执行对象检测。设置这种类型的摄像机需要额外的驱动程序安装步骤,Arducam 已经为 Jetson Linux Driver (L4T)提供了一个驱动程序。在安装了必要的驱动程序和 Python 库之后,Yolov5 在 Jetson Nano 上实现为 JetsonYolo,并以每秒 12 帧的速度获得了令人满意的结果。**
> *****引文*****
```
@inproceedings{omidi2021embedded,
title={An Embedded Deep Learning-based Package for Traffic Law Enforcement},
author={Omidi, Abbas and Heydarian, Amirhossein and Mohammadshahi, Aida and Beirami, Behnam Asghari and Haddadi, Farzan},
booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},
pages={262--271},
year={2021}
}
```
# 您(也)需要在您的组织中进行数学优化…现在!
> 原文:<https://towardsdatascience.com/you-also-need-mathematical-optimization-in-your-organization-now-4be1b4e3090?source=collection_archive---------31----------------------->
## [练习笔记](http://towardsdatascience.com/tagged/notes-from-practice)
## 机器学习可能不足以充分利用数据科学

由[马尔辛·乔兹维亚克](https://unsplash.com/@marcinjozwiak?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/warehouse-truck?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
我将告诉你亚当*的故事。亚当是一名卡车调度员,在配送仓库工作。他每天的工作就是给卡车分派几百个日常订单,这样就能按时送到客户手中。他已经在这方面工作了 10 年,很难找到替代他的人(即使他生病了),因为他非常了解客户、订单和货运公司。
Adam 需要一台辅助显示器,因为他需要持续处理订单数据和卡车数据,同时在地图上检查距离和行驶时长。它花费了公司€200 英镑,但却帮助亚当更高效地工作,减少了他在电脑上切换窗口所需的时间。
在许多组织中,要获得第二台显示器,您需要填写表格,解释您为什么需要购买。然后,它必须得到您的直线经理、他/她的经理和采购/IT 人员或两者的批准。相当多的文书工作,对吧?
回到 Adam 的工作……他每天调度 20 辆卡车,根据货物类型、停靠站数量和总公里数,在欧洲国家的国内送货,从€到€的费用可能约为 300 到 500 英镑。所以他每天花费大约 8000€,而不需要问任何人。它让€每年赚 200 万英镑!
在许多组织中,辅助显示器只需要几个决定:*是或否*以及可能*在不同尺寸或型号之间进行选择。*
另一方面,在他的工作中,他有数万亿种不同的选择来分配订单给卡车——而且他通常必须在几个小时内完成!即使他的决策和最优分配之间有 5%的成本差异,也会让€公司每年损失 10 万英镑——比亚当的年薪还要多!
> 亚当在数万亿个选项中做出的决定可能会在无人察觉的情况下导致€每年增加 10 万美元的成本,而€200 显示器的决定需要许多双眼睛。
这就是数学优化/运筹学(OR)发挥作用的地方。我们可以给亚当提供一个工具,使用*数学优化来构建*高效的旅程*。*这样的工具可以轻松帮助他在更短的时间内构建更具成本效益的旅行,这样他就可以专注于其他关键任务,如寻找更便宜的运输公司或故障排除:这将成倍增加总体节省*。*我不会在此详述,但人们可以研究一下像*车辆路线问题*这样的主题,以了解这是如何实现的。
回到标题…看看你的组织和你资产负债表上最大的支出项目。它可以是运输,可以是与员工相关的成本,可以是能源或任何适合您的业务(模型)的东西。
> 你能识别出你组织中的"亚当"们吗?他们每天都在管理你的成本,并独自做出非常复杂的决策。
如今,组织中的大多数数据科学工作都由机器学习类型的项目所主导,这是有充分理由的:通过更好地理解今天或未来使用 ML,可以发现这种影响。另一方面,有些人*在查看你的数据或你的 ML 结果*并*采取行动*可能会让你的组织**损失几百万**。他们可能被称为计划狂、员工调度员、网络设计专家、价格分析师或其他什么人,这取决于你的组织是做什么的。
**识别这些人并了解他们是如何做出决定的,这是通过数学优化/运筹学实现节约的第一步。**
如何实现这些节约?在我的下一篇文章中…在那之前,保持安全!
*亚当是一个虚构的人物,他的故事受到了我从一位经验丰富的经理那里听到的故事的启发。他讲述的原始故事如下——和亚当的故事一样令人印象深刻:
> 一个普通的白领员工需要 50 美元费用的多个签名,但可以召集 20 人开会 1 小时,花 1000 美元而不要求任何人。
我是一名运筹学从业者/领导者,为德国邮政 DHL 集团工作。另一方面,这些观点并不代表我的现任或前任雇主的观点。如果你想取得联系,请随时联系[*LinkedIn*](https://www.linkedin.com/in/bariscemsal/)
# 你不是我的 SQL 类型
> 原文:<https://towardsdatascience.com/you-are-not-my-data-type-3ba3d3dec258?source=collection_archive---------35----------------------->

法比奥·桑塔尼耶洛·布鲁恩在 [Unsplash](https://unsplash.com/s/photos/type?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
## SQL 数据类型快速介绍
SQL 有许多风格——Oracle、SQL Server、MySQL、BigQuery、PostgreSQL——就像每种风格都有不同版本的 SQL。这不是关于什么是不同的,这是关于 SQL 数据类型和如果你需要动态地改变类型该怎么做。
**让我们开始吧!**
就像我上面说的,SQL 有很多种风格,其中每一种都有一长串非常相似的东西,但它们之间的不同之处也有一长串。
在我的职业和自由职业生涯中,我个人使用过 SQL Server、PostgreSQL、MySQL 和 Google BigQuery,每次我觉得有些东西应该工作,但却不工作,我不得不咨询万能的 Google。
这篇文章并不是关于各种口味之间的区别。在本文中,我想强调所有 SQL 风格中存在的数据类型。
标准 SQL 语言确实有一个管理机构,它被命名为 SQL ANSI SQL 的标准方言。你可以仔细阅读文档,但是它说的是需要一种所有平台都必须遵守的标准方言。这就是为什么这篇文章将重点放在这样的。
这里有一个很棒的资源,它向您展示了每个版本中的内容,以及一个可以与您正在使用的平台一起使用的变体。这里可以找到那个[。](https://nils85.github.io/sql-compat-table/datatype.html)
有 4 个类别,每个类别都有自己的有用数据类型。这 4 个类别是:
1. 用线串
2. 数字的
3. 日期和时间
4. 布尔代数学体系的
## 字符串数据类型
所有编程语言中最常见的数据类型之一就是字符数据类型。它也可能是你以“Hello World!”的形式编写的第一个程序的一部分
CHAR()和 VARCHAR()是 ANSI SQL 中的两种字符类型。CHAR()和 VARCHAR()能够容纳 8,000 的相同大小的字符串长度。这两者的主要区别在于表创建后会发生什么。
当您创建一个定义了 CHAR(4)列的表时,该列的字符串长度是固定的,只有 4 个字符(例如。ABCD’),无论如何都占用相同的存储空间。当您定义了一个带有 VARCHAR(4)列的表时,那么它也可以容纳 4 个字符,但是如果字符更少,它将占用可变的大小。实际上,CHAR()的大小是固定的,VARCHAR()的大小可以根据指定的大小而变化。
## 数字数据类型
有两种数值数据类型,INT()和 DECIMAL()。显然,两者的主要区别在于 INT()没有小数,而 DECIMAL()有小数。
对于 INT()你可以存储一个从-2 到 2 -1 的数字。这是一个相当大的数字!DECIMAL() numbers 最多可以存储 38 个数字,包括小数点的左右两边。
当用 INT 创建一个列时,不需要指定任何东西,它可以自动保存最大值。然而,在创建小数列时,您必须指定两件事。第一个是小数点左边多少位,第二个是小数点右边多少位,总共 38 位。
## 日期和时间数据类型
我想从这里显而易见的数据类型开始,DATE()和 TIME()。将列指定为日期时,将获得 YYYY-MM-DD 格式的日期,将列指定为 TIME()时,将获得 hh:mm:ss 格式的时间。
这一类中还有一些其他有用的函数,可以返回计算机的当前日期和时间。NOW()、TODAY()和 GETDATE()就是其中的几个。
## 布尔数据类型
这是一种包含 TRUE 或 FALSE 的唯一数据类型。你可能会问为什么会在这里,因为 SQL 没有这种数据类型。在大多数情况下,你是对的。然而,有一种语言拥有它,那就是 PostgreSQL。
在 PostgreSQL 中,通过简单地编写 boolean 将列定义为 BOOLEAN,并返回“t”或“f”
所有其他类型的 SQL 本质上都没有布尔值,但是它们有变通方法。中的一个变通方法是将列定义为数字(1),这意味着它只能接受 1 或 0,其中 1 为真,0 为假。还有一个名为 BIT()的工具可以做同样的事情。
## 转换数据类型
一种更有用的技术是能够动态改变数据类型。您可以通过使用 CAST()或 CONVERT 函数()来实现这一点,但它们做的是同样的事情。
CAST 函数的调用方式如下`CAST('2020-06-11' AS DATE)`。也可以使用 CONVERT(),`CONVERT(DATE, '2020-06-11')`。
虽然这只是对 SQL 中的数据类型的一个尝试,但我鼓励您在 google 上搜索 SQL 中的数据类型,以便真正感受一下那里有什么。请记住,我尽了最大努力来包含所有 SQL 风格中存在的数据类型,但是如果有一种数据类型在所有风格中都不标准,那么快速的 google 搜索会有所帮助。
如果你错过了这个伟大的资源,我会在这里再次链接。这简要概述了数据类型和每种 SQL 风格中可用内容,以及如果某些内容不适用于您正在使用的内容,应该用什么来代替。
[SQL 兼容表(nils85.github.io)](https://nils85.github.io/sql-compat-table/datatype.html)
我希望您了解了一点数据类型,感谢您的阅读。
# 你可能没有充分利用熊猫的“read_csv”功能
> 原文:<https://towardsdatascience.com/you-are-probably-not-making-the-most-of-pandas-read-csv-function-51bcf069e646?source=collection_archive---------9----------------------->
## 它不仅仅是读取文件

在 [Unsplash](https://unsplash.com/s/photos/perfect?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上 [Aljoscha Laschgari](https://unsplash.com/@alaschgari?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
Pandas 可以说是最流行的数据分析和操作库。我认为熊猫被广泛使用的原因是它具有大量强大而多样的功能。
Pandas 函数通常在默认设置下运行良好。但是,如果您有效地使用这些参数,它们可以提供更多的功能。在本文中,我们将详细介绍 read_csv 函数,以充分利用它。
read_csv 是最常用的 Pandas 函数之一。它通过从 csv 文件中读取数据来创建数据帧。然而,它几乎总是以默认设置执行。
如果您通读过文档,您会注意到 read_csv 函数有许多参数。这些参数增加了功能的功能性和灵活性。
例如,如果 csv 文件包含一列日期,它将存储在数据类型为 object 的 dataframe 中。但是,为了在 dt 访问器下使用 Pandas datetime 函数,我们需要 datetime 数据类型的日期。我们总是可以在读取数据后转换数据类型。更实际的方法是在读取数据的同时处理这项任务。
parse_dates 参数完成了这项任务。我们来做一个例子。我有一个包含 3 列的示例 csv 文件。
```
import numpy as np
import pandas as pddf = pd.read_csv("/content/sample1.csv")df.dtypes
name object
height int64
date object
```
如您所见,日期列的数据类型是 object。
```
df = pd.read_csv("/content/sample1.csv", parse_dates=['date'])df.dtypes
name object
height int64
date datetime64[ns]
```

(图片由作者提供)
数据类型在熊猫中很重要。大量函数是特定于特定数据类型的。因此,每一列都有合适的数据类型是高效和准确的数据分析和操作的一个要求。
我们可以使用 read_csv 函数的 dtype 参数将数据类型分配给任何列。尽管 Pandas 能够推断出合适的数据类型,但在某些情况下我们可能需要调整它们。
例如,字符串与对象数据类型一起存储。对于某些操作,最好使用字符串数据类型,而不是对象。
```
df = pd.read_csv(
"/content/sample1.csv",
parse_dates=['date'],
dtype={'name':'string'}
)df.dtypes
name string
height int64
date datetime64[ns]
```
现实生活中的数据是杂乱的,通常需要大量的数据清理。丢失的值可能不会以标准格式表示,如 None 或 NaN。例如,“9999”或“?”可用于表示缺失值。
如果我们知道用作缺失值表示的值,我们可以在读取 csv 文件时处理它们。这些值被传递给 na_values 参数。
我们来做一个例子来演示这个案例。
```
df = pd.read_csv(
"/content/sample2.csv"
)df
```

(图片由作者提供)
```
df = pd.read_csv(
"/content/sample2.csv",
na_value = [9999, "?"]
)df
```

(图片由作者提供)
我们通常在数据分析中使用布尔值。它们指示条件或值是否为真。布尔值有真和假,但原始值可能以不同的方式表示。
例如,是和否、正确和不正确、1 和 0、成功或失败是用来代替真和假的一些值。true_values 和 false_values 参数在读取 csv 文件时将它们转换为实际的 true 和 false 布尔值。
这里有一个简单的例子。

(图片由作者提供)
```
df = pd.read_csv(
"/content/sample1.csv",
true_values = ['Yes'],
false_values= ['No']
)df
```

(图片由作者提供)
考虑这样一种情况,我们有一个非常大的 csv 文件,只需要其中的一部分进行分析。Pandas 提供了一些选项来处理这样的任务。
我们有以下 10000 行的 csv 文件。
```
df = pd.read_csv(
"/content/sample1.csv",
)print(df.shape)
(10000,3)
```
第一个选项是使用 nrows 参数从头开始读取一定数量的行。
```
df = pd.read_csv(
"/content/sample1.csv",
nrows=500
)print(df.shape)
(500,3)
```
数据帧包含 csv 文件的前 500 行。
第二个选项是使用 skiprows 参数从开头跳过一定数量的行。
```
df = pd.read_csv(
"/content/sample1.csv",
skiprows=9500
)print(df.shape)
(500,3)
```
现在数据帧包含了 csv 文件的最后 500 行,因为我们跳过了前 9500 行。
skiprows 参数也接受 lambda 表达式。例如,下面的代码将从 csv 文件中每隔一行读取一次。
```
df = pd.read_csv(
"/content/sample1.csv",
skiprows = lambda x: x % 2 == 1
)print(df.shape)
(5000,3)
```
read_csv 函数还允许读取一些列。所需的列被传递给 usecols 参数。我们可以使用要读取的列的标签或索引。
```
df = pd.read_csv(
"/content/sample1.csv"
)print(df.shape)
(100,50)
```
我们有一个 50 列的 csv 文件。假设我们想阅读前 10 个专栏。
```
df = pd.read_csv(
"/content/sample1.csv",
usecols = np.arange(10))print(df.shape)
(100,10)
```
Pandas 默认为数据帧分配整数索引,但是我们可以改变它。index_col 参数用于指定要用作索引的列。
考虑下面的数据帧。

(图片由作者提供)
如果我们想使用日期作为索引,我们可以如下读取 csv 文件。
```
df = pd.read_csv(
"/content/sample1.csv",
index_col = 'date'
)
```

(图片由作者提供)
## 结论
为了最大限度地使用一个函数,我们需要全面了解它的参数。对于 read_csv 函数,默认设置在大多数情况下都很好。然而,为了充分利用它,我们可能必须将一些参数付诸行动。
我们使用 read_csv 函数的参数实现的功能也可以在读取整个 csv 文件后完成。但是,这不是最佳选择。此外,我们可以通过在读取 csv 文件时处理这些操作来节省内存。
感谢您的阅读。如果您有任何反馈,请告诉我。
# 你没有充分利用 SHAP 值——特征组和相关性
> 原文:<https://towardsdatascience.com/you-are-underutilizing-shap-values-feature-groups-and-correlations-8df1b136e2c2?source=collection_archive---------6----------------------->
## 你的模型是你数据的一个镜头,塑造了它的望远镜
我和 SHAP 价值观合作已经有一段时间了,有机会测试了很多我从未在网上见过的应用程序,但它们运行得非常好,所以我想和更多的人分享。
因为这篇文章是更高级的东西,所以我不会对什么是 SHAP 价值观做任何更深入的介绍,除了重申它们给出了 l *ocal* 的解释,与*的具体数据点相关。*这就是他们力量的来源。它们也是对非结构化特征重要性的最正式的严格方法。
在本案例中,我将通过对要素进行分组和查看其 shap 值的相关性来展示两种有用的技术,以更好地理解您的数据。这里所有的代码都是。也看看[这篇文章](/you-are-underutilizing-shap-values-understanding-populations-and-events-7f4a45202d5)的更高级的分析。
# 数据集
我使用了两个不同的 Kaggle 数据集进行分析,试图揭示这些技术的潜力,但这只是一个玩具描述。只有当你想到在行业中实践机器学习时获得的数百个变量的庞大数据集时,你才能理解它们对你的帮助有多大。
[**澳大利亚会下雨吗**](https://www.kaggle.com/jsphyg/weather-dataset-rattle-package) **?** 关于前一天的 21 个变量
风、湿度、温度等。
[**车祸致命吗?**](https://www.kaggle.com/silicon99/dft-accident-data)59 关于撞车的变量
车辆、位置和事故信息

我选择了两个我以前从未处理过的数据集,我想看看这种基于模型的分析是否有什么有趣的见解。无论如何,我会和你分享我在剧情中所看到的。
*注意:为了获得合理的 shap 值,模型不应该过度拟合,否则 shap 值可能对测试集目标统计没有意义。*
# 功能组的形状
我们通常有自然的方法来分组我们的特征。例如,我们有不同的数据源,或者不同类型的信息。对于时间窗数据,我们可以使用不同窗口大小的要素,等等。在我们的案例中,可以单独查看每个特征,但是现在当你在表格模型上工作时,在早期建模阶段有数百个甚至数千个变量是很常见的。
有时,人们只是使用现成的汇总图将变量作为一个整体来看,然后对组内变量的原始值进行求和,甚至对整个数据集的绝对值求和,得出一个“该组要素的平均重要性”的数字。首先,*对原始值求和是很重要的,*因为你可以让相关变量相互对抗,让整组变量产生零影响,即使每个变量似乎都有一些影响。
然后,您可以使用与要素本身相同的逐个样本细节来查看要素组的形状(您会丢失作为颜色的变量值,因为您当然没有单个值,但是如果您感兴趣的话,您总是可以输入一些变量来显示大小)。

这些小组从不同的角度研究这些数据。很明显,下午 3 点比上午 9 点更重要(这有道理吧?它是后来的,所以它更接近我们想要预测的那一天)。同样,显而易见,与雨或湿度直接相关的变量比与风、压力,尤其是温度相关的变量更重要。
当我们处理更多的变量时,事情变得更加有趣。在英国事故数据集中,似乎真的有比我们一眼就能看到的汇总图更多的东西,我们可以通过对要素进行分组来开始质疑我们的数据。

与事故本身相关的特征对我们的问题(“事故是致命的吗”)最有影响,这很有意义。但是看起来事故组有很多不同的东西,如果能有一个更详细的视图就好了。我将事故变量分为三组:前的**、**期间的**、**后的**。例如*车辆操纵*在**之前**,*第一撞击点*在**期间**,而*Did _ 警察 _ 官员 _ 出席 _ 事故现场*在**之后。****

很有意义的是,**事故 _after** 变量有如此多的信息,因为如果事故真的很严重,警察可能只会去现场。但在事故发生之前,有一些信息是可用的,例如驾驶员的哪种操纵或者车辆是否正在离开车道,这是潜在的预测材料,可以帮助防止未来的事故(“不要在这里操纵”,“离开车道时减速”)。这必须谨慎进行,因为它不是一个因果模型,但我不想进入数据本身太深,因为我的目的只是给不同的 shap 分析一些想法。你可以这样做是为了获得具体的见解,或者像我在这里所做的那样,只是作为对数据集的基于模型的探索。
# 形状相关性
如果您正在研究高级 shap 分析,您可能已经了解了很多关于相关性的知识,并且知道当特征具有完全不同的分布时分析相关性是多么的不领情,或者更糟糕的是比较分类和数值特征。shap 相关分析具有不受这种差异干扰的非常有用的特性。无论特征是分类的、有序的还是连续的,它的形状值都是连续的。您可以从 shap 相关图中获得关于要素本身分布的一些见解,但您也可以了解它们的影响在模型中是如何相关的。您可以使用这种检查来驱动进一步的分析,例如具有意外依赖关系的特定变量对之间的依赖关系图。这里我画的是斯皮尔曼相关的绝对值。您可能对查看相关性本身感兴趣,但有时您只想知道变量是相关的,然后绝对相关性会提供更好的整体可视化。

这种分析还可以帮助您选择特性,因为您可以更好地理解变量之间的关系,这些关系可以是协同的或冗余的。如果变量是多余的,它们可能会分割它们之间的重要性,删除其中一个会增加另一个的重要性。但是我稍后会专门发一个关于特性选择的帖子,所以让我们举最后一个例子来激发你的大脑潜能和可能性。

车祸的变数太多,很难在这里集中注意力。反正可以看一些明显的关联,比如*路口细节*、*特殊条件*和*路口位置。*我还不确定在这种情况下,对于大型特征集,最好的可视化方式是什么,但是我已经准备了另外三种有趣的可视化方式,仅仅是为了给我们的数据提供不同的视角。
有些比较明显,比如*年龄带*和*年龄,*有些有道理,比如*打滑翻车*和*限速(左)。*但是*车辆 _ 驶离 _ 车道*和*警察 _ 警官 _ 出勤*的关系呢(右)?与*公共汽车或蔻驰乘客*相关的两个相关性很有趣,其中*驾驶员的年龄范围*(中间)*、*以及*车辆操纵*(左)。想一想为什么这种相关性会存在!公共汽车没有太多的机动动作,其司机也没有总司机那么多样化。如果我们感兴趣的话,我们可以深入了解我们的模型到底发生了什么。

某些选定变量的绝对相关聚类图(标题中的选择)。
# 结束语
所有的代码都在[这本卡格尔笔记本](https://www.kaggle.com/estevaouyra/shap-advanced-uses-grouping-and-correlation)里,在那里我得到了数据集并非常直接地应用了分析。在这篇文章之后,我还写了另一篇更高级的分析,如果你喜欢这篇文章,也可以看看那里的。
我希望我能够提供一些见解,也许可以帮助你进行一些你不知道如何做的日常分析,或者这些想法可以帮助你探索和理解你的模型或数据集-记住 shap 可以同时帮助我们。
# 你没有充分利用 SHAP 价值观:理解人口和事件
> 原文:<https://towardsdatascience.com/you-are-underutilizing-shap-values-understanding-populations-and-events-7f4a45202d5?source=collection_archive---------13----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 你的模型是你数据的透镜,SHAP 是它的望远镜
我和 SHAP 价值观合作已经有一段时间了,有机会测试了很多我从未在网上见过的应用程序,但它们运行得非常好,所以我想和更多的人分享。这篇文章是[关于特性组和相关性的另一篇文章的延续](/you-are-underutilizing-shap-values-feature-groups-and-correlations-8df1b136e2c2)。
我选择了一个更简单的帖子,只是为了看看人们是否觉得它有用,因为我在那里有很好的回应,所以我想分享一个更有趣的分析,我认为这需要我付出更多的努力,他们在这里!我确信如果你有很多变量的表格数据,它们会很有用。
再说一遍,既然这篇文章是为了更高级的东西,我就不深入介绍什么是 SHAP 价值观了,除了重申它们给出了 l *ocal* 的解释,与*的具体数据点相关。这就是他们力量的来源。它们也是对非结构化特征重要性的最正式的严格方法。*
所有代码都在[这个 kaggle 笔记本](https://www.kaggle.com/estevaouyra/shap-advanced-uses-subpopulations)里。
## 数据集
我使用了两个不同的 kaggle 数据集进行这些分析,试图揭示这些技术的潜力,但这只是一个玩具描述。只有当你想到在行业中实践机器学习时获得的数百个变量的庞大数据集时,你才能理解它们对你的帮助有多大。我在[的另一篇文章](/you-are-underutilizing-shap-values-feature-groups-and-correlations-8df1b136e2c2)中给出了更多关于它们的信息,所以在这里我只是重申一下基本情况。
[**澳大利亚会下雨吗**](https://www.kaggle.com/jsphyg/weather-dataset-rattle-package) **?** 关于前一天的 21 个变量
风、湿度、温度等。
[**车祸致命吗?**](https://www.kaggle.com/silicon99/dft-accident-data)59 关于撞车的变量
车辆、位置和事故信息
# 子群体形状
这是最让我困惑的一个应用程序,它的使用率很低。它提供了在许多应用中非常有用的信息,但人们通常不会直接计算它们,而是查看全局汇总图并推测不同组中的重要性会有什么不同。
通常根据模型的预测来划分人群,以便为每个人群建立特定的策略,例如在将患者送去做更多检查或送回家的医疗应用中,或者在需要收入证明的信用评分应用中。
在我们的玩具示例中,我们可以通过模型对第二天下雨的预测来划分我们的日子。在这种情况下,变量的重要性顺序不会改变很多,但是根据应用,每个预测波段的轮廓可能非常不同。

澳大利亚降雨的预测四分位数
它在英国事故数据集中也没有太大变化。除了总的来说最重要的变量始终是受害者的类型(行人、骑自行车者等),我们可以看到年龄在高风险路段(4 和 5)更明显,而第一撞击点仅在低风险路段(1 和 2)明显。看到这一点,我的直觉是,如果人更“脆弱”,例如行人、老人,撞击点并不重要,在这种情况下,事故更有可能是致命的。但这只是我的第一个想法,如果这种洞察力很重要,我们可以进一步分析数据本身。

事故死亡率预测五分位数
我们可以把不同的年份分开,看看这个模型是否在一段时间内总体稳定。我们可以看到,在 2013 年,阳光的重要性已经超过了风速。如果变量的分布在其他年份有所不同,这将在这张图表中显而易见。

我们也可以问问自己,热天与冷天在影响降雨概率的变量分布方面有多大差异。只需查询数据集并获得相应的行,我们就可以比较每个拆分。虽然寒冷的日子更受风和降雨本身的影响,但炎热的日子日照是第二个最重要的变量。

例如,在事故数据集中,我们可以查看不同类型的伤亡事故,试图了解每种事故的特征影响有何不同。例如,我们可以看到,第一撞击点对于公共汽车乘客来说是不同的重要,而速度限制对于摩托车事故来说是不同的重要。

根据我们想知道的内容,可以对上面简单显示的概要图进行许多改进。假设我们想知道的恰恰是“在摩托车与行人的碰撞中,哪些特征具有不同的碰撞轮廓?”。我们可以精确地问我们的 shap 值。

为了说明我的意思,我在这里展示了每组的平均 shap 值之间的差异。有趣的是,最大的差异不一定在最重要的特性上。此外,根据应用的不同,可能有其他更好的差异指标(例如,仅使用中位数或其他分位数而不是均值,或者 KL 散度),但我不想深入研究某个特定的应用,只是为了直观地了解您如何思考自己对模型影响数据的分析。
# 选择子群体的形状
现在回到基础。我们刚刚看到了如何对不同的子群体应用 shap 分析来分别理解它们或对它们进行比较,现在我们将注意到如何使用 shap 本身来选择子群体。

在我这里的所有数据中,没有什么太有趣的东西,但我希望你能明白。因为数据集中的每一行都有一个关联的 shap 值,所以我们可以根据特定的标准使用这些值进行过滤。上面,我选择了湿度不是影响变量的所有例子,我们可以看到其他特征的形状。相反,我们可以查看特定特性的值,我们可以查看该人群的其他指标或我们感兴趣的任何内容。

例如,假设你在一家社交媒体公司工作,你正在测试一个包含信用信息或教育信息或类似信息的新数据源,你将使用这些信息来改进平台内部的一些算法。很可能,来自新数据源的特性会对您的客户产生非常不同的影响,您可能想知道为什么。我们可以假设,这些信息将对互联网使用率低的客户非常有影响,因为你自己的内部社交媒体数据对于高使用率的客户来说已经非常有用了,这就为低使用率的客户留下了盲点,而这些低使用率的客户可能会受到这些非互联网数据的启发。
嗯,很容易看出是否是这样的情况:只需为你的新功能选择具有较大 shap 值的客户,并查看他们的互联网使用概况。您可以通过多种不同的方式对其进行分析,以尝试并了解受您的新功能高度影响的人群。例如,如果您想决定是否为这个数据源的更新付费,这可能是必不可少的。只是一个细节:如果你有很多新特性,你可能想把你的 shap 值分组,就像我在[这篇文章](https://medium.com/r?url=https%3A%2F%2Ftowardsdatascience.com%2Fyou-are-underutilizing-shap-values-feature-groups-and-correlations-8df1b136e2c2)中解释的那样。
所有这些都是为了说明你可以直接查询你的 shap 值,而且这种查询可以让一些查询变得容易很多。
# 我的数据上发生了什么—了解事件
你应该清楚的一点是,有了好的问题,你可以从 shap values 中获得很好的洞察力。我所说的理解事件是比较人群的更一般的版本,根据你所做的选择(你的“测试和控制组”,如果你可以的话),你可以得到有价值的洞察力。

我不确定在这两个 kaggle 数据集中是否会有什么有趣的东西可以分析,但是当我画出每个月的预计死亡人数时,我很清楚我们有一个很好的事件可以研究。为了更好地理解这一事件,我将模型的预测与实际死亡进行了比较。似乎在 2013-05 年死亡率确实增加了,但预测和真实之间的比率达到了最大值,这意味着该模型预测过度(相对于其典型行为)。

我看了一眼 shap 值,将 2013 年至 2005 年与 2013 年至 2004 年进行了比较,就像我之前比较人口一样。就好像事件是我的“试验组”,前一个月是我的“对照组”。我可以使用完整的数据集作为“控制”,或者使用完整的数据集减去事件。请注意,根据不同的选择,您将回答不同的问题。

与上个月的功能影响比较。因为我只对增加风险的变量感兴趣(为什么峰值这么大),我可以只看正 diff 变量。
两个最大的影响差异在*伤亡人数*和*车辆数量*。我会描述“这些变量在事件期间有不同的影响”,或者更好地说“与上个月相比,这些变量不同地增加了风险”。
因为伤亡人数(和车辆数量)如此之大,我决定查看有大量伤亡的数据。我在同一事故的数据集中找到了 4600 行!其中 12%报告死亡。

2013 年在 Sheppey 十字路口发生的重大事故。[来源](https://www.kentonline.co.uk/sheerness/news/britains-biggest-crash---one-22962/)。
只是出于好奇,我做了一点研究,发现了关于这个巨大事故的[很多新闻,经经纬度证实。甚至还有 youtube 视频。但是我注意到自动`to_datetime`没有正确捕捉日期,因为它是 9 月 5 日,而不是我想的 5 月 9 日。此外,4600 行的绝对数量与 150 辆汽车的消息不一致,至少乍一看是这样,因为我没有对数据生成和数据集的创建进行更多的研究。无论如何,这里的目标与其说是从这个特定的数据集获得洞察力,不如说是说明技术,所以我不会担心这一点。](https://www.bbc.com/news/uk-england-kent-23970047)
假设我们想确保我们理解为什么我们的预测增加了那么多:然后我们可以测试伤亡人数和车辆数量是否真的使我们的预测相差那么多。

因此,我将这些变量固定为上个月的平均值(大约 2),并重新运行该月的预测。我们可以看到预测下降了很多,完全打破了那里原来的峰值。
我在这里做的分析都很简单,在某些情况下,你可能需要做得更好——比如以一种更微妙的方式输入变量,而不是一些平均值——但我相信这些简单的分析或类似的东西可能会在很多时候提供足够的信息。
## 应用程序
想象一下,你正在预测一家电子商务公司下个月的收入,突然你的预测比上个月下降了 20%!这可能是因为业务或经济中的实际问题,也可能是因为您的模型中的某些变量由于某种任意原因而中断。你可以使用我称之为的*形状事件分析来解开正在发生的事情。*
或者想象一下,你的预测一周接一周地不断减少,你想知道这些变化是系统性的,依赖于许多变量,还是由一两个变量引起的。这类问题可能需要我们考虑更好的分析,比如根据形状随时间变化的斜率对特征进行排序,等等。
无论如何,有趣的是引起变化的主要特征可能不是模型中最重要的特征。我们讨论的是相对形状,其平均形状从 0.05 变化到+0.05 的特征在事件发生前和发生时的汇总图中都是不可见的,但可能是预测变化的最重要驱动因素。
# 结论
我很高兴至少有一个有趣的数据点可以更好地理解,因为我不确定这些 kaggle 数据集是否足以说明这些技术。我希望已经给了你一些基础和直觉,关于更好的 shap 分析,可以用来理解一般的东西,甚至监视正在发生的事情。
希望你觉得它有用,如果你有疑问,请通过在 LinkedIn 上发布答案或[给我发消息来告诉我。](https://www.linkedin.com/in/estevao-uyra-pv/)
在[这个 Kaggle 笔记本](https://www.kaggle.com/estevaouyra/shap-advanced-uses-subpopulations)中找到所有的分析代码。
# 你可以用数据库代替卡夫卡
> 原文:<https://towardsdatascience.com/you-can-replace-kafka-with-a-database-39e13b610b63?source=collection_archive---------6----------------------->

[图片来自 Unsplash]
## 但是看在上帝的份上,不要
工程师们一直在寻找少花钱多办事的方法。这篇文章温和地提醒人们,与陈词滥调相反,少并不总是多。
我愉快地想起了一集旧的 Top Gear,主角们每人得到 1 万英镑,购买一辆二手中置引擎意大利超跑,从布里斯托尔开到斯劳的一家绅士俱乐部。以经典的 Top Gear 方式,大屠杀接踵而至:哈蒙德买了一辆锈桶法拉利 308 GT4,克拉克森买了一辆“不可阻挡的”玛莎拉蒂 Merak(刹车曾有过辉煌的日子),梅带来了一辆从一开始就坏了的兰博基尼 Urraco(由于电路不稳定,开在卡车后面)。从娱乐的角度来看,这一集真的很棒。汽车本身真的很糟糕;没有人能坚持到拍摄结束。
你可能会想,他偏离了自己的轨道。这一切跟数据库和卡夫卡有什么关系?首先,这一集在 2011 年播出,同年 LinkedIn 发布了 Kafka。但我向你保证,不是那样的。
是什么促使了这一切?网络上充满了奇特的比较和古怪的伪逻辑含义。例如,因为 Apache Kafka 是一个持久的中间件,一些互联网“专家”热衷于指出它实际上是一个数据库!它让你存储记录并检索它们,当然这是一个数据库。不,不是,但这有点跑题了。最近,我的兴趣被一个更罕见、甚至更不寻常的论断激起了——如果你愿意的话,是前者的反义词。也就是说,由于他们所谓的相似性,你实际上并不需要卡夫卡——你需要的只是一个普通的旧数据库。消息传递:已解决!表面上。
花点时间澄清一下,我已经被关于仿效卡夫卡《没有经纪人》的关键部分的建议淹没了。当您可以通过快速访问零件箱来构建自己的平台时,为什么要采用世界上最受欢迎的开源事件流平台呢?你所需要的只是一个数据库和一点编程工作?
我最近在 Quora 上做了一篇[短片,简洁地解释了为什么这是一个如此糟糕的想法。在这里,我有机会阐述一下。](https://www.quora.com/Why-do-we-need-Kafka-We-can-always-write-events-to-a-database-instead-of-a-topic-and-consume-those-events-by-querying-the-database-every-second/answer/Emil-Koutanov)
公平地说,Quora 问题的关键并不是用一个类似的数据库支持的解决方案来取代 Kafka,以及所有的华而不实的东西,而是更可能是一种快速而廉价地实现基本的持久消息传递功能的方法,而不需要致力于一个成熟的消息传递平台——另一组齿轮,它们有自己的部署和维护开销、基础设施要求、技术学习曲线等等。从表面上看,这是一个很有说服力的论点——为什么要有意识地引入复杂性,而用少得多的资源就可以获得相似的结果呢?所以,我们再深入挖掘一下。
我们都遇到过这种情况:一个组织要求在激进的时间框架内实现雄心勃勃的目标,而这个组织似乎很少关心构建可持续的软件密集型系统,以增加长期价值。也许你在一家初创企业中辛勤工作,或者为一家大型商业银行工作……更重要的是,你的团队领导或友好的开发经理让你承担一项需要做出重大工程妥协才能实现目标的任务。团队没有使用正确的工具来完成工作,而是决定用手头的东西来凑合。毕竟,时间是至关重要的。那些管理奖金不会自己赚到。(实际上,你会感到惊讶。)
首要前提很简单。以共享数据库服务器为例——关系模型或文档存储。将事件作为记录从生产者进程写入表(或*桶*、*集合*、*索引*等)。(确切的术语取决于所讨论的数据库;因此,我们在最广泛的意义上使用术语“表”。)数据库可以使用一个表来保存所有事件记录,用一个索引属性来表示事件类型——模拟“主题”的概念。此外,主题可以通过“分区”进一步分解,使用另一个索引属性捕获记录键或它们的散列。方便的是,使用分片的 No/NewSQL 数据库可以提高可伸缩性,因为主题和分区可以分布在数据库节点的主动-主动集群中。
从消费者实例定期轮询数据库,在处理记录时更新消费者的状态。在数据库支持的情况下,在消费者端使用连续查询来最小化资源开销和周期性查询的延迟。(连续查询在 NewSQL 产品中变得越来越常见;虽然在主流 SQL 数据库中还是有些少见。理想情况下,模型应该允许多个生产者和消费者同时操作;否则,系统的可伸缩性将受到损害。无法支持并发消费者,系统至少应该支持主动-被动故障转移;否则,可用性将会受到影响。
下图展示了我们的临时事件总线的概念模型。

艺术家对数据库支持事件总线的印象[图片由作者提供]
跟踪消费者状态和处理不相交的消费者组的精确机制是不确定的。您可能会考虑借鉴 Kafka 的著作,使用偏移量来跟踪消费者的位置,同时将记录持久化到逻辑消费之外——从而支持单个持久化数据集中的多组消费者。或者,可以通过在插入点复制记录,在消费后删除记录来实现不相交的消费者组。
图中还有几项值得注意。第一个是“仲裁逻辑”。我们需要一种机制来仲裁记录数据的消费,这样就不会有记录被两个相关的消费者同时消费,并且记录以正确的顺序被处理——这是我们系统的安全属性。此外,我们需要确保消费者偶尔的失败不会阻碍进展——活性属性。这两个属性在分布式系统的设计中至关重要。
图表中的另一个重要项目是“内务逻辑”。这涉及到清除陈旧数据的需要,确保我们不会失去控制而耗尽存储。或者,我们可以将较旧的数据移动到更便宜的存储层,而不是不可挽回地将其清除。内务处理还可能包括诸如记录压缩之类的活动——丢弃已被更新事件取代的事件,避免下游的额外处理。
架构部分就绪后,让我们看看游戏如何进行。
从功能的角度来看,在数据库之上设计一个消息传递系统是完全可能的。但是,您很快就会意识到,数据库只能满足您最基本的持久性需求——记录的存储和检索、备份和复制(如上所示),以及(在某些产品中)高可用性和/或水平 I/O 扩展。您甚至可以获得原子事务。
像 Kafka 这样的平台关注的是以记录为中心的流数据,这主要涉及到*分布*——而不仅仅是记录的持久性。这就是棘手的地方。在构建分发层的过程中,当涉及到仿真 Kafka 提供的开箱即用的基本功能的子集时,您几乎总是要重新发明轮子。
例如,Kafka 有内置的机制,用于扇出数据,并通过持久偏移支持多个不相交的消费者,这是通过一个称为消费者组的结构自动管理的。Kafka 还允许您轻松地对数据进行分区,以并行处理记录,同时保留因果相关记录的顺序。一个使用者组可以用来在一个组内的使用者群体中平均分配分区,这是一种负载平衡器。它处理消费者的扩展以及消费者组内的故障——在组中幸存的成员之间重新分配负载。在上图中,这被笼统地称为“仲裁逻辑”。模仿这种功能需要您从头开始构建一个组成员服务,以及一个故障检测器。如果使用 Java,你可以遵从一个库——比如 [JGroups](http://www.jgroups.org/) 。或者,如果你需要与其他语言交互,群组成员可以在[领事](https://www.consul.io/)或[阿帕奇动物园管理员](https://zookeeper.apache.org/)之上实现。或者,如果你真的想要简朴,你可以依靠数据库来帮助仲裁组成员——通常通过一些应用级的租用协议。仲裁逻辑驻留在哪里是一个单独的问题;仲裁逻辑可以打包成独立的进程,也可以嵌入到消费者的进程空间中。采用后者会使您拥有更少的“活动部件”,但如果消费者生态系统是用多种编程语言实现的,则会导致更复杂的消费者实现和可重用性的损失。无论如何,你正在积累复杂性——这正是你想要避免的。
下一个:家政。存储很便宜,但不是免费的。I/O 也不是,尤其是在处理非常大的 B 树索引时,这种索引随着时间的推移变得臃肿和支离破碎。实现基本的基于时间的清除本身并不困难,但是它需要在某个地方托管清道夫逻辑——通常在一个专用的进程中(比如 CRON 作业)。或者,您可以在生产者或消费者流程中托管清道夫,这可能需要一些协调逻辑,并且在多语言生态系统中再次丧失可重用性。存储分层将复杂性提升了一个档次。最后,您可能需要在某个时候实现压缩——删除被最近事件取代的事件记录,从而减少消费者端的处理。现在,您正在执行复杂且消耗资源的查询,平衡功能性、可维护性和性能。
事件流平台的另一个经常被忽视的特性是访问控制。Kafka 提供了对系统中所有参与者(消费者、生产者、管理者)的细粒度控制,以及对操作类型(读、写、创建、删除、修改等)的细粒度控制。)它们被允许调用和可能被操作的资源(主题、消费者组、配置)。换句话说,我们可以准确地控制谁可以访问什么。在两个进程通过共享介质进行通信的小型系统中,安全性可能会被忽略。然而,随着我们系统的增长,信息安全变得势在必行。大多数数据库提供访问控制;但是,它的粒度仅限于表级别。在一个表中包含逻辑上独立的记录集的情况下,这可能是不够的,这迫使您将记录拆分到多个表中,并相应地分配权限。你希望避开经纪人吗?抱歉,老伙计。
在数据库的基础上构建这些功能(以及我没有提到的许多其他功能)——虽然可能,但也是一项艰巨的任务。从基本原则出发实现这一点既耗时又容易出错,并且需要“一套特殊的技能”。
从非功能的角度来看,Kafka 在生产者端(单位时间内发布大量记录)和消费者端(尽可能并行处理大量记录)都针对高吞吐量进行了优化。数据库不能碰这个。
通用数据库——包括关系数据库和 No/NewSQL 数据库——通常面向广泛的存储和检索模式。性能自然是一种妥协。Kafka 可以轻松处理在商品和云硬件上每秒移动数百万条记录,延迟在数十到数百毫秒之间。我已经在稍微受欢迎的[为什么卡夫卡如此之快](https://medium.com/swlh/why-kafka-is-so-fast-bde0d987cd03)一文中解释了它是如何做到这一点的。为了方便读者,我在这里总结一下。
Kafka 没有详细说明“快速”的定义,而是通过某些深思熟虑的设计决策实现了其标志性的性能特征,如使用仅附加日志(避免随机 I/O)、批处理读写、批处理压缩、非刷新缓冲写入(避免`fsync`)、零拷贝(不涉及 CPU 并最大限度地减少模式切换的 I/O)、绕过垃圾收集,以及其他几项。相反,Kafka 没有提供基于内容定位和检索记录的有效方法——这是数据库相当有效的方法。(这就是卡夫卡不能被视为数据库的原因;如果不扭曲基本定义,就不可能。)但是作为一个分布式的、只附加的日志,Kafka 是无可匹敌的。
在数据库中实现类似的吞吐量和延迟数字可能需要专业硬件和高度集中的性能调优的组合,这本身就是一项利基技能。根据所选择的数据库,这实际上可能无法实现。在对性能敏感的环境中设计和构建数据密集型分布式系统还需要具备跨多个学科的卓越工程能力。
举一个例子——偏移提交管理。卡夫卡的速度来源于效率。不同于在消费点删除消息的传统消息代理(招致随机 I/O 的惩罚),Kafka 不会在消息被消费后清除它们——相反,它通过在内部主题上发布特殊的“偏移”记录来独立跟踪每个消费者组的偏移。(实际上,递归地应用它自己。)Kafka 的消费者很“便宜”,因为他们不会修改日志文件。这意味着大量的消费者可以同时阅读相同的主题,而不会使集群不堪重负。添加一个消费者仍有一些成本,但主要是顺序读取,顺序写入率较低。因此,在多样化的消费者生态系统中分享一个卡夫卡主题是相当正常的。
在数据库上模拟消费者组当然是可能的,但是这并不简单,也不具有高性能——更新消费者的偏移量需要数据库 I/O,这并不便宜。或者,您可以通过物理或逻辑复制每组消费者的记录来模仿消费者群体——很像 RabbitMQ 和类似产品采用的扇出策略——然后在消费后立即删除记录。同样,数据库 I/O 是不可避免的;更糟糕的是,你现在正在放大写。你甚至可以走上优化之路:使用不同的数据库来跟踪偏移量,甚至使用磁盘支持的内存缓存——比如 Redis、Hazelcast 或 Apache Ignite。以复杂性为代价,I/O 变得更便宜。
综上所述,也许您有一个小型系统,您的消息传递需求非常基本,您的性能和可用性需求适中。在这种情况下,以上几点都不太可能说服你;数据库支持的解决方案可能看起来更有吸引力,概念上也更简单。一想到摆脱经纪人可能就很有诱惑力。您过去可能已经构建过类似的数据库支持的解决方案,并且可以理解的是,您有信心可以用最少的努力来修改现有的代码。
这很公平。然而,我劝你在考虑现在的同时,也要考虑未来的需求。您需要确定,无论您今天部署的是什么解决方案,您和您的同事都可以在未来几年轻松维护(重点是后者)。该系统需要应对未来的负荷,或者至少提供一条增长的途径。我倾向于认为,在某个平行宇宙中,我们都是分布式和容错系统方面的知名专家,超快、无限可伸缩的数据库无处不在。现实更加令人清醒。使用 Kafka 这样现成的事件流媒体平台的优势在于,你可以利用其建设过程中所付出的巨大工程努力——该平台已经存在了大约十年,最初由 LinkedIn 孵化,2011 年开源。这转化为无数的基本特性、稳定性、性能,以及一个庞大而积极的工程社区的支持,从而确保产品的连续性。
你可能会问,这一切与《Top Gear》这一集有什么关系?主角的任务是在资源不足的情况下实现一个不切实际的目标。以娱乐的名义完成,非常精彩——为了你最终的观看乐趣,以他们的代价获得一个难忘的笑声。同样,试图在共享数据库上模拟一个功能和性能适中的事件流平台是徒劳的,也有点讽刺意味。如果有的话,这将是值得纪念的。
在处理以事件为中心的数据时,是否没有数据库支持的解决方案的空间?当然有。在事件驱动的架构中,数据库可以与事件流平台和谐共存。这是另一篇短文的基础——事件存储库的设计。在这里,我认为事件存储的大多数预期功能——实体状态快照、二级索引、实体突变历史、窗口和聚集、流连接等等——只能使用数据库来实现。我还认为没有放之四海而皆准的事件商店;任何重要的事件存储几乎肯定是定制的实现,由一个或多个现成的数据库支持。总的来说,数据库在组织数据以便有效检索方面非常有用,只要你不把它们用作(近)实时发布平台。
这篇文章对你有用吗?我很想听听你的反馈,所以不要退缩。如果你对卡夫卡,Kubernetes,微服务,或者事件流感兴趣,或者只是有什么疑问, [*在 Twitter 上关注我*](https://twitter.com/i/user/562466177) *。我也是*[*Kafdrop*](https://github.com/obsidiandynamics/kafdrop)*的维护者和* [*有效卡夫卡*](https://www.apachekafkabook.com/) *的作者。*
# 机器学习确实需要数学
> 原文:<https://towardsdatascience.com/you-do-need-math-for-machine-learning-cf934a607960?source=collection_archive---------7----------------------->
## 不要跳过 ML 中最不受重视的技能。写代码,学理论。

照片来自 U[n flash](https://unsplash.com/photos/5mZ_M06Fc9g?utm_source=unsplash&utm_medium=referral&utm_content=creditShareLink)——署名属于[罗马法师](https://unsplash.com/@roman_lazygeek)
# 介绍
显然,让有抱负的机器学习工程师相信学习理论在实践中被高估已经变得非常流行。各自文章的作者喜欢声称他们能够在没有深刻理解数学概念的情况下做好工作。
由于迄今为止,我在大多数行业项目中的经历都完全相反,所以我想分享一个基本上与众不同的观点。因此,我认为这篇文章会受到我个人对这个话题的偏见的影响。然而,我对这些论点深信不疑,所以希望你能容忍我。
首先,让我们从一个思维实验开始:
# 潜在的生存偏差
想象一下,一个没有任何数学知识的未来 ML 工程师正在学习基础知识,准备涉足这个领域。他或她在网上阅读的许多文章认为,对该理论的良好理解或多或少是强制性的。除了辞职,我们的潜在客户现在面临两种选择:
* 遵循建议,建立坚实的数学知识基础。与此同时,最好学习编码和个人项目工作
* 忽略这些建议,只关注后两个
假设他们选择了选项 2)并找到了一份工作。一切都很顺利,他们设法在行业中茁壮成长。四年后,他们发现根本不需要那些配方。事实上,他们能够证明那些来自学术界的势利小人是错误的,那些人告诉他们,如果没有基础知识,他们就会失败。
现在可能是在 Medium 上写一篇文章的好时机,告诉大家数学和统计在现实世界的机器学习中是如何被高估的。
相反,考虑一个平行宇宙,在那里我们的曼梯·里学徒有着完全相反的经历。工作几个月后失败了,因为不知道一些理论,事情就不那么容易了。现在,他们可能会也可能不会决定是时候弥补他们的知识差距了。
然而,他们写一篇关于这种经历的文章的动机可能会比第一种情况低得多。谁喜欢在公共场合谈论他们个人的失败,尤其是由一点点粗心引起的失败?
这个虚构的场景绝对不是为了贬低任何有过这样经历的人!相反,我想鼓励你在没有理论的情况下,对机器学习成功的个人故事持保留态度。
仅仅因为它对互联网上的一些人有效,并不意味着同样的方法对你或一般人也有效。
我甚至会说,从长远来看,将机器学习理论视为一门被高估的学科很容易让你陷入困境。为了支持这种说法,我现在将继续举一些其他的例子,并把它们放在一个更广阔的视角中。
这些场景在某种程度上是基于我自己的轶事经验和偏见。因此,我建议你也不要盲目相信我自己的推理。
# 每个问题看起来都像钉子
让我们假设您——相信预打包的库就是您所需要的——正在开发一个需求预测模型,该模型是根据过去销售的商品进行训练的。网上有很多使用 RNNs 和 LSTMs 进行时间序列预测的例子和教程。
因此,您只需选择您最喜欢的模型,并根据您的数据进行调整。
一切都很好,MSE 每天都在变得更好,并且你格外小心不要在你的测试中引入任何[前瞻偏差](https://www.linkedin.com/pulse/avoiding-forward-bias-time-series-machine-learning-rohit-walimbe-1/)。最后,您将模型提交给管理层,现在第一个问题出现了:
管理层希望在他们的预测中看到置信区间。
您进行了研究,了解到您还可以使用神经网络来拟合基于网络输入的 T2 正态分布。这个例子显然需要根据您的递归模型进行定制。
鉴于您现在已经有了开箱即用运行 ML 包的经验,这当然不再是一个问题。模型得到更新,你做下一个演示,甚至管理层都很高兴,你的模型最终被推向生产。现在,每个人都很高兴接下来的两周。
在短暂的平静之后,你的采购和销售部门开始抱怨。出于某种原因,你的模型在某些情况下预测了负需求。这再次导致越来越不愿意相信一个除了你和你的团队之外没有人理解的模型。
如果这些置信区间不是一个要求,你可以简单地通过正确的激活函数挤压你的预测。一个 ReLu 可能就足够好了。
这在这里是不可能的,所以你再次回到实验室,发现你可以[截断正实数的正态分布](https://en.wikipedia.org/wiki/Truncated_normal_distribution)。由于你的问题现在相当具体,你在网上再也找不到合适的例子,因此需要自己摸索。
最后,你成功了,模型实现了,你期待着最终离开这个项目。不知何故,随着时间的推移,事情变得有点令人伤脑筋。
然后,在你开始做你的第一个模型几个星期后,有人提醒你卖出的商品数量不一定等于实际需求。出于显而易见的原因,你不能比你的公司销售的更多。
因此,如果有足够的数量,你在数据中看到的可能远远低于人们实际购买的数量。
这就把你带到了审查数据的概念。你现在必须完全自己推导的损失函数,给了你一些不舒服的感觉,你开始想接下来会发生什么。
这个例子是构造出来的吗?在某种程度上,是的,但我实际上在实践中处理过一个非常类似的问题,说得好听点,知道一点理论是相当有帮助的。这里有两件事需要考虑:
**1)在上面的例子中,如果你首先意识到你的问题的统计特性,你能节省多少时间?**
当然,每个项目迟早都会结束,希望是一个成功的项目。然而,在这个过程中,如果你能够以一种全面的方式定义你的问题,你就有可能跳过很多的尝试和错误。
如果我们在早期就意识到我们正在处理一个连续的、积极的、被审查的目标,我们可能会更快地找到一个合适的模型。
现在让我们实话实说:你总是会遇到超出你现有知识范围的问题。尽管如此,坚实的基础至少可以帮助你把解决问题的过程引向正确的方向。
**2)你能只用标准库就做到吗?**
尽管有上面的例子,你仍然可以通过预打包的模型获得合理的性能。但是,请想一想:如果您知道您的数据具有高度二次关系,则具有二次变换要素的简单线性模型可能会优于任何复杂的替代模型。
一个复杂的神经网络可能需要数百个示例才能在这项任务中获得合理的性能。另一方面,我们提出的线性模型只需要一部分数据就可以快速收敛到生成函数。
现在让剩余项近似遵循广义 Beta 分布,定制模型的性能会突然比 sklearn 的任何模型都好。
另一个更实际的最近的例子是新冠肺炎模型。如果你想让你的生活变得简单,你可以把每天的病例输入 LSTM 来预测未来的感染。然而,这种方法会完全忽略像 T2 先生及其后代这样的烈性传染病模型。
这种方法已经存在了几十年,单独使用任何一种 ML 算法都很难胜过它们。一个奇特而有创意的解决方案可能会把 ML 放在这些已建立的模型之上。
如果你决定,在任何时候,将你的 ML 方法建立在 SIR 的基础上,你最好准备好钻研一些公式。突然间,微分方程不再那么没用了…
总结这一部分:不要误解我!预包装的型号通常开箱后性能很好。然而,你应该记住,有时你需要为你的特定问题量身定制解决方案,以获得实际优势。
当您的数据太小时,ML 实际上无法自己找到任何有意义的结构,这种情况尤其如此:
# 在机器学习中,数据集的大小很重要
考虑另一个虚构的场景:
你正在参加一个工作面试,并与你未来的老板讨论你的机器学习模型可能会为他们解决什么问题。假设我们正在与一家工业材料生产商打交道,他们的目标是通过计算机视觉检测传送带上的受损货物。
碰巧的是,你在过去的 6 个月里一直在图像分类项目上努力工作,而你的 Kaggle 分数在这段时间里直线上升。这给了你极大的自信,你告诉你的潜在雇主,他们最好当场雇用你。
快进—你得到了这份工作,现在你第一次看到了他们目前收集的数据。当你把鼠标移到数据文件夹,发现总共只有 100 张图片时,你突然感到不寒而栗。
对于训练一个可靠的神经网络来说,这些例子显然太少了。如果你幸运的话,你甚至可以在考虑一个`import tensorflow as tf`之前,就要求他们将这个数额增加 100 倍。
万一你运气不好,你的老板会告诉你,创建新的训练数据代价太大。
质量保证团队需要进行成本高昂的测试,以确定某个物品是否受损。在这一点上,除了尝试构建一个定制模型并尽可能多地将领域知识融入其中,可能没有其他选择了。
当预先打包的解决方案不再存在时,坚实的数学基础现在允许你在你的算法中表达复杂的概念。
在更高的层面上:如果你的特定问题的性质偏离标准太多,标准库算法可能根本不起作用。在这一点上,基本上没有其他选择,除了写你自己的算法,你应该知道一些理论。
# 速度的问题
如果您经常使用 Python 和 R,您可能已经达到了尽可能避免 for 循环的程度。尤其是当您的模型在每个训练时期运行多个循环时,性能会迅速下降。
这对于现代模型来说尤其不方便,因为在现代模型中,GPU 和 CPU 已经频繁地被推到极限。
幸运的是,许多这些方法依赖于线性代数。由于后者,我们可能能够用更快的矩阵乘法来取代缓慢的 for 循环,甚至从将整个块移动到 GPU 中受益——参见[本文](https://medium.com/@aishahsofea/for-loops-vs-matrix-multiplication-ee67868f937)中的介绍性示例。
在一般情况下,我们可以依靠大量关于如何提高计算线性代数效率的文献。《T2 矩阵计算》一书可以说是关于这个话题的一个非常受欢迎的来源。
特别是对于高斯过程回归,最近已经开发了大量的性能改进。现代计算线性代数使许多这些解决方案成为可能。
这里[这里](https://arxiv.org/abs/1903.08114)[这里](https://arxiv.org/abs/1503.01057)可以找到一些参考资料。
除了性能之外,另一个支持计算的例子是浮点精度。由于内存是有限的,一些数学运算会导致有效的计算产生无用的数据。我最喜欢的统计学例子是[最大似然估计](https://en.wikipedia.org/wiki/Maximum_likelihood_estimation)。
我不会在这里详细说明,但是天真地运行最大似然法很快在计算机上变得不可行。除非—我们在这个过程中利用了对数的单调性和积和性质。
你可以在这里找到关于这个主题[更广泛的解释,在这里](https://math.stackexchange.com/questions/892832/why-we-consider-log-likelihood-instead-of-likelihood-in-gaussian-distribution)找到一个相关的问题[。](http://gregorygundersen.com/blog/2019/01/18/log-likelihood/)
当然,你很可能不想在学术界发表你的模型和解决方案,那么为什么还要费心呢?嗯,一个聪明的数学“技巧”可以决定在一个 10 实例 GPU 集群和一台笔记本电脑上运行您的模型。
能够以这种方式为公司和客户节省大量资金,肯定会提升你作为机器学习工程师的市场价值。
# 大海捞针
让我们考虑赞成学习某种理论的最后一个论点。如果你曾经不得不调试一个复杂的计算机程序,你可能会体验到这个过程是多么的令人厌倦。
有时候你需要花几个小时去发现代码中的一个问题,然后才意识到这个错误实际上是多么的愚蠢。
现在想象一下,你要用一种你从未见过的外国编程语言调试一个程序。如果你幸运的话,有人已经在 Stackoverflow 上解决了完全相同的问题,你可以简单地复制他们的解决方案。
然而,当程序太复杂时,你可能需要自己解决用外语工作的障碍。在某些时候,你可能会找到正确的解决方案,然后继续前进。
然而,这个过程可能会比你一开始就知道这门语言要困难得多。更有经验的人可能只需要几分钟就能解决一个你花了几个小时甚至几天才发现的问题。
当你在没有任何理论知识的情况下实现复杂的深度学习模型时,这也可能发生在你身上。现代神经架构看起来越来越像真正的计算机程序。
如果你不相信我,我推荐你看一看[神经图灵机](https://arxiv.org/abs/1410.5401)或者[可微分神经计算机](https://deepmind.com/blog/article/differentiable-neural-computers)。
现在,即使你在 Github repo 中找到了你最喜欢的模型,你可以复制和粘贴,一旦你输入你的特定数据集,你可能仍然会得到奇怪的结果。
渐变爆炸?也许你应该首先标准化你的数据。你的可变自动编码器产生垃圾?考虑将您的方差输出神经元转换为严格正的。正态分布不适用于负方差。
虽然尝试、错误和有益的在线社区可能最终会引导你找到解决方案,但这个过程可能会相当令人生畏和漫长。如果你不能依靠对正在发生的事情的整体理解,你遇到的任何错误都可能让你受到机会和其他人的帮助的支配。
在您的机器学习之旅的早期,这肯定会定期发生在您身上,并且是意料之中的。然而,在某些时候,你可能不想再用 Stackoverflow 上的五个未回答的问题来为错过的截止日期辩护了。
# 关键要点
缺乏数学和统计知识应该阻止你成为一名机器学习工程师吗?肯定不是!在生活中,平衡的方法可能是最可持续的选择。
我绝对不认为博士学位是在这个领域取得成功的必要条件。不过,让我总结一下我的主要观点,以建立一个坚实的理论基础:
* 找到提高预测和计算性能的方法的机会增加了
* 您有更广泛的工具箱来解决非标准问题
* 开发变得更快更有效,因为您确切地知道您的模型内部发生了什么
* 您可以在早期发现违反理论的情况,并避免它们导致任何后续问题
一开始就学习肯定会让你慢下来。尽管如此,长期的好处将远远超过最初的麻烦。
希望这篇小文章已经说服你开始深入研究机器学习的理论方面,如果你还没有这样做的话。当然,你不应该忽视这门手艺的其他方面,比如编程、计算机科学和早期的应用项目。
如果你刚刚开始,我希望你在这个迷人的旅程中有很多乐趣。
最后,如果你不同意我的评估,我期待着一次愉快的讨论——无论是在评论中还是通过其他沟通渠道。
*原载于 2021 年 7 月 2 日*[*【https://sarem-seitz.com】*](https://sarem-seitz.com/blog/you-do-need-math-for-machine-learning/)*。*
# 你不会因为使用花哨的技术而变得更好,而是通过在基础上努力
> 原文:<https://towardsdatascience.com/you-do-not-become-better-by-employing-fancy-techniques-but-by-working-on-the-fundamentals-17d5c471c69c?source=collection_archive---------9----------------------->
## 《元学习:**如何学习深度学习并在数字世界茁壮成长**一书的作者 Radek Osmulski 访谈
*一系列采访强调了作家在数据科学领域的出色工作以及他们的写作之路。*

照片由 Radek Osmulski 提供
> “只要你写你想写的,这才是最重要的;没人知道这是长久的还是仅仅几个小时的问题。”
> ― **弗吉尼亚·伍尔夫,*一间属于自己的房间***
为了将机器学习领域的显著工作推向前沿,我去年开始了一系列采访。在第一季中,我 [***展示了来自知名数据科学家和 Kaggle 大师***](https://github.com/parulnith/What-I-learnt-by-interviewing-numerous-Kaggle-Grandmasters) 的故事,他们分享了他们的旅程、灵感和成就。第二季,我在采访书籍作者。作为一名作家,我非常尊重写书的人。一篇写得很好的文章需要大量的时间、精力和耐心,而为一本书复制同样的内容绝非易事。本期访谈将揭示数据科学领域一些知名作者的故事。
# 见见作者:Radek Osmulski
Fastai 社区众所周知,不仅为世界提供了进入机器学习的手段,还不时提供了伟大的研究人员。Radek Osmulski 就是这样一位快速学习的人工智能研究工程师。他为来自硅谷、澳大利亚和迪拜的几家初创公司工作。2018 年,他赢得了谷歌赞助的 Kaggle 比赛。令人惊讶的是,Radek 没有正式的数学或计算机科学背景。关于学习机器学习和成为可雇佣的人,有许多事情他必须弄清楚。他在他的书→ [**元学习:如何学习深度学习并在数字世界中茁壮成长**](https://radekosmulski.gumroad.com/l/learn_deep_learning) 中记录了他的所有学习,在这次采访中,我们将了解更多关于他在这本书背后的想法、动机以及对数据科学爱好者和作家的建议。让我们开始吧。
问:这本书的想法是如何产生的?
***Radek:*** 学习机器学习有这么多让人惊讶的事情。
最初,你可能会觉得你在学习 ML 上投入了很多努力,但进展甚微。你可能会经历一系列讲座或完成一门 MOOC 课程。但是当面对现实生活中的 ML 问题时,你可能很难找到正确的方向。
你回顾过去的六个月,虽然你可能会发现你在学术上学到了很多,但你影响周围世界的能力可能只有轻微的变化。
这正是我的经历。我在这种学习的边缘状态中度过了几年,对我周围的世界没有任何影响。没有雇主敲我的门。我尝试了一下 Kaggle,但发现它非常令人困惑。
有几次,我想放弃,想中途退出。所以我连续五个月没碰过 ML。
但后来我决定再试一次。这次我不会用我的方式。我会不遗余力地研究什么对其他人有效,其他人如何学习并与社区互动,以及他们如何取得惊人的成果。
这让一切都不同了。结果是惊人的。
很快我赢得了一场纸牌游戏比赛。从那以后,我担任了几个非常体面的深度学习角色,与我见过的一些最有趣的人一起工作。
我从没想过我的生活会变成这样。我写了元学习来分享导致这个令人惊讶的结果的想法和技术。
在更个人的层面上,我写这本书的动机是为了与 fastai 社区保持联系。曾经有一段时间,我会在 fast.ai 论坛上发很多帖子,我当然很怀念那段时间。我也在寻找一种结束我投入了八年生命的冒险的感觉。
问:你能为读者总结一下这本书的要点吗?
***拉德克:*** 确定!我努力使这本书尽可能简洁。在大约 90 页的篇幅里,我讨论了学习中理论和实践的相互作用、成为一名优秀的开发人员意味着什么、我们使用的工具的重要性、机器学习的核心是什么等等。

作者图片
但最重要的一点是,你可以实现你下定决心要做的任何事情。我指的是非常具体的字面意思。不管你的背景如何,你都可以掌握机器学习。
但是你不能仅仅靠意志力和努力来做到。这样不行。
你不必设定夸张的目标,不必非常自信,也不必非常有才华。无论如何,很多事情都是旁观者的看法。
但重要的是你采用什么方法,你继续学习。
问:你认为这本书的目标读者是谁?
***Radek:*** 任何认为自己是机器学习学生的人。
这是一个非常宽泛的范畴。一方面,**元学习**的目的是让一个绝对的初学者,并向他们展示他们需要专注于什么以取得快速进步。
这对已经学习机器学习一段时间或刚刚开始学习的人来说特别有帮助。

作者图片
但你也可能是一个经验丰富的专业人士,仍然会从阅读这本书中受益匪浅。
我们都有自己的盲点,我们的弱点。你不会因为使用花哨的技术而变得更好,而是通过在基础上努力。
另外,不管你的背景如何,让自己了解互联网上的事情是如何进行的,这是一个非常有价值的提议。
**问:读者怎样才能充分利用这本书?**
***Radek:*** 念出来。记下你遇到的想法。和你的朋友讨论它们。洗澡的时候想想他们——在推特上说说他们。把它们变成你自己的。
诚实地尝试这些技术。什么对你特别有效?把这种或那种技术付诸实践,你感觉如何?
正是通过这些活动,我们可以反思自己的经历,并用自己的话来表达我们所学的东西,这样我们才能学得最好。
问:对于一个刚刚起步的新作家,你有什么建议?
***Radek:*** 写作就像机器学习一样。
从事机器学习项目有一个自然的流程。你首先想出一种方法来验证你的结果。然后构建一个简单的模型。当您迭代管道的所有组件时,您可以提高模型的性能,同时确保您的模型可以很好地概括看不见的数据。
模型架构并不那么重要。同样,使用什么编程语言来实现您的解决方案也无关紧要。或者你懂多少数学。方法本身在很大程度上决定了你是否会得到一个好的结果。

作者图片
同样,写作有一种自然的流程,当你从外向内看时,你可能没有意识到。我发现通过阅读作家的写作书籍(斯蒂芬·金的《写作论》,史蒂文·普莱斯菲尔德的《艺术之战》)和听蒂姆·费里斯的《作家采访播客》对了解作家的思维非常有帮助。
如果要我总结主要观点,就在这里。
你花在写作上的时间才是最重要的。所有的初稿都普遍令人讨厌。好的写作是你不断修改的坏的写作。你要说什么比你怎么说更重要。随身携带一个小笔记本,记下想到的点子。最后但并非最不重要的一点是,没有一种沟通是完美的,但套用史蒂夫·乔布斯的话来说,是什么造就了一名优秀的作家?他们的船!
问:你花了多长时间写完这本书?更重要的是,你是如何在工作之余写了一本书的?
***拉德克:*** 写这本书花了我八年时间。每天我都会研究一个问题,这个问题会让我在学习机器学习的过程中慢下来。或者我会尝试一种我以前没用过的技术。
我会在网上通过帖子和推特谈论这些。
感觉就像我体内有这本书,它真的在一页上跳了出来。我在 3 周内写了这本书的前 60%,其中大部分是在寒假期间写的。
这本书的其余部分也没有花太长时间来写,但随着时间的推移,它变得更加舒展。
**问:你有最喜欢的书和作者吗(在技术或非技术领域)?**
Alexey: 外面有那么多我喜欢的书!
但是我想让大家读的一本书是埃里克·d·拜因霍克的《财富的起源》。它讨论了如何使用算法来得出关于周围世界的非常有意义的结论。更重要的是,它解构了作为一门科学的经济学,并指出它错在哪里。
正如尤瓦尔·诺亚·哈拉里所说,我们人类生活在一个由抽象观念构建的世界里。没有人见过一个公司或知识产权走在街上,但这些和其他概念一样塑造了我们生活的世界。这些想法没有什么固有的固定不变,随着时间的推移,它们已经发生了显著的变化。推而广之,如果我们可以用更好的想法取代一些想法,我们周围的世界将会开始变得不同。
对于当今世界根深蒂固的不平等和对环境的破坏,没有什么比我们对人性(行为经济学)和一般经济学的看法更有贡献了。
我很乐观,因为历史表明我们有能力取代我们用来组织自己的思想,但不幸的是,我们在这方面效率很低。这个过程漫长而充满曲折,不会有任何结果,就像进化一样。
👉**你期待与 Radek 建立联系吗?关注他** [**推特**](https://twitter.com/radekosmulski) **。**
👉**阅读本系列的其他访谈:**
</dont-just-take-notes-turn-them-into-articles-and-share-them-with-others-72aa43b83e29>
# 你不需要管弦乐队
> 原文:<https://towardsdatascience.com/you-dont-need-an-orchestrator-6517b243dece?source=collection_archive---------7----------------------->

演职员表:[https://unsplash.com/photos/NsgsQjHA1mM](https://unsplash.com/photos/NsgsQjHA1mM)
这是编排数据管道的好时机。
[气流](https://airflow.apache.org/)、[路易吉](https://github.com/spotify/luigi)、[达格斯特](https://dagster.io/)、[尼菲](https://nifi.apache.org/)、[提督](https://www.prefect.io/)、[凯德罗](https://github.com/quantumblacklabs/kedro)等等……围绕数据编排工具的技术领域充满了伟大的软件,大多是开源的。
我们正慢慢地从经典的调度(又名 cron)转向真正的编排。
基本的调度包括 crontab 和其他类似的程序。你只需要写下你想运行一个任务的时刻,程序就会一直等到该运行的时候。
时间安排就是列出事件发生的时间。编排更进一步,在任务之间创建触发器。它更有机。
这些工具包源于[有向无环图(DAG)](https://en.wikipedia.org/wiki/Directed_acyclic_graph) 的概念
TL;DR;
DAG 是一种图结构,只有节点之间的直接链接,没有任何循环。这种模式的最大优势是能够创建简单的单向流。
难怪所有这些编排器都是数据科学领域的趋势:它们完全符合数据转换的线性流程。

DAG 模式。对了,写一个不一定要懂图论(图片由作者提供)。
# 工程成本
这些工具的缺点是设置时间长。
入门笔记非常容易理解,你可以在几分钟内得到工作代码。
但是要真正利用这样的框架,你需要时间、实验和有经验的工匠。
您必须深入考虑数据的输入和输出,才能进行出色的编排。你的数据从哪里来?企业如何消费数据?您正在处理流或批处理工作流吗?你的用例是如何随着时间发展的?…
如果您不能得到直接的答案,编排解决方案应该是处理复杂业务需求、遗留、新数据源、团队规模等的关键…
它们有助于建立您可以利用的长期目标渠道。这是有成本的。工程费用。
首先,你的工程师必须知道或学习一个特定的工具包。大多数管弦乐队在这里只有十年左右的时间。有几个非常有经验的家伙对这些,或者如果有的话,他们非常昂贵。
这些工具的学习曲线没有那么深。尽管如此,你不可能像使用 Jupyter 笔记本那样在一天内学会一种气流。
今天学习一种新工具,明天就会有回报。但是时间也是金钱。
# 扳机,扳机,扳机…
orchestrator 框架还有另一种替代方案:云提供商无服务器特性。
无论是在亚马逊网络服务,谷歌云平台,还是微软 Azure,都有服务来大规模地以非常低的价格编排任务。
**功能服务**:这是最有趣的服务之一。您可以在不提供任何服务器的情况下部署和启动您的代码。无论是收集还是转换数据,functions services 的设置都非常简单快捷,运营成本非常低。
**容器即服务**:类似地,你可以发布自己的容器来构建任务或 web 服务,而不需要任何服务器设置。
存储:作为云革命的一部分,如今你可以非常便宜地存储万亿字节的数据。您甚至可以设置可访问性级别来优化成本。
**排队**:跨服务异步通信。消息队列可用于分离重量级处理、缓冲或批处理工作,以及平滑尖峰工作负载。
**事件触发**:根据规则触发事件。链接任务。
下面是一个具有 AWS 特性的高级模式示例。它显示了从 FTP 到数据湖的数据摄取,这些数据到数据仓库的转换,然后自动报告的创建或机器学习任务的触发。

高层管道示例(图片由作者提供)。
**利用这些特性协调任务有一些优势。**
**快速开发**。在一个独特的云提供商内部,这些服务遵循相同的设计。当你学会使用一个,就很容易和其他人一起使用。它非常适合您在云环境中使用的其他工具。每个服务并没有聚集在一个完整的框架中。它们是可以即插即用的小软件。
**没有增加基础设施成本**。大多数时候,不需要设置服务器或任何复杂的架构。有服务。你用什么付什么。
**它执行任务隔离**。设计小范围的任务是设计事物的好方法。尤其是在投入和产出方面。这样,您可以在需要时定期添加或更新一些部件。
经典的 orchestrator 工具提供了以非常复杂的方式创建任务的设计自由度。虽然这种自由也适用于云提供商服务,但您仍然处于一个受限的环境中。您不能创建自己的服务。
这接近于所谓的“供应商锁定”。然而,限制通常会让您从过于复杂的设计决策中解放出来。
你只是协调你的任务。直男。
事实上,在经典的 orchestrator 领域中确实如此。但是你需要更多的时间和经验来正确地设计任务的输入和输出,以使事情变得更小。更多的自由,更多的可能性,因此,更多的思考,更多的争论。
有时候你想把事情做完。
**然而,也有一些不利的方面。**
您创建的工作流和触发器越多,跟踪整个事情就越复杂。在短期内,相对于开发的容易性,大的画面是丢失的。
人们可以考虑使用一些自制工具或第三方解决方案[来收集所有管道元数据信息。它在开始时可以工作,但是当你必须扩展时,事情就会变得糟糕。](https://www.cloudockit.com/)
供应商锁定也是一个问题。您已经选择与云提供商合作,所以这是更大选择的一部分。尽管如此,你不能定制,你不能添加新的功能。任何供应商的新更新都更容易让您的遗留系统崩溃。
当你根据云提供商的规则设计你所有的工作流程时,你不可能很快摆脱它。
在更全球化的自制开源软件和第三方服务中,这些是经典的争论。在构建工作流程时,这仍然是一个需要牢记的要点。
# 什么时候?谁啊。在哪里?
和往常一样,每个概念都有自己的优点和缺点。
我们描述了从数据角度编排任务的两种方式。
当选择一个或另一个仍然取决于一些因素:
如果你的团队很小,开始一个新项目,一个新的基础设施(如果它是云提供商绑定的奖励),你的选择可能是云提供商特性。
你不需要设置任何服务器,不要太担心大的设计问题,你可以快速迭代商业解决方案(因此可以快速交付),而且仍然非常便宜。
如果您有工程师、不断增长的项目、许多困难的数据输入和输出,那么 orchestrator 工具可能是个好主意。
您的优势是将所有的编排分离到适当的文件中,并且可以在用户界面中可视化大型管道工程。
大多数时候你可以自定义它。添加新功能。完全免费,如果开源,但有工程费用。
反正**整体景观成熟**。对于不同的问题,获得许多解决的机会总是好的。关键是要试验和学习几种资产,以更好地了解什么对当前项目有好处。
如果你想进一步讨论这些问题,请在评论中告诉我。我很乐意让你关注这些不断发展的技术。
我是一名数据科学家和工程师,曾在新闻、零售和职业体育等多个行业工作过。通过这项工作,我了解到分享我们自己的经历是帮助他人和发展自己的一种强有力的方式。如果你也有这种想法,并且正在寻找大量的数据资源,[请订阅我的时事通讯,从工程师的角度来看](https://fromanengineersight.substack.com/)。
# 机器学习不需要数学
> 原文:<https://towardsdatascience.com/you-dont-need-math-for-machine-learning-e168b7d973d4?source=collection_archive---------3----------------------->
## 意见
## 跳过 ML 中最被高估的技能。改为编写代码。

照片由 [**Max Fischer**](https://www.pexels.com/@max-fischer?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) 发自 [**Pexels**](https://www.pexels.com/photo/photo-of-woman-standing-in-front-of-blackboard-5212320/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)
数学很恐怖。
你想做机器学习,但你读过它需要概率论、统计学、微积分和线性代数。
我猜你要回到学校呆 4 年…
谢天谢地,这不是真的。就拿一个自学 ML,然后花了 3 年时间为一家初创公司运行机器学习的软件开发人员来说吧。
我会解释为什么你不需要数学。
# 学习数学会让你慢下来
想象一下,在驾驶汽车之前需要了解发动机是如何工作的?这可能有助于你在比赛中调整赛车。但是你肯定不需要这些知识来开车。
类似地,许多 ML 作者建议在实现机器学习之前涵盖几个数学主题。虽然这是好意,但这不是你现在需要的建议。
为什么 ML 建议这么学术?其中大部分来自那些在学术界呆过的人。在人工智能研究中,数学是必不可少的。需要解剖模型,发明新算法,写论文。
但你不是在写论文。你学到的东西已经足够危险了。解决问题,创造价值。甚至可能围绕你所创造的东西开一家公司。这将是 90%的实践和 10%的理论。
您可以通过反复试验、遵循最佳实践和发展经验法则来学习 ML。注册 [Kaggle](https://www.kaggle.com/competitions) ,解决问题,回顾其他成功的提交,迭代。6 个月后,你会对自己的能力感到惊讶。
# **ML 工程师和数据科学家不怎么做数学**
真正的挑战是数据,而不是数学。
检索、探索、清理、整形和可视化数据是这项工作的主要部分。这需要技巧、经验和直觉的结合。
有了顶级的数据辩论技巧,你就有 90%的机会来训练强大的模型。SQL、Pandas 和可重用函数是您的好朋友。远不止微分方程。
作为一名数据科学家,我承认使用过几次数学。我已经编写了计算置信区间的函数,并用图书馆不支持的语言重写了距离度量。但即使是这些,我也是按需学习的。
一些数据科学家从头开始重写复杂的算法,但这是例外而不是规则,对于刚刚进入该领域的人来说是不需要的。
选择、培训、评估和部署模型——这些都不直接需要数学。我们从现有的库中选择合适的工具,并在需要的地方应用它们。
通过将现有工具与专有数据相结合,有足够的价值可以提取。如果需要开发新模型的能力,大多数 ML 从业者将会失业。
# **图书馆为你挑起重担**
现成的工具是你最好的朋友。
Python 有数量惊人的(并且还在增长的)库,这些库实现了几乎所有的 ML 算法。你每天的工作就是选择合适的工作,然后创造性地运用它。
甚至[吴恩达](https://en.wikipedia.org/wiki/Andrew_Ng)著名的[深度学习课程](https://www.coursera.org/specializations/deep-learning)在让你从零开始建立一个神经网络之后,也依赖于使用现有的库。使用成千上万其他科学家已经在使用的久经沙场的库是有意义的。这确保了它们经过良好的测试并不断改进。
凭借对输入、输出和配置的一些直觉,您可以放心地将大多数模型视为一个黑盒。
## 这些 Python 库让我受益匪浅:
* 熊猫。加载和整形数据。
* NLTK。标记和处理文本。
* sci kit-学习。你所有的经典 ML 算法。
* 喀拉斯。Tensorflow 的高级 API。
* 根西姆。无监督学习。
* Numpy。使用数组。
* 斯帕西。各种 NLP 工具。
你不需要理解数学,因为库的作者已经为你做了。我们可以放心地在晚上睡觉,相信他们已经正确地实现了算法。
在行业和实践中,好处在于创新模型的应用方式,而不是创造新的模型。
# **在编码和数学之间,学会编码**
在解决一个 ML 问题的竞赛中,软件开发者每次都赢了数学家。
懂 SQL,Python,有点[黑客](http://www.paulgraham.com/gba.html)的感觉会让你走的又远又快。了解微积分和微分方程可能会让你走得更远,但这将是一个缓慢的旅程。
因此,如果时间有限,可以选择学习数学或编程,那就学习编程吧。
如果你能阅读文档并实现它,你就成功了一半。
如果你决定不把机器学习作为职业,编程技能可以让你找到其他几份工作。如果你没有证书,我就不能对数学说同样的话。
# **您可以通过全部试用来选择最佳型号**
数学会给你关于模型如何工作及其权衡的直觉。但是还有一个选择。尝试所有型号。
云平台允许通过点击一个按钮并行运行无限的代码。
当我在 ML 职业生涯早期面临新问题时,我运行了每一个流行的 Scikit-learn 模型,并选择了一个效果最好的模型。一个名为 [FloydHub](https://www.floydhub.com/) 的云服务让我在编写了几个简单的脚本后就可以做到这一点。
也就是说,**详细记录你进行的每个实验**。这将有助于您找到模型解决特定问题的模式。如果你注意到一个模型持续优于其他模型,慢慢挖掘它是如何工作的,并开始从概念上理解它。
随着时间的推移,这将建立你的直觉,你将不再需要尝试所有的事情——这个模型将不再是一个黑箱。
# **如果你一定要学数学**
也许你被算法的具体细节所吸引。太好了!了解它们是如何工作的。但要明智地去做。
不要从统计学教科书开始。从挑选一个你用过的模型开始。
研究模型并将其分解成小部分。然后尝试从头开始编码。一旦你这样做了,你就可以反直觉地再次抛弃数学。你现在受益于它给你的直觉。
不同的数学将帮助你学习 ML 的不同部分。梯度下降的微积分,神经网络的线性代数,解释结果的统计学,以及框架问题的概率。也就是说,你只需要其中的一部分。
还是那句话,从问题入手,而不是从课本入手。毕竟,我们记得我们应用了什么。
# **最终想法**
如果你已经读到这里,我希望我已经说服你直接投入进去,不用担心数学。虽然如果你有数学知识会有所帮助,但是不要让数学知识的缺乏阻止你。
我想让你学 ML,造东西。机器学习在自动化重复性任务、为公司提供动力以及为社会创造繁荣方面有着巨大的前景。最快的方法是尝试、错误和迭代。
善于编写代码,依靠现有的库,调查别人是如何解决问题的,并获得反馈。
到目前为止,在你的 ML 旅程中,最重要的不同是什么?
# 你不需要成为物理学家来理解量子机器学习
> 原文:<https://towardsdatascience.com/you-dont-need-to-be-a-physicist-to-understand-quantum-machine-learning-e0c91db4dfc3?source=collection_archive---------49----------------------->
## 作为一名机器学习工程师,你会做得很好
本帖是本书的一部分: [**用 Python 动手做量子机器学习**](https://www.pyqml.com/page?ref=medium_physicist&dest=/) **。**
机器学习的目的是给尚未标记的事物贴上标签。例如,我们试图预测一幅图像显示的是一只猫还是一只狗。

作者弗兰克·齐克特的图片
对人类来说,区分猫和狗是一件容易的事。为了让一台机器接管这项任务,你需要给它编程。您需要准确地指定如何执行每一个步骤。问题是,虽然我们可以做到,但我们不知道如何做到的确切规则。
大自然给了我们能力,却没有让我们知道这些能力是如何工作的。显然,这种能力本身就是一种进化优势,而对它的了解却不是。
由于无法告诉机器如何区分猫和狗,我们产生了用教另一个人的方式来教机器的想法。
如果你想教你的孩子猫和狗的区别,你给她看动物并给它们命名。
每当她正确地认出一只猫或一只狗,你就以同意来奖励她。万一她错了,你纠正她。你给孩子的反馈,哪怕是最微小的差异,都会让她有所提高,因为她为了一个奖励,下意识地优化了自己的行为。
我们没有指定如何完成任务的细节,而是向机器呈现正确标记的示例,并说:“*自己想办法*”。
教一个孩子和教一台机器之间的一个主要区别是表现形式。我们人类把知识保存在大脑中。它由无数相互连接的神经元组成,形成一个网络。我们的感官将电脉冲输入这个网络。输出转化为我们的思想和对我们肌肉的命令。作为老师,我们无法控制学生的大脑是如何工作的。
作为机器学习工程师,我们的主要任务之一是提出一种足够复杂的表示,以保持我们希望它学习的知识,但又足够简单,可以在合理的时间内训练。这种表示也称为模型。
这个模型有一个结构。它可以是任何东西,从简单的数学公式到人工神经元网络。这个模型有参数——可训练的参数。这两件事决定了我们模型的复杂性。一般来说,我们的模型参数越多,它越能执行任务,但越难训练它。
一旦我们确定了模型的结构,我们就开始训练它。我们的目标是找到一组参数值,让模型正确预测事物的标签。我们通过向我们的模型呈现示例来做到这一点,获取它的预测,并通过将模型的输出与实际标签进行比较来计算误差。
最终,我们寻找使误差最小的参数值组合。我们试图解决一个最优化问题。
量子机器学习(QML)是利用量子计算来解决机器学习任务,比如优化参数值。
我们在 QML 用来表示手头任务的特定模型可能不同于我们在经典机器学习中使用的所有模型。然而,它的目的仍然是一样的。它保存了我们想要学习的知识。
我们在 QML 使用的特定算法可能不同于我们从经典机器学习中知道的所有算法。但是它的目的仍然没有改变。我们的目标是找到最好的——或者至少足够好的——参数值。
在机器学习参数的同时,需要拿出合适的模型和合适的优化算法的,是你这个机器学习工程师。
这在 QML 没有什么不同。那么,为什么你需要成为一个物理学家来应用 QML 呢?
**因为你需要理解所有这些量子力学的东西*,你说呢?*
*在 QML,我们使用量子叠加、纠缠和干涉来解决问题。这些是惊人的,也许是违反直觉的现象。但是不管它们看起来有多怪异,量子力学系统都遵循一定的物理定律。这些定律使得系统以特定的方式运行。*
*如果我们想应用 QML,了解系统在特定情况下的行为比了解支配它们行为的确切物理定律更重要。*
*我们来看看经典的机器学习。让我们看看人工神经元网络。这些灵感来自我们大脑中的自然同类。然而,你不必成为生物学家也能理解它们是如何工作的。*
**
*作者弗兰克·齐克特的图片*
*这同样适用于量子机器学习。让我们以纠缠为例。*
*它从量子叠加开始。与经典比特不同,量子比特不是 0 或 1。它是 0 和 1 的复杂线性组合。只有当你测量它的时候,你观察到它要么是 0 要么是 1,就像一个经典的比特。*
*CNOT 门接收两个输入量子位,给出两个输出量子位。第一个输入称为控制量子位。第二个输入称为受控量子位。*
*如果控制量子位处于|0⟩状态,那么什么都不会发生。输出等于输入。如果控制量子位是|1⟩,那么 CNOT-gate 在控制量子位上应用 X-gate(非门)。它翻转了受控量子位的状态。*
*下图描述了 CNOT 门的真值表。*
**
*作者弗兰克·齐克特的图片*
*如果你有两个纠缠的量子比特,它们共享一个叠加态。一旦你测量了一个量子位,它的纠缠同伴会立即跳到不同的叠加态。即使在光年之外。它似乎知道测量已经发生,并且呈现出确认测量值的状态。*
*量子纠缠听起来很像斯科特用来把柯克和斯波克传送到企业号的传送器。*
**
*作者弗兰克·齐克特的图片*
*让我们放下科幻小说。让我们看看数学告诉我们什么。*
*我们可以用 CNOT 门纠缠两个量子位,它有两个量子位单位矩阵,最后两个元素的顺序互换,就像这样:*
**
*如果控制量子位处于叠加态,我们可以看到振幅为 1/sqrt(2)的两个状态。这些国家是|00⟩和|11⟩.其他两个状态(|01⟩和|10⟩)的振幅为 0。因此,每当我们测量两个量子位元中的一个,我们也可以扣除另一个量子位元的值。等式如下:*
**
*最后,我们来看看纠缠在实际中是如何影响量子比特的。我们从一个简单的电路开始。我们对量子位 q0 应用哈达玛门。它将量子比特置于叠加态。*
**
*作者弗兰克·齐克特的图片*
*结果显示量子位 q0(从右到左或从上到下读取)要么是`0`要么是`1`,每个概率为 0.5。量子位 q1 不受影响。我们总是测量它为`0`(因为 Qiskit 在|0⟩).状态下初始化量子位*
*让我们看看,如果我们通过应用 CNOT 门来纠缠两个量子位,会发生什么。*
**
*作者弗兰克·齐克特的图片*
*虽然控制量子位 q0 的测量概率保持不变,但是如果量子位 q0 处于|1⟩.状态,我们看到控制量子位 q1 将其状态从|0⟩切换到|1⟩*
# *结论*
*这是对纠缠的三种解释,轶事的,数学的,和实际的。这些解释都对纠缠有所启发。虽然数学解释是最精确的,但也是最难把握的。然而,你不需要成为一个物理学家来理解纠缠意味着什么,以及如何在量子电路中使用它。*
*你不需要知道支配两个粒子纠缠的物理定律。理解一个量子位是一个受限于规则的概率系统就足够了。这些规则——比如纠缠——可能并不直观。他们甚至可能是反直觉的。但是如果你接受它们,你就可以学习如何使用它们,并且不需要知道潜在的物理定律。*
*量子计算是一种非常有前途的机器学习工具。它建立在量子力学的理论和公式之上。这些都是高级物理概念。然而,你不需要成为物理学家,也能理解如何使用量子计算来解决机器学习任务。*
*本帖是本书的一部分: [**用 Python 动手做量子机器学习**](https://www.pyqml.com/page?ref=medium_physicist&dest=/) **。***
**
*在这里免费获得前三章。*
# 你喜欢云原生技术,但你知道它提倡什么吗?
> 原文:<https://towardsdatascience.com/you-love-cloud-native-tech-but-do-you-know-what-it-advocates-8d0ea9f59727?source=collection_archive---------31----------------------->
## 云原生(Cloud-Native)似乎是最近的另一个流行词,但它真正的意思是什么呢?

由 [Unsplash](https://unsplash.com/s/photos/cloud?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的 [Pero Kalimero](https://unsplash.com/@pericakalimerica?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
如果您是 web 或软件开发人员、DevOps 工程师或数据科学家,您肯定听说过术语云原生。有时,当我们总是偶然发现一个术语时,我们不知何故会想到它的意思。类似于大纲的东西。**然而,我们真的知道这意味着什么吗?**
那么,到底什么是云原生工具和云原生应用。你能向一个五岁的孩子描述这些术语吗?因为,真的,如果你不能通过五岁的考验,你还没有内化这些术语,你的真理是片面的。
这个故事试图让我们了解以前黑暗的角落,并一劳永逸地澄清术语云原生。为此,我们不会遵循字典的方法,而是试图建立一种直觉。我们开始吧。
> [学习率](https://www.dimpo.me/newsletter?utm_source=medium&utm_medium=article&utm_campaign=cloud_native)是我每周给那些对 AI 和 MLOps 世界好奇的人发的简讯。你会在每周五收到我关于最新人工智能新闻、研究、回购和书籍的更新和想法。订阅[这里](https://www.dimpo.me/newsletter?utm_source=medium&utm_medium=article&utm_campaign=cloud_native)!
# 云原生
Cloud-native 封装了一组实践,组织可以使用公共、私有或混合云提供商来构建和管理大规模应用程序。但是我们承诺不遵循字典的方法。
> 使用云原生实践的组织旨在更快地交付新功能,并对变化或意外事件做出更快的响应。
所以,你应该问的第一个问题是,“这有什么意义?”让我们把它分解成对企业的意义和对开发者的意义。
从业务角度来看,使用云原生实践的组织旨在更快地交付新产品功能。此外,公司可以对任何变化或意外事件做出快速反应。**因此,这里的关键术语是速度和敏捷性。**
采用云原生战略的公司可以向客户交付更多价值,交付速度更快,并轻松扩展其产品的功能。同样,增长和服务可用性的提高也是云原生实践的诱人成果。该公司可以让客户满意,增加其客户群,并确保其产品全天候可用。
从技术角度来看,采用云原生思维意味着自动化、流程编排和可观察性。这些似乎本身就是流行语,我们再深入探讨一下。
自动化意味着开发人员可以更快地将新特性发布到产品中,而无需人工干预。编排意味着管理和配置成千上万的服务几乎不费吹灰之力。最后,可观察性意味着,在任何时候,工程师都可以获得系统状态的整体视图,同时还可以知道每个组件分别在做什么。
# 但是怎么做呢?
通常,术语原生云与容器和容器编排引擎密切相关。
容器很小,通常是不可变的单元,应用程序的一部分在里面运行;想一个微服。开发人员可以创建一个容器来封装一个简单的服务,快速部署它,轻松更新它,并在不再需要时销毁它。
但是设计这样的架构,其中应用程序被分解成许多微服务,产生了新的挑战。如何管理所有这些容器?我们如何发现可用的服务,如何配置它们,以及如何确保它们按照预期的方式工作?
许多项目被引入来解决这个问题。Docker Swarm 和 Apache Mesos 是几年前最引人注目的两个项目,但 Kubernetes 赢得了容器编排引擎之战,现在是事实上的标准。
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务。它采用了一种声明性的配置方法,在这种方法中,用户指定世界的期望状态,而不明确指定这应该如何表现。这种想法让我们可以灵活地运行分布式系统,并根据用户的需求进行扩展。
简而言之,Kubernetes 是一个容器协调器,帮助确保每个容器都在它应该在的地方,并且容器之间可以相互通信。如果你想知道为什么你应该关注 Kubernetes,请阅读下面的故事:
</kubernetes-101-why-should-you-care-6793a09a096a> [## Kubernetes 101:你为什么要关心?
towardsdatascience.com](/kubernetes-101-why-should-you-care-6793a09a096a)
随着时间的推移,几个 CNCF(云本地计算基金会)项目扩展了 Kubernetes 的功能。服务网格工具允许对集群内的流量进行粒度控制。日志记录和跟踪有助于调试每个事件。新的存储选项允许有状态应用程序无问题地运行:所有这些以及更多的东西使 Kubernetes 成为了增长最快的开源项目之一。
# 结论
如果您是 web 或软件开发人员、DevOps 工程师或数据科学家,您肯定听说过术语云原生,但它真正的含义是什么呢?
从业务角度来看,使用云原生实践的组织旨在更快地交付新功能,并对变化或意外事件做出更快的响应。
从技术角度来看,采用云原生实践的工程团队包含自动化、编排和可观察性,将应用程序分解为许多小型的容器化服务。
推动这一发展的关键技术是容器和容器运行时以及编排引擎,如 Kubernetes。
简单地说,容器很小,通常是封装了工作负载及其依赖关系的不可变单元。Kubernetes 是一个容器协调器,它帮助确保每个容器都在它应该在的地方,并且容器之间可以相互通信。
云原生应用和工具已经存在,所以让我们熟悉一下这些术语吧!
# 关于作者
我叫[迪米特里斯·波罗普洛斯](https://www.dimpo.me/?utm_source=medium&utm_medium=article&utm_campaign=cloud_native),我是一名为[阿里克托](https://www.arrikto.com/)工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请关注我的 [Medium](https://towardsdatascience.com/medium.com/@dpoulopoulos/follow) 、 [LinkedIn](https://www.linkedin.com/in/dpoulopoulos/) 或 Twitter 上的 [@james2pl](https://twitter.com/james2pl) 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
# 你一定知道 Python JSON 转储,但可能不是所有方面
> 原文:<https://towardsdatascience.com/you-must-know-python-json-dumps-but-maybe-not-all-aspects-fa8d98a76aa0?source=collection_archive---------7----------------------->

图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1213008) 的 [Lubos Houska](https://pixabay.com/users/luboshouska-198496/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1213008)
## 关于 Python JSON 模块的 dump(s)方法的一些技巧
Python 通过其“json”模块内置了对 JSON 文档的支持。我敢打赌,我们大多数人都用过,有些人用得很多。我们知道可以使用`json.dumps()`方法轻松地将 Python dictionary 对象转换成 JSON 字符串。然而,这种方法并不像大多数开发人员想象的那么简单。
在本文中,我将通过例子介绍这个`json.dumps()`方法。用法的复杂和稀有是由浅入深的。
现在我们应该开始了。不需要下载任何东西,你的 Python 3.x 必须自带 JSON 模块。
```
import json
```
## 目录
[1。漂亮输出的自动缩进](#61c8)
[2。自定义分隔符](#7e31)
[3。排序键](#e117)
[4。跳过非基本键类型](#94e3)
[5。非 ASCII 字符](#aede)
[6。循环检查](#e0a0)
[7。允楠](#2b4e)
[8(不是一个数)。定制的 JSON 编码器](#4a4d)
# 0.“转储”和“转储”的区别
万一有些新手在读这篇文章,大概有必要提一下,JSON 模块中有两个类似的方法——`dump()`和`dumps()`。
这两种方法有几乎相同的签名,但是`dump()`将转换后的 JSON 字符串写入一个流(通常是一个文件),而`dumps()`只将字典转换成 JSON 字符串。
假设我们有一个简单的字典如下。
```
my_dict = {
'name': 'Chris',
'age': 33
}
```
如果我们想将其转换成格式化的 JSON 字符串并写入文件,可以使用`dumps()`。
```
with open('my.json', 'w') as f:
json.dump(my_dict, f)
```

然后,如果我们检查工作目录,文件`my.json`应该在那里,包含转换后的 JSON 字符串。

如果我们使用`dumps()`,它只是将字典转储到一个字符串中。
```
json.dumps(my_dict)
```

由于这两个方法具有相同的签名,本文将只关注`dumps()`,因为所有这些技巧都适用于另一个方法。
# 1.漂亮输出的自动缩进
看到由`dumps()`方法输出字符串了吗?它不适合阅读。如果我们有一个非常大的 JSON 文档,所有内容都将在一行中输出。
为了以漂亮的格式输出 JSON 字符串,我们可以很容易地添加一个参数“indent”。它接受一个整数作为参数。
```
json.dumps(my_dict, indent=2)
# OR
json.dumps(my_dict, indent=4)
```

# 2.定制的分离器
JSON 遵循一种非常严格的格式,例如,同一级别的项目必须用逗号分隔,并且在键和值之间必须使用分号。因此,默认情况下,项目分隔符和键分隔符将是`,` 和`:` 。
然而,如果我们想输出一个紧凑的 JSON 字符串,我们可以通过参数`separators`改变这些分隔符。我们必须同时传递元组中的两个分隔符。
```
json.dumps(my_dict, separators=(',', ':'))
```

如果我们不必将 JSON 字符串用作 JSON,我们也可以将分隔符修改为我们想要的任何值。例如,我们可以让它变成 PHP 样式,如下所示。
```
json.dumps(
my_dict,
separators=('', ' => '),
indent=2
)
```

# 3.排序关键字
JSON 通常不关心项目的顺序。因此,当我们将 Python 字典转储到 JSON 字符串时,条目的顺序将保持不变。
```
json.dumps({
'c': 1,
'b': 2,
'a': 3
})
```

但是,如果我们确实想通过项目键对转换后的 JSON 字符串进行排序,我们可以很容易地将`sort_keys`参数设置为`True`。
```
json.dumps({
'c': 1,
'b': 2,
'a': 3
}, sort_keys=True)
```

该键将按字母顺序排序。
# 4.跳过非基本键类型
JSON 只支持几种类型的对象作为项目键,分别是`str`、`int`、`float`、`bool`和`None`。这些类型被称为基本类型。如果我们试图用非基本类型的键转换字典,就会抛出一个`TypeError`。
```
json.dumps({
'name': 'Chris',
(1,2): 'I am a tuple'
})
```

这是有意义的,因为 JSON 不支持集合类型作为键。然而,如果我们想跳过这些毫无意义的类型,我们可以将`skip_keys`设置为 true 来隐藏这些项目。
```
json.dumps({
'name': 'Chris',
(1,2): 'I am a list'
}, skipkeys=True)
```

# 5.非 ASCII 字符
非 ASCII 字符不能保证在所有平台上都能很好地显示,而且还可能在传输 JSON 字符串时造成麻烦。因此,在将它们转换成 JSON 字符串时,Python 会对其进行如下编码。
```
json.dumps({
'name': 'Chris',
'desc': 'There is a special char -> ¢'
})
```

但是,如果我们不想对这些特殊字符进行编码,我们可以将`ensure_ascii`设置为 **false** 。
```
json.dumps({
'name': 'Chris',
'desc': 'There is a special char -> ¢'
}, ensure_ascii=False)
```

# 6.循环检查
在 Python 中,可以用循环引用定义一个字典。举个例子,我们用一个名为“dictionary”的键定义一个字典,值暂时为`None`。
```
my_dict = {
'dictionary': None
}
```
然后,我们把字典本身赋值为键“dictionary”的值。
```
my_dict['dictionary'] = my_dict
```
现在,如果我们试图输出这个字典,Python 将显示`...`,因为有一个循环引用。

如果我们试图从这样的字典中转储 JSON 字符串,循环引用将被检测到并抛出一个错误。

但是,如果我们不想检测循环引用,就让它过去,我们可以将参数`check_circular`设置为 false。
```
json.dumps(my_dict, check_circular=False)
```

唯一的区别是,后者将真正尝试逐层转储循环引用的字典,直到它溢出。这样做也许没有什么好处。即使你想用这个特性来达到别的目的(我找不到例子……),也一定有比这更好的方法。
# 7.允许 NaN(非数字)
默认情况下,当转换带有无效数字(如`nan`、`inf`和`-inf`)的字典时,它会保持原样。
```
import numpy as npjson.dumps({
'my_number': np.nan
})
```

如果我们试图将“JSON 字符串”用于其他目的,这会导致一些问题,因为它不再是有效的 JSON 字符串。`NaN`不是有效的 JSON 值类型。
如果我们想避免这种情况,或者至少让问题早点暴露出来,我们可以将参数`allow_nan`设置为 **false** 。当有一个 NaN 时,将会抛出一个错误。
```
json.dumps({
'my_number': np.nan
}, allow_nan=False)
```

# 8.定制的 JSON 编码器
最后但同样重要的是,让我们看看如何定制 JSON 编码器。我们可以轻松地调整 JSON `dumps()`方法的行为,而不是让一切都默认。
例如,我们有如下这样的词典。它有一个 datetime 对象作为项目中的值。
```
from datetime import datetimemy_dict = {
'alarm_name': 'get up',
'alarm_time': datetime(2021, 12, 3, 7)
}
```
在这种情况下,如果我们试图将字典转储到 JSON 中,将会抛出一个类型错误。

如果我们想在转储字典时解析 datetime 对象怎么办?我们可以创建一个继承类`json.JSONEncoder`的子类,然后实现`default`方法。
```
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
return json.JSONEncoder.default(self, obj)
```
在上面的代码中,我们检查对象是否是日期时间对象。如果是这样,我们将日期时间转换成一个字符串,然后返回它。否则,就使用`JSONEncoder.default()`方法。
一旦我们得到了这个定制的编码器类,我们可以将它传递给`dumps`方法中的`cls`参数。它的行为将会改变。
```
json.dumps(my_dict, cls=DateTimeEncoder, indent=2)
```

# 摘要

图片由[亚历山大·巴甫洛夫·波德瓦尔尼](https://pixabay.com/users/visionpics-4638469/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6795381)从[皮克斯拜](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6795381)拍摄
在本文中,我只介绍了 Python 中的一种方法— `json.dumps()`。对于像这样的内置方法,我们通常会大量使用它们,但是我们可能不知道它的每一个方面。强烈建议调查我们熟悉的方法,而不是继续寻找那些很少使用的方法,因为前者可能会让我们花的时间更有价值。
<https://medium.com/@qiuyujx/membership>
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和其他成千上万的作者!(点击上面的链接)
# 您需要了解编译/解释、静态/动态和强/弱类型
> 原文:<https://towardsdatascience.com/you-need-to-know-compiled-interpreted-static-dynamic-and-strong-weak-typing-2e7f76c9dc3d?source=collection_archive---------23----------------------->
## 在这篇文章中,我将试图揭开编译/解释、静态/动态类型、强/弱类型等术语的神秘面纱,并定义一些术语,如运行时、编译等

Amy Hirschi 在 [Unsplash](https://unsplash.com/s/photos/keyboard?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
如果你上过一些编程课程,你可能会遇到下面的陈述,并有所理解
* Python 是强动态类型的
* JavaScript 是弱动态类型的
看完这篇文章,你就能清楚地理解上面的说法了。我们将会看到 Python、JavaScript 和 C++中的代码片段。如果你不知道语法或任何语言,没关系,你仍然能够理解底层的概念。
# 目录
* 一些定义和需要记住的要点
* 编译语言与解释语言
* 静态类型与动态类型
* 弱类型与强类型
* 编程语言分类
* 结论
# 一些定义和需要记住的要点
* 静态类型化/动态类型化中的“类型化”是指**数据类型**。它不是指按键的行为。我对此感到困惑,我确信可能还有其他人也有同样的遭遇
* 编程语言根据两种不同的属性分为静态/动态类型和强/弱类型,即静态/动态类型与强/弱类型无关
## 定义
* **源代码**——这是用 Python、JavaScript、C++等写的代码。又称**人类可读代码**
* **机器代码**——这是用机器语言(通常是二进制)编写的代码,可以被机器理解。
* **编译器-** 可以把源代码转换成机器码的计算机程序
* **编译**——将代码从 Python、JavaScript、C++转化为低级机器码。基本上将人类可读的代码转换成机器代码
假设编译成功,即源代码没有语法错误,我们继续运行。
* **运行时**——编译后,程序运行,代码执行。这段时间被称为运行时间。
* **执行**——实际运行/执行代码。这发生在源代码被转换成机器代码之后和运行期间
* **句法错误——**语言的句法(语法)错误。例如:在 C++中缺少分号,在 Python 中不正确的缩进,等等
* **运行时错误**—**运行时错误**是在程序执行期间发生的错误,例如:被 0 除,试图访问未定义的变量,对不兼容的数据类型使用运算符
* **类型转换**——将一个值从一种数据类型转换成另一种数据类型,例如:char 可以根据它的 ASCII 值转换成 int。类似地,数字 10 可以转换成字符串“10”。类型转换可以是**隐式**(由语言自动完成)或**显式**(您可以手动更改类型)
# 编译语言与解释语言
基于源代码如何被转换成机器代码,一种语言被分类为编译/解释的。
我将借用 [FreeCodeCamp](https://www.freecodecamp.org/news/compiled-versus-interpreted-languages/#:~:text=Interpreted%20vs%20Compiled%20Programming%20Languages:%20What's%20the%20Difference?,-Every%20program%20is&text=In%20a%20compiled%20language,%20the,reads%20and%20executes%20the%20code) 的这个类比。
基本上认为你有一个用德语写的食谱,需要你的朋友帮助你把食谱翻译成英语并照着做。有几种方法可以解决这个问题
* 你可以一个接一个地翻译每一步,也就是说,首先你的朋友翻译第一步,你跟着翻译。然后你进入第二步,你的朋友翻译它,你跟着做。诸如此类…..
* 你立刻翻译整个食谱,然后照着做,也就是说,你的朋友把整个食谱翻译成英语。现在,你一步一步跟着它走。
第一种方法类似于“解释”,而第二种方法类似于“编译”
## **解释语言**
程序的源代码被转换成机器代码并逐行执行。如果您定义了一个函数但从未调用它,则该函数不会被执行,因此,该函数中的任何运行时错误都不会导致错误。
让我们考虑一个 Python 中的例子

代码片段
尽管我们的函数被 0 除(运行时错误),但程序运行/执行成功。由于我们从不调用函数 **func,**该函数从不执行,因此运行时错误从不出现
让我们考虑一个类似的 JavaScript 例子

代码片段
像 Python 一样,JavaScript 也不会引发任何错误。由于我们从不调用函数,所以程序执行成功。
> 注意:如果您在上述代码片段中调用了该函数,将会导致错误
让我们考虑 Python 中的另一个例子

代码片段
这一次,我们在函数外用 0 除。这导致 Python 引发一个错误。但是,请注意,print 语句仍然被执行,它们的输出显示在终端中。这是因为每一行都是逐个执行的。因此,当我们执行第三个 print 语句时,Python 甚至还没有执行除以 0 的操作,因此,在此之前不会出现任何错误
## **编译语言**
整个源代码被转换成机器码。转换后,程序被执行。因此,无论函数是否被调用,函数中的任何运行时错误都会被引发。
让我们考虑一个 C++代码片段

代码片段
在函数 func 中,我们给变量 **var** 赋值 10,而没有定义变量(运行时错误)。尽管我们不调用该函数,但错误仍然会出现。“Hello World”不在终端中显示。
让我们考虑另一个片段

代码片段
与 Python 或 JavaScript 不同,“cout”语句不会被执行。首先,整个源代码被转换成机器码,并引发运行时错误。因此,“Hello World”不会显示在终端中。因为我们没有定义变量 **var** ,所以出现了错误
# 静态类型和动态类型
## 静态打字
> 在静态类型语言中,变量在其生命周期中具有固定的数据类型。
类型检查是在编译时完成的
考虑下面的 C++代码片段

代码片段
最初,我们将变量 **var1** 声明为一个 int,并给它赋一个整数值。但是后来,我们把它重新赋值为一个字符串。C++是一种静态类型语言,不支持这种行为。
> **例如:C,C++,Java,Rust,Go,Scala**
## 动态打字
> 在动态类型语言中,变量的数据类型可以在它的生命周期中改变
类型检查是在运行时完成的
让我们试着做和上面一样的事情,但是使用像 Python 这样的动态类型语言。

代码片段
初始变量 **var1 是一个整数变量。然而**,后来我们赋了一个字符串值,变量的数据类型改变了。像 Python 这样的动态类型语言允许我们改变变量的数据类型。
让我们在另一种动态类型语言 JavaScript 中做一些类似的事情。

代码片段
注意:如果你使用 **const** 关键字,你不能改变变量的数据类型。常量值不能被重新赋值,因此在某种程度上它们的数据类型是静态的。然而,Java 脚本是一种动态类型语言,因为变量的数据类型在其生命周期中会发生变化
> 例如:Perl,Ruby,Python,PHP,JavaScript
# 弱类型和强类型
## 弱类型化
> 弱类型语言支持不同数据类型之间的操作。这些语言支持隐式类型转换,一种数据类型转换成另一种数据类型。
让我们考虑一个 C++代码片段

代码片段
**a** 的 ascii 值为 **97** 。当我们尝试做 **10+a** 时,C++隐式地将 a 类型转换为它的 **ascii 值(97)** 。因此输出是 107。
让我们用 JavaScript 做一些类似的事情。

代码片段
JavaScript 将数字 **10** 转换为字符串 **"10"** 并将其连接为 **a**
> **例如:C/C++,JavaScript,PHP,Perl**
## 强力打字
> 强类型语言不支持不同数据类型之间的操作。他们可能支持也可能不支持类型转换。但是,它们不隐式地对数据进行类型转换。
让我们考虑 Python 中的一段代码。我们将尝试做和上面一样的事情。

代码片段
当我们试图将一个整数和一个字符串相加时,我们得到一个类型错误。这使得 Python 具有强类型。但是,请注意,可以将整数显式类型转换为字符串并执行操作。
> **例如:Python,C#,Java,Scala,Ruby**
# 编程语言分类

[https://medium . com/Android-news/magic-lies-here-static-typed-vs-dynamic-typed-languages-d 151 C7 f 95 e2b](https://medium.com/android-news/magic-lies-here-statically-typed-vs-dynamically-typed-languages-d151c7f95e2b)
# 结论
我希望我能够解释这些术语,以及一门语言是如何被确定为编译/解释、静态/动态、弱/强的。了解一门语言的这些特性将让你知道在开始学习这门语言时会遇到什么。如果您发现任何错误或知道任何好的类比来解释差异,请告诉我:)
> 在 [LinkedIn](https://www.linkedin.com/in/rahulbanerjee2699/) 、 [Twitter](https://twitter.com/rahulbanerjee99) 上与我联系
# 作为数据科学家或机器学习从业者,你需要学习这一技能
> 原文:<https://towardsdatascience.com/you-need-to-learn-this-one-skill-as-a-data-scientist-or-machine-learning-practitioner-e15291bfed70?source=collection_archive---------14----------------------->
## [入门](https://towardsdatascience.com/tagged/getting-started)
## 从持续学习和练习这一技能中获得的回报会将你的职业生涯推向新的高度

由[凯瑟琳·拉威利](https://unsplash.com/@cathrynlavery?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/writing-robot?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
数据科学作为一门学科和职业,要求其从业者具备各种技能,从沟通、领导力等软技能到演绎推理、算法思维、编程等硬技能。但是有一项关键技能是数据科学家应该掌握的,不管他们的经验如何,那就是 ***编写*** 。
即使是在量子计算或医疗保健研究等技术领域工作的数据科学家也需要写作。培养强大的写作能力需要时间,数据科学家面临的挑战可能会阻止他们轻松表达自己的想法。这就是为什么这篇文章包含了各种写作策略,并解释了它们如何有益于数据科学和[机器学习](https://www.nvidia.com/en-us/glossary/data-science/machine-learning/)专业人士。
# 1.简式写作
让我们从我们遇到的最典型的通俗易懂的写作风格开始。以简短的形式写作通常不费力,也不会占用太多时间。Twitter、LinkedIn、脸书、Quora 和 StackOverflow 上写的机器学习和数据科学内容都属于这一类。

*照片由* [*奥斯迪斯特*](https://unsplash.com/@austindistel?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上* [*下*](https://unsplash.com/s/photos/tweet?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
长篇内容,比如书籍、文章和论文,通常是最有价值的材料。所有这些都需要时间来写、读和分析。另一方面,社交媒体平台上的短格式内容可以提供信息,同时使用的精力和时间远远少于长格式内容。
目前,我们有幸见证人工智能先驱和知名机器学习实践者之间分享的话语和思想,而不必等待他们撰写和发表研究论文或论文。在社交媒体平台上写简短的帖子可以让你洞察那些不容易用语言表达的观点和看法,你的声音可以参与进来,观点可以被分享。
对于那些想通过社交媒体帖子与其他 ML 专家联系的人,我建议关注一些发布关于机器学习和数据科学的真实相关信息的人。花些时间阅读帖子上的讨论和贡献的基调,如果你有任何有价值的贡献,请说出来。
为了让你开始,这里有一个发布人工智能相关内容(以及其他有趣内容)的个人列表:[吴恩达](https://twitter.com/AndrewYNg),[杰弗里·辛顿](https://twitter.com/geoffreyhinton),[阿利,K·米勒](https://twitter.com/alliekmiller),[安德烈·卡帕西](https://twitter.com/karpathy),[杰瑞米·霍华德](https://twitter.com/jeremyphoward),[弗朗索瓦·乔莱](https://twitter.com/fchollet),[奥雷连·杰龙](https://twitter.com/aureliengeron),[莱克斯·弗里德曼](https://twitter.com/lexfridman)。还有很多人可以关注,但是这些人的内容会让你忙上一阵子。
# 问答平台
问题/答案作为一种写作形式,门槛最低,也不会消耗太多时间,取决于你回答所提问题的能力。
鉴于你的职业,我相信你听说过 StackOverflow,互联网上最受工程师欢迎的资源。说到在 StackOverflow 上提问,事情就没那么简单了;清晰和透明是必需的。恰当地编写查询是 StackOverflow 的一个非常重要的组成部分,他们已经出版了关于这个主题的综合指南。
这一部分的要点是:在 StackOverflow 上提问和回答问题有助于你在提出问题时变得简洁明了,在回答问题时也变得透彻。
# 2.电子邮件和消息

*照片由* [*马克西姆·伊利亚霍夫*](https://unsplash.com/@glvrdru?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上* [*下*](https://unsplash.com/s/photos/newsletter?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
撰写电子邮件和消息并不是机器学习的专利,但实践撰写有效消息艺术的数据科学家和机器学习实践者往往会在公司和团队中蓬勃发展,原因显而易见,其中一些原因是贡献、联络和完成工作的能力。
撰写好的信息和电子邮件可以让你获得一个新的职位,让你的项目得到资助,或者让你进入一个学术机构。Purvanshi Mehta 写了一篇文章,探讨了在 LinkedIn 上向个人发送冷消息以建立网络的有效方法。Purvanshi 的文章是一个循序渐进的指示,对采用冷信息礼仪。
# 3.博客和文章
许多专家认为,博客和文章在机器学习社区中有着独特的作用。文章是专业人员了解最新软件发布、学习新方法和交流思想的途径。
技术性和非技术性的 ML 文章是你会遇到的最常见的两类文章。技术文章由描述性文本和描述特定功能实现的代码片段或要点组成。非技术性文章包括更多的描述性语言和图片来说明想法和概念。
# 4.时事通讯

*图片由*[*cotton bro*](https://www.pexels.com/@cottonbro?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)*来自* [*Pexels*](https://www.pexels.com/photo/man-in-black-long-sleeve-shirt-sitting-at-table-4064828/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)
创办和维护一份时事通讯可能不适合数据科学家,但这种写作方式已经证明可以为那些愿意付出努力的人提供专业和财务优势。
对于 DS/ML 专业人员来说,时事通讯是提高在人工智能领域的认知度和影响力的重要战略手段。时事通讯的写作风格是没有定义的,所以你可以写你选择的任何方式。你可以开始一份正式的、冗长的、严肃的时事通讯,或者一份简短的、信息丰富的、有趣的。
从中吸取的教训是,创建一份简讯可以帮助你在你的领域、业务或组织中发展个人品牌。那些喜欢你所做的事情的人会继续消费和推广你的素材。
有一千个理由让你今天不应该开始时事通讯,但为了激发一些灵感,下面是一些你可以基于你的时事通讯的想法,我也包括了一些你应该订阅的人工智能时事通讯。
与人工智能相关的时事通讯创意:
* 一个 AI/ML 视频的集合,每个视频都有你的输入。
* AI/ML 文章合集阅读。
* 求职者可能感兴趣的你所在地区的招聘信息。
* 为对人工智能更实际的应用感兴趣的 ML 从业者提供最新的相关人工智能新闻。
记住,你的简讯的频率、长度和内容都是由你自己决定的。如果你觉得没有太多时间,你可以开始每月一期的时事通讯,或者每天一期的时事通讯,像机器一样生产内容。
订阅机器学习和数据科学时事通讯:
* [由](https://read.deeplearning.ai/the-batch/)[吴恩达](https://www.linkedin.com/in/andrewyng/)批
* [数据运球](https://www.kennethjee.com/newsletter)被[肯吉](https://www.linkedin.com/in/kenjee/)
* [奥雷利·艾迅](https://www.oreilly.com/emails/newsletters/)
* 丹尼尔·伯克的新闻简报
* [数据科学周刊](https://www.datascienceweekly.org/)
* [数据药剂](https://dataelixir.com/)
# 5.证明文件

*照片由* [*西格蒙德*](https://unsplash.com/@sigmund?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上* [*下*](https://unsplash.com/s/photos/documentation?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *。*
技术文档和非技术文档都是软件工程职业中常见的活动。数据科学家也不例外,解释软件代码或单个特性的文档是推荐的,并被认为是最佳实践。
什么时候一个项目是成功的?有些人可能会认为这是当你的模型在测试数据集上达到了可接受的精度?
有经验的数据科学家知道项目的成功受许多变量的影响,包括软件的可维护性、寿命和知识转移。软件文档是一项任务,它可以改善项目的前景,超越单个团队成员的能力。更不用说,它提供了软件质量和可维护性的额外层面。
数据科学家应该知道的文档的主要优点之一是它在减少新项目成员或数据分析师新手对源代码的查询方面的作用。大多数关于源代码的问题都与文件位置、编码标准和最佳实践有关。这些数据都可以记录一次,供许多人参考。
# 这里有一些你可以记录下来的想法
* 代码文档:为了保证跨应用程序的一致性,标准化实现风格和格式是至关重要的。这种一致性使得新开发人员更容易过渡到代码库,因为编码标准是通过代码文档给出的。
* **研究和分析:**鉴于软件产品特性的重要性,成功的开发总是依赖于彻底的研究和分析。任何在项目开始时工作过的 ML 专家都会处理来自涉众的过多的特性请求。围绕特性请求的文档化信息使项目中涉及的其他各方能够更直接地了解所提议的特性的需求和有用性。它还强制特征请求者进行更好的研究和分析。
* **数据库配置/应用程序信息:**记录特定于应用程序的信息,比如配置参数和环境变量,对于任何软件团队来说都是至关重要的,尤其是当你跳槽到一家新公司的时候。
* **How-tos:** 软件库和软件包的安装可能很困难,但事实是不同的操作系统甚至版本可能有不同的安装过程。在官方库文档中发现缺失的依赖项和安装程序必须经历的怪癖并不罕见。
* **API 文档**:当团队开发内部和外部 API(应用程序编程接口)时,他们应该记录那些 API 所需的方法、函数和数据资源的组件。没有什么比使用非文档化的 API 更烦人的了;整个过程变成了一个猜谜游戏,您将花时间研究一个未记录的 API 的参数、内部工作和输出。在使用你的技术资源时,通过创造一个流畅的体验来节省你的团队和客户的时间。
毫无疑问,大量的资源允许组织处理多种类型的文档,有些甚至雇佣技术作家。虽然这些都是可行的选择,但对于希望认真对待软件完整性的机器学习专家来说,练习记录开发的程序和软件以推广他们可以提供全面解释的想法是至关重要的。
在谷歌上快速搜索“ [*如何编写好的软件文档*](https://www.google.com/search?q=how+to+write+good+software+documentation&sxsrf=AOaemvJr0c0YU8mz6TDXZj3U6UZMzCzr8g:1633532008928&source=hp&ei=aLhdYfa_NYnQgwfw1oT4Aw&iflsig=ALs-wAMAAAAAYV3GeHfX0i3zL5GkEow8R6Wjw0_R8yC3&ved=0ahUKEwj2k_n4hLbzAhUJ6OAKHXArAT8Q4dUDCAk&uact=5&oq=how+to+write+good+software+documentation&gs_lcp=Cgdnd3Mtd2l6EAMyBAgjECcyBggAEBYQHjIGCAAQFhAeMgYIABAWEB4yBggAEBYQHjIGCAAQFhAeUPgHWPgHYIUSaABwAHgAgAGeAYgBngGSAQMwLjGYAQCgAQKgAQE&sclient=gws-wiz&pccc=1) ”可以找到[的好资源](https://technicalwriterhq.com/how-to-write-software-documentation/),它们都分享了相同的信息和关于文档的最佳实践。
# 6.研究论文

*照片由*[*Ron Lach*](https://www.pexels.com/@ron-lach?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)*来自*[*Pexels*](https://www.pexels.com/photo/woman-checking-the-script-written-on-the-paper-8085944/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)*。*
2020 年,我发表了一篇关于[如何阅读研究论文](/how-you-should-read-research-papers-according-to-andrew-ng-stanford-deep-learning-lectures-98ecbd3ccfb3)的文章,引起了巨大轰动。当谈到利用 ML 算法和模型时,我们必须优化我们阅读这些论文的方式,就像经验丰富的机器学习专家一样。
写机器学习研究论文是硬币的另一面。我从来没有写过研究论文,现在也不打算开始写。然而,一些机器学习专业非常关注写作和发表研究报告。作为职业成功的衡量标准,研究机构和公司使用个人或团体发表的论文数量。
写研究论文是一门艺术;研究人员和科学家必须考虑数据的结构和内容,以确保信息、突破或想法得到有效传递。我们大多数人可能不会很快写研究论文,但是采用写好研究论文的实践是有价值的。例如,有一个摘要、引言和结论是一个可以转移到其他写作部分的写作结构。
继续阅读一些研究论文;注意作者使用的语言、结构和视觉图像的使用。尝试并采纳你在下一篇文章中发现的任何好的实践。
# 7.书籍和电子书

*照片由* [*刻痕纹*](https://unsplash.com/@jannerboy62?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上的 Unsplash*
毫无疑问,ML/DS 书籍是关于机器学习理论和实践经验的最权威的书籍。我并不是建议所有的数据科学家和 ML 工程师都应该写一本书。但是请原谅我。
我翻阅了我书架上几位用 AI/ML 写书的作者,他们都在各自的领域有着丰富的经验。
写关于机器学习的非虚构的、技术性的书籍是非常困难的。它需要高水平的理论和实践行业知识,只有通过完全沉浸在学习、研究和实施中才能获得。要培养数百名 ML 工程师和数据科学家,你的声誉必须建立在坚实的学术、商业或研究证书之上。更不用说作家在交付写得好的书时,是需要创造力的。更具体地说,他们必须掌握在书中传达复杂主题的艺术。
我的论点是,要创作一部永恒的机器学习书籍,你必须沿着专业知识的道路走下去。这听起来并不诱人,但我希望你考虑这样一个事实,即设定写书的长期目标将推动你更深入地研究机器智能或所选领域的主题,这将增强你对人工智能的总体理解。
面向数据科学家和机器学习从业者的书籍:
* 尼克·博斯特罗姆的《超级智能》
* [陈秋帆李开复《AI 2041》](https://books.google.co.uk/books/about/AI_2041.html?id=tUASEAAAQBAJ&source=kp_book_description&redir_esc=y)
* [aurélien géRon 利用 Scikit-Learn、Keras 和 TensorFlow 实践机器学习](https://learning.oreilly.com/library/view/hands-on-machine-learning/9781492032632/)
* 人工智能:彼得·诺维格的现代方法
你会发现,前面列出的大多数作者创作了本文列出的大多数(如果不是所有)写作形式,而不管他们的领域专业,因此我认为*写作*是机器学习从业者和数据科学家需要掌握的一项重要技能。
# 结论
每当我被问及什么样的人生决定给我带来了最大的好处,无论是经济上的、学术上的还是职业上的,我通常会用我决定写作来回答。
在这篇文章中,你已经看到了数据科学家和机器学习专家如果定期编写人工智能相关材料可能会获得的几个优势。这一节集中了本文中列出的所有好处,以确保它们都能切中要害。
* ML 专业人员使用写作以简单的方式交流复杂的主题。通过阅读 Andrej Karpathy 写得很好的[博客,我能够对卷积神经网络的实际应用获得更大的赞赏。](http://karpathy.github.io/2015/10/25/selfie/)
* 不同类型的写作可以帮助你提高创造力和批判性思维。我最近读了李开复和陈秋凡的《人工智能 2041》,作者通过精心编写的虚构故事和对人工智能技术的透彻解释,研究了人工智能技术及其对人类生活的影响。两位作家都写了很多年的书,也写过其他的书。可以合理地得出结论,他们的写作能力允许作者表达涉及人工智能技术的未来情况,并通过基于当前人工智能发展的批判性和逻辑预测来探索人工智能集成的未知社会影响。
* 以讲故事的形式写作赋予项目生命。好故事是讲出来的,伟大的故事是写出来的。当与讲故事的艺术相结合时,向客户、投资者或项目经理等利益相关者复述机器学习项目会产生积极而令人兴奋的变化。一位数据科学家向利益相关者解释为什么联邦医院应该利用一种新的最先进的癌症检测深度学习模型,当结合患者早期诊断的故事时,这种模型变得更有影响力和相关性。
* 在机器学习社区中,写作是一种成功的知识转移方法。你在 DS/ML 世界中获得的大部分信息将通过书面内容获得。文章、散文和研究论文都是多年知识的宝库,它们被组织成简洁的章节,有清晰的解释和易于理解的格式。写作是浓缩多年知识和经验的有效方法。
你知道我们钦佩和学习的人工智能先驱和专家也定期发表文章吗?在这篇[文章](https://docs.google.com/document/d/1WJP7hFiaP9L5r97dVK5j8y6XAjJypZfKh5FqMM2v7Dc/edit#heading=h.hevat0uwelz7)中,我编制了一份人工智能领域个人的候选名单,并提供了他们工作的样本,强调了他们工作的价值和后果。
感谢阅读。
**本文最初发表于** [**Nvidia 开发者博客**](https://developer.nvidia.com/blog/an-important-skill-for-data-scientists-and-machine-learning-practitioners/)
# 我希望这篇文章对你有所帮助。
要联系我或找到更多类似本文的内容,请执行以下操作:
1. [**成为推荐媒介会员,支持我的写作**](https://richmondalake.medium.com/membership)
2. 订阅我的 [**电子邮件列表**](https://richmond-alake.ck.page/c8e63294ee) 获取我的时事通讯
3. 通过 [**LinkedIn**](https://www.linkedin.com/in/richmondalake/) 联系我
# 你需要停止阅读关于成为数据科学家的耸人听闻的文章
> 原文:<https://towardsdatascience.com/you-need-to-stop-reading-sensationalist-articles-about-becoming-a-data-scientist-ee3cb392833f?source=collection_archive---------5----------------------->
## 阅读这些文章如何对有抱负的数据科学家不利。

安德里·甘泽维奇在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
当我刚开始学习数据科学时,我会阅读我能找到的每一篇关于那些做了我试图做的事情的人的文章:成为一名自学成才的数据科学家。
这些文章充满了没有计算机科学学位的人的细节,他们不顾一切,在荒谬的几个月内自学成为数据科学家。自然,这些人实际上也是被 FAANG 公司当场雇用的。
带有耸人听闻标题的文章,如“我如何在 6 个月内成为一名数据科学家”或“我如何在全职工作的同时学习数据科学 1 年后成为一名谷歌数据科学家”,描述了作者如何在没有太多经验的情况下“挤”和“爬”到数据科学职位。
这些文章向读者承诺,如果他们也“忙碌”,他们可以实现他们最疯狂的梦想,成为一名获得职业满足感和六位数薪水的数据科学家,所有这些都在可笑的短时间内完成。
作为一个已经接受过软件开发和大学水平数学教育的人(或者说是对成为数据科学家的一些要素有一些经验的人),我看到这些文章有几个非常明显的问题,这些文章贴满了希望和 hustle 的魅力,作为一种获得观点和激励人们的方式,他们也可以在不到一年的时间里成为数据科学家,而无需事先培训或经验。
# 优势就是一切。
这些成功案例通常与您的一般数据科学学习经历不同,这是一种优势。
那些自学数据科学并在六个月内被聘用的人很可能拥有许多优势来实现这一目标。
每天学习八小时、接受大学教育、拥有电脑、负担得起 MOOCs 和训练营,以及生活在科技中心,这些都是这些人很少提及的优势。这些人描绘了一幅忙碌而艰辛的画面,但很少描述他们拥有的优势,这些优势也帮助了他们一路走来。
虽然我不是说这些人有不公平的优势,但我觉得这些优势会帮助任何人在不到一年的时间里学习数据科学。
还应该说,物质优势并不是帮助人在短时间内成为数据科学家的唯一因素。
能够很好地进行面试,具有某个行业的背景知识,以及能够快速理解抽象概念,这些都是人们在实践数据科学的第一年内能够被 FAANG 公司聘用的方法。
这样做的目的是不要因为这些人的优势而贬低他们的成就。仅仅因为他们有优势,并不意味着他们没有为他们所取得的成就或获得的机会而努力。
重点是强调某些人如何拥有物质和非物质优势,使他们有可能在短时间内实现如此崇高的目标。
重点是强调你应该如何对这些耸人听闻的文章持保留态度,理解你的情况可能与作者的情况不同。这将改变你追求数据科学学习和职业的方式,并可能改变你与其他人相比的道路。
换句话说,不要因为你无法匹配某个在学习数据科学概念后六个月内被聘用的人的时间表而责备自己。
# 仅仅因为它对一个人有效并不意味着它对每个人都有效。
每天都有大量关于如何学习数据科学的新文章发表。许多作者承诺提供完美的课程,帮助任何人在短时间内学习数据科学。
然而,很少有一篇文章概述了大众学习的完美方式。大多数文章概述了对作者有效的方法,以及他们认为对其他人有效的方法。
这里的要点是不要阻止作者根据他们的观点和个人经验撰写关于如何最好地学习数据科学的文章。这些文章是信息宝库,是有抱负的数据科学家的良好起点。
然而,这些文章应该是这样的——起点。
有抱负的数据科学家应该对这些文章持保留态度。仅仅因为一种方法对一个人有效并不意味着它对所有人都有效。
至关重要的是,在撰写这些文章时,要考虑到一些作者在编码、数学和数据分析方面已有的不同技能水平。
例如,当我写我学习数据科学的个人计划时,我没有花太多时间列出基本的编码课程,因为我已经有了软件开发的背景。这意味着我可以直接进入更具挑战性的概念,比如将 Python 用于数据结构和科学计算。没有编程经验的人阅读这篇文章时,如果没有足够的基础编程课程,可能会尝试学习相同的课程,但失败了。
跟随这些课程会很快成为令人沮丧的经历,因为你期望的结果并不总是实现。
所以,善待自己。仅仅因为某个特定的方法或课程对其他人有效,并不意味着它对你也有效。与你学习的最佳方式和你已经拥有的技能相协调。从每门课程中挑选最适合你的部分,用它们来指导你的学习之旅。
否则,看了这些文章还指望得到同样的结果,不比瞎子领瞎子强多少。
# 在不到一年的时间里学会成为一名数据科学家应该被认为是一个奇迹。
一年不是一段很长的时间。
不管别人怎么说,在一年内完成任何事情都是令人印象深刻的,几乎是不可能的。尤其是在成为数据科学家的时候。
将自己成为数据科学家的最后期限设定为六个月,甚至一年,这可能会让你发现精通任何事情都需要多长时间。
当我在大学学习软件开发时,除了摆弄 HTML 和 CSS 之外,没有任何编程经验,我花了整整一个学期(四个月)学习 C#编程基础。整整四个月。
如果你的目标是在六个月甚至一年内成为一名数据科学家,当你开始学习一些你以前没有经验的东西时,这四个月会很快被消耗掉。令人惊讶的是,你需要多长时间才能掌握抽象概念,并教会你的大脑以不同的方式思考问题。
随着时间一个月一个月地过去,你的最后期限突然被掩盖了,你没有完成别人在半年内能够完成的事情会让人感到沮丧。
虽然最后期限是设定目标的重要部分,但了解你的学习道路不会和别人的一样也很重要。如上所述,通常是那些具有特殊优势的人,例如每天学习八小时的能力,或者已经拥有大学学位的人,可以在短时间内成为数据科学家。
当你想到这一点时,你会发现一般的开发人员都拥有软件工程或计算机科学的学位,而他们花了*四年*才完成。此外,一般的数据科学家都有硕士学位,这可能需要*额外的两到四年*才能完成。
在我看来,普通人要想在数据科学领域找到一份工作,可能需要*至少*一年的学习时间。这就意味着与普通大学毕业生相比,你要以超快的速度工作。因此,重要的是要记住,为自己设定几乎不可能的最后期限,实际上你可能会阻碍你的进步,甚至会恶化(也是最重要的)你的心理健康。
问任何一位数据科学家,他们都会肯定地说学习数据科学很难。不要让自己更难过。不要只关注目的地,学会享受旅程,学会对每天学到的每一条信息心怀感激。随着时间的推移,这些信息将转变为数据科学家的职业生涯。
# 最后的想法。
阅读关于成为数据科学家的耸人听闻的文章通常会遵循一条轨迹,这条轨迹始于灵感,终于沮丧。
一开始,当你听说每个成功人士都实现了改善生活和成为数据科学家的梦想后,你会觉得自己可以做任何事情。
然而,经过一段时间的学习,这种灵感的感觉会慢慢退化为沮丧和气馁的感觉,因为你会意识到这个过程有多艰难。此时,最好停止阅读这些文章,开始专注于自己的道路。为什么?
因为如上所述,成功人士撰写的文章概述了他们拥有优势并在短时间内取得成功的历程。这些文章没有概述如何最好地学习数据科学。
这并不是说作者应该停止写这些文章。这些文章对于激励下一代数据科学家非常重要。如果没有这些文章,我认为很少有人愿意尝试数据科学。
相反,这是说,读者应该半信半疑地看待这些文章,并阅读它们的真实面目:只讨论一个人经历的个人成功故事。你很少会读到那些分享学习数据科学失败故事的人,或者那些花了数年时间才实现目标的人。
因此,如果你的道路和别人的不匹配,不要担心。你可能需要六个月,甚至几年。唯一重要的是你坚持到底,并考虑分享你的旅程。因为信不信由你,你的故事将有助于激励某人去尝试你一度认为不可能的事情:成为一名数据科学家。
# 您可能不需要 for 循环
> 原文:<https://towardsdatascience.com/you-probably-do-not-need-that-for-loop-6de79dffbaf?source=collection_archive---------7----------------------->
## 声明性思维如何帮助您编写更好的数据科学代码
每当我在一段数据科学 Python 代码中看到 for 循环,我的第一反应是“那可能不需要”。然而,for 循环只是更传统的软件工程命令式方法和本质上更具声明性的数据科学方法之间更深刻的哲学差异的一个例子。本质上,在命令式编程中,重点是告诉计算机*如何*执行任务,而在声明式编程中,我们只是简单地陈述*我们想要什么*,计算机应该负责任务如何执行。这通常会产生更短更快的代码。
本文的目标是让您意识到这种编码风格的差异。特别是对于从 C#或 C++等更主流编程过渡到数据科学的人来说,进入声明性思维模式非常重要。

伊利亚·巴甫洛夫在 [Unsplash](https://unsplash.com/s/photos/coding?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
# 善意的虹膜
我们第一个例子的目标是计算数据帧中一列的平均值。在考虑这两种方法之前,请先尝试自己解决问题。
作为基础,我们使用 iris 数据集,并希望计算`sepal_length`列的平均值:

我们首先从命令式方法开始:我们迭代`sepal_length`列中的数字,将它们全部相加,跟踪长度,最后通过将总和除以列的长度来计算平均值:
```
5.843333333333335
```
或者,这是声明式方法:
```
5.843333333333335
```
因此,在命令式解决方案中,我们花费了大量代码来告诉计算机做什么。在声明式方法中,我们简单地声明我们想要得到特定列的平均值。这很好地说明了代码比命令式方法要短得多,并且在更高的抽象层次上运行。
# 每个物种的最小-最大值
下一个例子有点复杂。我们希望计算每种独特虹膜类型的每个测量变量的最小值和最大值。因此,`sepal_length`的最小值适用于三种类型的虹膜,同样适用于`sepal_width`等。在看我的答案之前,你可以自己尝试一下。
我们首先从纯粹的命令式方法开始。请注意,我故意省略了 smarter Python 和 Pandas 的任何语法,只是为了清楚地说明您需要多少代码来完成这项工作:
```
{'sepal_length': {'setosa': 5.0, 'versicolor': 5.7, 'virginica': 5.9},
'sepal_width': {'setosa': 3.3, 'versicolor': 2.8, 'virginica': 3.0},
'petal_length': {'setosa': 1.4, 'versicolor': 4.1, 'virginica': 5.1},
'petal_width': {'setosa': 0.2, 'versicolor': 1.3, 'virginica': 1.8}}{'sepal_length': {'setosa': 5.0, 'versicolor': 5.7, 'virginica': 5.9},
'sepal_width': {'setosa': 3.3, 'versicolor': 2.8, 'virginica': 3.0},
'petal_length': {'setosa': 1.4, 'versicolor': 4.1, 'virginica': 5.1},
'petal_width': {'setosa': 0.2, 'versicolor': 1.3, 'virginica': 1.8}}
```
声明式方法的美妙之处在于它几乎直接来自问题陈述:

这个代码是:
* 短得多:3 行对 34 行。
* 比命令式代码更加通用。比如除了`min`和`max`再加`median`就简单很多了。
* 对于更大的数据集,速度要快得多。
# 最终声明
真正进入陈述性思维需要一些时间。特别是对于那些已经精通其他命令式语言的人来说,这可能很难做到。一个很好的练习是强迫自己使用内置的熊猫解决方案来解决问题。如果你觉得自己要去显式循环,回到绘图板。因为循环并不总是错误的答案,但是在开始的时候,我宁愿谨慎。
这篇文章也可以在 github 上阅读[,包括所有代码。](https://github.com/PaulHiemstra/declarative_programming_article/blob/main/declarative_programming_article.ipynb)
# 我是谁?
我叫 Paul Hiemstra,是荷兰的一名教师和数据科学家。我是科学家和软件工程师的混合体,对与数据科学相关的一切都有广泛的兴趣。你可以在 medium 上关注我,或者在 LinkedIn 上关注我。
如果你喜欢这篇文章,你可能也会喜欢我的其他一些文章:
* [掌握数据科学并不是学习一系列技巧](/mastering-data-science-is-not-learning-a-series-of-tricks-df66d8529c29)
* [学习 AI 机器人玩井字游戏系列文章](https://towardsdatascience.com/tagged/rl-series-paul)
* [牛郎星图解构:可视化气象数据的关联结构](/altair-plot-deconstruction-visualizing-the-correlation-structure-of-weather-data-38fb5668c5b1)
* [面向数据科学的高级函数式编程:使用函数运算符构建代码架构](/advanced-functional-programming-for-data-science-building-code-architectures-with-function-dd989cc3b0da)
* [通过规范化扩展您的回归曲目](/expanding-your-regression-repertoire-with-regularisation-903d2c9f7b28)
# 您应该为这些数据科学障碍做好准备
> 原文:<https://towardsdatascience.com/you-should-be-prepared-for-these-data-science-obstacles-b8fd73873d01?source=collection_archive---------27----------------------->
## 意见
## 这里有 4 个你可能会遇到的障碍…

[劳尔·纳杰拉](https://unsplash.com/@reinf?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/obstacle?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)【1】上的照片。
# 目录
1. 介绍
2. 缺失数据
3. 利益相关者问题陈述
4. 沟通有效性
5. 工程生产
6. 摘要
7. 参考
# 介绍
数据科学角色伴随着各种挑战,也就是说,我将讨论我经历最多的挑战,同时强调它们的重要性、影响和可能的解决方案。其中一些障碍也适用于其他角色,如数据工程、机器学习工程、软件开发和数据分析。习惯数据科学中的某个过程可能很容易,因为你可能会遇到一个路障,这肯定会让你迷失方向。这就是为什么我将讨论我的四大数据科学障碍,从数据质量、商业敏锐度到生产就绪代码,所以希望你能了解这些可能出现的问题,并在它们发生之前做好准备。
# 缺失数据

照片由[泰勒·卡拉汉](https://unsplash.com/@tylercallahan?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/empty?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)【2】上拍摄。
数据科学家可能遇到的最常见的障碍之一包括处理丢失的数据。通常,当你在学习数据科学时——就像在大学或在线课程中一样(*有时是*),并且更加关注各种各样的机器学习算法时,你可以跳过处理数据集中缺失数据的部分,以便首先在高层次上研究算法。如果您刚刚开始学习数据科学,您可以练习使用缺失数据的方法之一是创建一个模拟数据集,在不同的列中包含不同的缺失数据,可以是自变量,也可以是因变量。然后,您可以测试各种解决方案以及对您的模型产生最佳影响的解决方案组合。
有几种方法可以解决丢失数据的问题,无论是简单地删除数据集中的 NA(*null-missing*)值,还是以某种方式输入它们的值。这最终取决于你的数据集,丢失了多少,以及你试图解决什么问题。如果您看到太多缺失数据,这可能意味着您可以找到一个更好的数据源,或者收集更多只包含一小部分缺失数据的要素,而不是输入缺失值。
> 以下是处理缺失数据的一些方法:
* 用该特征的最小值填充 NA 连续值
* 用该特征的最大值填充 NA 连续值
* 用该特征的平均值填充 NA 连续值
* 删除丢失数据的行
* 用空格填充 NA 分类变量
* 预测缺失值
* 添加新的重要功能,减少数据丢失
如您所见,处理缺失值的方法数不胜数,因此最好在开始数据科学工作之前练习这些技能,但您现在也可以作为一名数据科学家学习和应用这些技能,方法是更详细地研究各种插补方法。要真正知道使用哪种方法,您必须在试错法中应用这些解决方案,同时还要了解您的更改对您的整体模型和预测的影响。
# 利益相关者问题陈述

由[活动创作者](https://unsplash.com/@campaign_creators?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/business?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)【3】上拍摄的照片。
作为一名数据科学家,你将不得不与其他利益相关者合作,有时是技术性的,有时不是。得出一个简单的问题陈述并最终由数据科学模型解决可能会很复杂。你必须清楚,如果你是首先提出问题陈述的人,而利益相关者是给你分配问题的解决方案,那么你必须确保你们两人在根本问题上意见一致。
> 一个糟糕的问题陈述的例子是:
* 使用机器学习来解决为什么我们不能手动完成这个过程,也许是回归
> 一个好的问题陈述的例子是:
* 手动对产品进行分类既耗时又不准确
那么是什么让第一种说法如此糟糕呢?就是因为它试图先解决问题,没有 ***突出*** 只解决问题。有时候,作为一名数据科学家,您可以创建一些更手工制作的算法,这些算法可以更快地创建手动流程,但向数据科学家建议回归可以让他们在向外思考时首先关注这一点,并且首先关注更高的级别,这是一种更好的方式,这样您就不会立即将自己局限于特定的解决方案,特别是当它可能是不正确的解决方案时。
第二个例子很好,因为它简单明了。它涵盖了目前的行动,这是分类产品,它说,为什么这是目前的一个问题-需要太长时间和不准确的。现在,作为一名数据科学家,你可以很快想到一个分类算法,例如,作为解决这两个问题的直接方法。
# 沟通有效性

照片由[迪伦·吉利斯](https://unsplash.com/@dylandgillis?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/communication?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)【4】上拍摄。
与上面的障碍类似,你与其他利益相关者和同事沟通的总体效果也是如此。很有可能你会成为你们公司唯一的数据科学家之一,或者可能是唯一的一个。因此,你必须能够以非技术的方式解释你所做的几乎所有事情,或者只是以其他人可以理解的方式解释,而不仅仅是关于统计、机器学习以及只有数据科学家通常会知道的更具体的细节。
> 您会遇到沟通障碍的时候:
* 与风险承担者沟通问题陈述
* 将你的代码分解给软件工程师、数据工程师,或者机器学习工程师(稍后会有更多关于这个话题的*)*
* *允许他人解读你的结果*
*以上是你在交流中会遇到的一些障碍。如果你想成为一名真正成功的跨职能数据科学家,你不仅需要成为公司其他数据科学家的沟通大师,还需要成为产品经理和工程师的沟通大师。*
# *工程生产*
**
*克里斯托弗·高尔(Christopher Gower)在[Unsplash](https://unsplash.com/s/photos/code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)【5】上拍摄的照片。*
*这个问题更多的只是与数据科学家和他们一起工作的软件工程师、数据工程师或机器学习工程师之间的关系有关。大多数数据科学家不负责整个端到端解决方案,即使模型是主要部分(*是的,一些小公司的数据科学家——通常,有时是*)。也就是说,你不仅要在本地测试你的模型,还要在生产中测试。帮助您的工程师将与您一起工作,使您的代码更加模块化、可伸缩,并帮助解决生产中可能出现的错误——质量保证——或测试,这就是人们经常提到的情况。有时你可以在本地获得很好的结果,但之后你会发现在生产过程中效果并不好——所以这取决于你和你更亲密的同事一起工作,这样在生产日就不会有惊喜了。*
> *以下是将本地代码和开发代码转换为生产就绪代码时可能出现的一些问题:*
* *你的代码不是面向对象的编程( *OOP* )格式*
* *你没有考虑到最终可能发生的错误*
* *本地库的版本不能在生产环境中执行,否则需要在生产环境中定期更新*
* *部署过程一次只能获取一定数量的训练数据,而不是全部本地数据*
* *你正在使用的生产平台可能会引起一些新的问题( *Docker 等)。*)*
*如您所见,在数据科学过程的某些部分,将本地代码转换为生产就绪代码以及在生产就绪环境中可能会出错。*
# *摘要*
*我讨论了我在数据科学职业生涯中遇到的最大障碍。我敢打赌,其他人也有类似的经历,所以希望你能在这些事情发生之前了解到,这样你就能为即将到来的常见问题做好准备。我们已经概述了诸如缺失数据、利益相关者问题陈述、沟通有效性和生产中的工程等障碍。也就是说,如果你从事数据分析或软件工程等其他工作,其中一些也适用于你。*
> *这里再次总结了所有的障碍:*
```
*Missing DataStakeholder Problem StatementCommunication EffectivenessEngineering Production*
```
*我希望你觉得我的文章既有趣又有用。如果你自己经历过这些障碍,请在下面随意评论。你认为现在了解这些对你的数据科学职业有帮助吗?你同意还是不同意我的路障,为什么?*
*请随时查看我的个人资料和其他文章,也可以通过 LinkedIn 联系我。*
> *下面是我写的一篇类似的文章,关于掌握 Python 作为数据科学的先决条件[6]:*
*</you-should-master-python-first-before-becoming-a-data-scientist-8ac1b1b4e10f>
# 参考
[1]由[劳尔·纳杰拉](https://unsplash.com/@reinf?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/obstacle?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片,(2017)
[2]照片由[泰勒·卡拉汉](https://unsplash.com/@tylercallahan?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/empty?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)(2018)上拍摄
[3]2018 年[活动创建者](https://unsplash.com/@campaign_creators?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/business?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
[4]照片由[迪伦·吉利斯](https://unsplash.com/@dylandgillis?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在[Unsplash](https://unsplash.com/s/photos/communication?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)(2018)上拍摄
[5]Christopher Gower[在](https://unsplash.com/@cgower?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) [Unsplash](https://unsplash.com/s/photos/code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片,(2017)
[6] M.Przybyla,Y [在成为数据科学家之前,你应该首先掌握 Python](/you-should-master-python-first-before-becoming-a-data-scientist-8ac1b1b4e10f),(2021)*


浙公网安备 33010602011771号