从机械工程师过渡到机器学习工程师(或数据科学家)
我自己从一个物理机器的世界到另一个充满数字机器的世界的旅程故事。
图片提供:【https://www.core77.com
我有机械工程(ME)背景,因为我所有的学位都在我这里。在我获得学士学位后,当数据革命成形时,我正在机器人领域接受高等教育。当时人们更熟悉“大数据”这个词,而不是数据科学。然后,我迷上了机器学习,并从那时起开始将我的职业道路转向数据科学。我有一个机器人背景的良好开端,特别是在编程方面,所以我不必从零开始。尽管如此,我在尝试进入数据科学领域时遇到了许多障碍。
当时我很困惑,因为我不知道从哪里开始,我需要提高什么技能,我应该如何形成我的简历,等等。如果你正在阅读这篇文章,并且有类似的自我背景,我希望你会发现这很有帮助。
数据科学真的比机械工程好吗?
在我们决定为在线课程或其他不可逆转的行为投入时间和金钱之前,花点时间认真思考一下自己的动机可能是件好事。如果仅仅是因为这份 21 世纪最性感的工作,很有可能当热情消退后,你会在浪费投资和迷失在航行中后留下困惑和沮丧。
在我看来,任何两个工作之间的比较都是无关紧要的,因为任何领域都有其利弊。然而,作为一个在这两个领域都呆过一段时间的人,我想指出这两个领域的一些特点,仅仅反映我个人的观点。
机械工程:
- 医用电气技术往往在行业内长期有效。例如,卡尔曼滤波器、PID 控制等。那些现在都在用,可能比我还老。这样,你就不必在你的学术教育之后经常学习和更新自己,至少在理论知识方面。(持续学习对于保持领先地位并不重要。)
- 有限的工具集:有机器控制、硬件设计等标准工具的基准。同样,这又回到了第一点,你不必经常更新。
- 并不总是要求高等教育学位。这是我的主观观察。这是因为这样一个事实,为了正常工作,并被认为是一个高技能的机械工程师,一个人需要一个长期的工作经验,而不是在一个特定的 ME 浓度高等教育学位。通常情况下,我发现硕士学位足以让一个人在整个职业生涯中追求我。
- 有点无聊,因为大多数问题都有很好的定义和经过测试的解决方案。这项工作的主要部分通常是选择正确的解决方案和工具。此外,机器是一致的和可预测的,因此一次有效的解决方案往往会在很长一段时间内有效。
数据科学具有几乎完全相反的特征。
数据科学:
- 技术日新月异。此外,你不仅要应对 AI/ML 的变化,还要应对更广泛的软件工程领域,尤其是当你选择成为一名 ML 工程师的时候。
- 明面上是广阔的。我几乎从来没有发现任何 ML 应用程序仅仅通过一个设计就可行。你可能会说“好吧,这难道不是一件好事吗,我只需要找到至少一种做事的方法,并且坚持不懈地做下去”。不完全是。你不仅要成为这个领域的主管,你还得成为兼容。您选择的工具可能无法与团队的其他成员一起工作。
- 高等教育学位往往是必备的,至少能帮你通过简历筛选。根据这项研究,不到 30% 的数据科学家或 ML 工程师*,没有硕士或博士学位。但更有趣的是,根据同一项研究,不到 20% 的数据科学家和不到 50% 的 ML 工程师拥有计算机科学*学位。所以不要担心,如果你已经有了一个博士学位,你可能不需要再考一个。
- 可以充满惊喜,因为问题空间是巨大的,并且经常存在人类交互层。毕竟,不管人工智能应用程序是什么领域,它们最终都会暴露给人类用户。因为人类是不可预测的,所以你的解决方案的生命周期也是不可预测的。有些可能会持续到你离开公司的那一天,有些可能明天就需要重新设计。
- 普遍有吸引力的薪酬待遇。
我没有将上述特征分为优点和缺点,因为我认为这在很大程度上取决于个人。例如,如果你想要一个稳定的生活和一个常规的工作空间,那么我的无聊就不是一个缺点。
需要多长时间?
执行转换需要大量的时间和精力。作为任何一种投资,你可能要考虑多久可以收回利息。对某些人来说,这种转变可能是自然的,需要时间、数月甚至数年才能完成,正如本文中的。对于和其他来说,它可能会被顿悟的时刻所推动,并被一个仓促的计划所执行。以我的经验来看,如果你已经有了一份全职工作,通常需要几年时间,如果你在读大学,时间可能会短一点。主要是因为你需要空闲时间去发展新的技能。同样,这也很大程度上取决于你已经有什么,所以这个时间框架只是供参考。
怎么做过渡?
我很喜欢“过渡”这个词。你从一个状态过渡到另一个状态,而不是“跳到”或“开始一个新的”。换句话说,通过认识到你拥有的 ME 背景,并战略性地开发数据科学中所需的新技能,你将获得成功。
由于数据科学中的技能通常分为 3 个主要类别:数学/统计、领域知识和编程,因此我将相应地构建这一部分,每个部分都有一个“可重用性分数”,我认为在过渡中我们可以重用 ME 背景有多难。
数学/统计
可重用性评分:易。
如果有一件事是我对自己的背景最有信心的,那就是对数学和统计学的严谨和扎实的理解。回顾你的学业成绩单,数一数你学过的数学相关课程,你可能会感到惊讶。挑战在于,每当我和我的朋友谈论在大学学习数学时,我都会得到这样的回答:“是的,是的,我仍然在等待有一天我可以在我的现实生活中使用格林定理”。不,你不会。但那不是学习数学的目的。它给你“精神食粮”。它给你解决问题的动力。
我给你举个例子。如果我让你计算这个函数的一阶导数。简单,就是y = 2x
。但是,你如何利用这种技能赚钱呢(除了给一个高中生做家教)?
这里有个窍门,重要的不是数字和数学公式,而是数学背后的直觉。一阶导数告诉你函数增加或减少的速度。因此,在函数为常数的区域,它在 0 附近,而在其他地方不为零。让我们看看我们能用这种直觉做些什么。
现在,假设你是一名工程师,一家汽车制造厂的制造经理向你抱怨说,在让机器出厂之前,他们花了一大笔钱请一名测试人员来数机器上的螺丝数量。如果您能够自动化这项任务,那么在退出测试人员角色之后,您将获得 20%的利润。现在我们谈论的是真正的钱。
你拍一张测试者的照片。图片提供:https://en.wikipedia.org/wiki/Sobel_operator
你拍一张测试者的照片。你观察到螺丝和机身颜色不一样,那么如果有办法让它在照片中“弹出来”,你就可以有一个程序来“数”它们。因为你记得我们可以使用一阶导数来寻找函数显著变化的区域,所以你意识到这些螺钉的边缘应该具有非零的一阶导数。
应用一阶导数滤波后的相同照片。图片提供:【https://en.wikipedia.org/wiki/Sobel_operator
对图像进行一阶导数滤波,瞧!螺丝钉像星星一样明亮。
现在你可以开发一个计算机视觉系统来代替(可怜的)测试员,并收取你的份额。所有这些都是基于基本的一阶导数直觉。
再举个例子:如果我让你写下概率链规则,那就是小菜一碟:P(A,B)=P(A|B)*P(B)。但是这有什么用呢?
假设有一天,你的高级经理问了这样一个问题:“今年我们花费 2000 美元恢复 XYZ 服务器的可能性有多大?你知道,如果它倒下了。”这是一个数据科学家应该能够回答的有效且常见的业务问题。你会回过头来定义:
- 事件 A:我们必须在服务器 XYZ 上花费 2000 美元。
- 事件 B:服务器 XYZ 关闭。
因此,P(A|B)是你必须支付 2000 美元来修复服务器 XYZ 的概率,假设它确实坏了,P(B)是服务器坏了的概率。确定这些概率的一个快速而又肮脏的方法可能是:
- P(A|B):你把公司过去所有关于安装服务器 XYZ 的费用的发票翻出来,画一个柱状图,就截止到 2000 美元,假设曲线下的面积是 2%,即安装费用超过 2000 美元的概率。
- P(B):你翻查公司过去所有的服务器日志,找出所有有宕机的年份,然后除以总年数就可以了。假设在 10 年期间,它在 2 年内被破坏了两次,因此是 20%。
(请注意,异常检测是一个巨大的主题,这只是一个过于简化的解决方案。但我认为这足以说明问题。)
所以,你的公司今年必须支付 2000 美元来修复服务器的最终概率是0.2 * 0.02 = 0.004
,或者只有 0.4%。
任何学过概率统计 101 的人都可以编写链式法则,但是只有能够“翻译”它来回答商业问题,你才能得到这份工作。
领域知识
可重用性评分:中等。
与其他类型的工程不同,在其他类型的工程中,工程师的技能或工作成果直接产生最终用户购买的产品或服务,数据科学通常不直接创造价值。这就是领域知识至关重要的原因。数据科学家需要彻底了解业务,然后才能将人工智能模型应用于业务。
通常,我看到人工智能模型通过以下设置来创造利润:
- 取代由人类完成的单调乏味的任务(生产成本更低)。例如 RPA、自动化流程控制、需要对事物进行分类的任务等。
- 提高生产率(生产更快或销售更好)。例如过程优化器、AI 调度器、劳动力优化、推荐系统等。
- 基于发现的可行见解,修改/创建新的业务模式。例如,基于调查数据发现客户使用产品的新方式的分析,从而指导新的广告活动。
- 防止潜在损失。例如安全 AI、流失预测等。
这可能是显而易见的,但所有这些设置都回答了一个问题:
如何利用提出的 AI 模型盈利?
可以通过美元计数或其他 KPI 来衡量。当一个人第一次开始研究数据科学时,他们在第一个项目中可能会有很多顾虑。它可能是方法的新颖性、模型性能、计算复杂性、最新技术水平或模型评估等。对于整个解决方案的工作来说,所有这些都很重要。但是他们都应该以这个问题为导向。这听起来可能很实用,但实际上是。在任何事情发生之前,这个问题在步骤 0 应该有一个满意的答案。
这种转变有点困难,因为对于一个机械工程师来说,你不必真的关心价值是如何创造的。但你所拥有的,是如何设计和优化流程 : 机制设计、流程控制、热力学、奥托循环、顶点设计等训练有素的心态。都是过程!在某种程度上,商业模式本身就是一个过程。有更多的不确定性,因为人类参与其中(人类是随机的),但是你已经被训练在过程中模拟不确定性!
然而,你所带来的(你的面向过程的心态)只是交通工具,你仍然需要燃料来运行。你有没有想过你住的地方附近的美食街是怎么赚钱的?或者你穿的这件衬衫是在地球另一端的一个国家制造的,然后被运到你那里去买?只要把你的想法画在纸上,你会感到惊讶的。你有“良好的库存”,而不是“储气库”(只是一种对气体容器的花哨的热力学行话);你有了“数据提取管道”,而不是“流体导管”;不是“流量”,而是“数据传输速率”。物理定律是普遍适用的,它们在数字世界也同样适用。
编程;编排
可重用性评分:数据科学家:中等,ML 工程师:硬。
我区分这两者,因为在我看来,ML 工程师是可以做建模的核心软件工程师。因此,编程的能力水平需要达到极致。这种区别当然是相对的,因为肯定有数据科学家编写生产代码。
这是最需要花时间去掌握的部分。“编程”在这里有点用得不好,因为它不包括整个软件工程实践:网络、API、CI/CD、Dockerization 等等,你能想到的。不幸的是,ML 工程师之路需要他们(这里我假设数据科学家的背景包含在分析和可视化任务中,换句话说,就是那些不必编写生产代码的人。还是那句话,主观)。
TDS 上有无数的在线课程和精彩的文章,告诉你应该如何提高这一技能,所以我不会再重复了。我只想分享一点:封装的伟大概念,我指的不仅仅是 OOP。这对我的学习帮助很大。
您可以孤立地学习几乎任何软件工程工具/技能,并且一个完整的软件应用程序可以被分解成粒度模块,您可以分别学习每个模块。这太美了,我在机械工程专业做不到。这就产生了一个极其有效的学习策略:分而治之。我有很多文件夹,比如:python-practice
、spark-practice
、Docker-practice
、gRPC-practice
、k8s-practice
等等。每个不超过 5 个文件包含该工具最基本的示例代码。一旦你掌握了足够数量的这些“乐高积木”,设计 solution architect 将充满乐趣。你可以自由决定你的人工智能模型如何与其他模块互动。
请注意,将所有模块组合在一起以形成一个功能应用程序需要付出巨大的努力,否则,软件工程师就不会存在,因为人们只需选择模块,一个“通用”程序就会编译出所需的应用程序,就像你在麦当劳自助点餐亭订购一样。这是一个你可以用来快速学习的有效方法。
当我离开的时候,有两个工具我不得不花相当多的时间来学习: git 和 SQL 。这很有意义,因为在我这里,你不必管理代码,也不必管理关系数据库。对当时的我来说,Dropbox 足以分享代码,后跟前缀如“backup_20080202”的文件夹足以进行版本控制,Excel 可以保存表格。生活简单又容易。所以我必须用 git 和 SQL 来升级游戏。
git :我一直认为通过了解一些 git 命令行,我可以流利地使用 git,直到我需要和我的同事合并代码。因此,我推荐的最佳实践是和你的朋友一起做一些结对编程项目:课程项目、爱好项目,或者和你的同事一起开发一个小功能。你越早这样做,你就越早意识到 git 不仅仅是版本控制,你会过得越好。
SQL :在我看来,你用的是什么版本的 SQL 并不重要,只要你理解了SQL 中所有不同种类的连接并且知道如何检查结果,你就是优秀的。为什么?因为语法错误很容易发现,所以平台会说“错误:不能将字符串类型转换为整数。”,你的查询会失败,你会知道的。但是连接是无声的杀手。如果您使用了错误的连接类型,或者您没有仔细检查就相信了您的查询结果,那么很可能只有当您的经理叫您进来并质疑您的“荒谬”图表时,您才会发现这一点。例如,如果在不检查重复项的情况下将一个销售表与其他一些产品信息表联接起来,那么联接后的表将会有重复的行。换句话说,一个售出的产品可能会出现不止一次,在你的视觉化视图中,销售量表明你的公司是财富 500 强,而不是一个创业公司。没有人会认真对待你的陈述。
最后的话
简而言之,我们讨论了:
- 数学/统计:你已经从教育中获得了所有需要的基础训练。挑战在于将这些知识运用到解决现实世界的问题中。
- 领域知识:理解人工智能应用的本质目的是创造利润有助于你在看待商业模式时将镜头转向不同的角度。毕竟都是过程。
- 编程:分而治之。利用好封装的性质快速学习。
这些东西在我的旅程中给了我很大的帮助,我希望它们对你也有用。
在这个数据领域工作在很多方面都是有益的,但对我来说,这是当一个模型被部署到生产中并看着它运行时我的喜悦,就像当我建造一个机器人并第一次看到整个东西运行时一样。
我们毕竟是工程师。
“像伟大的工程师一样进行机器学习,而不是像你不是伟大的机器学习专家一样。”
快乐(机器)学习!
使用 googletrans 库翻译熊猫数据框
谷歌翻译标志
Googletrans 是一个免费的 python 库,它使用了 Google Translate API 。在本文中,我们将解释如何使用这个库来翻译 Python 中的字符串和数据帧。
翻译字符串
Googletrans 是一个第三方库,我们可以使用 **pip 来安装。**安装完库之后,我们导入模块 googletrans。
第一步是创建一个翻译器对象。然后,我们使用方法 translate ,将我们想要翻译的字符串作为第一个参数传递。该方法返回一个对象,其属性为**。文本**提供如下翻译后的字符串:
可以使用 src 和 dest 参数指定源语言和目的语言。如果没有提供,googletrans 会尝试检测该语言并将其翻译成英语。下面,我们把前面的字符串 Hola Mundo (西班牙语)翻译成意大利语和德语。
我们可以使用 *查询支持的翻译语言。*语言属性。该属性返回一个字典,其中包含了 googletrans 中可用的语言。
如前所述,translate 方法返回一个翻译后的对象。该对象具有以下属性:
翻译方法属性
到目前为止,我们已经将一个字符串翻译成了英语、意大利语和德语。现在,我们想完成一项更具挑战性的任务:翻译一个完整的数据帧。
翻译数据帧
我们要翻译的数据框可通过以下链接下载:
[## 马德里阿云达芒托群岛的门户网站
马德里大医院的门户网站——移民。西班牙语课程中的人物
datos .马德里. es](https://datos.madrid.es/portal/site/egob/menuitem.c05c1f754a33a9fbe4b2e4b284f1a5a0/?vgnextoid=454bdc5cc762b410VgnVCM2000000c205a0aRCRD&vgnextchannel=374512b9ace9f310VgnVCM100000171f5a0aRCRD&vgnextfmt=default)
datos.madrid.es 是 Madrid s 数据服务,包含近 500 个数据集,涵盖商业、交通、旅游、文化等广泛话题。目前,越来越多的欧洲城市提供开放的数据门户,允许公司、市民、研究人员和其他公共机构使用这些数据。数据集通常以该国语言提供。就datos . Madrid . es而言,大部分数据集都有西班牙语版本。
在本文中,我们翻译了包含 2018 年第一学期期间在市政办公室参加外国人西班牙语课程的学生的信息的数据集。该数据集包括关于学生的信息,如他们参加的课程级别、性别、年龄、国籍、教育水平和行政地位。
首先我们从datos . Madrid . de**下载 csv 文件。**然后,我们使用Pandas . read _ CSV函数将其加载到 Pandas 数据框中,并使用 pandas 可视化前 5 行。data frame . head方法。
数据集包含 8 栏:(1)级别,(2)性别,(3) Edad,(4)行政状况,(5)国家地理,(6)地区地理,(7)专业类别,和(8)研究级别。
现在,我们从翻译开始!首先,我们使用 熊猫来翻译列名。DataFrame.rename 功能如下:
使用 的熊猫。DataFrame.columns 的属性,我们可以检查翻译是否正确进行。在本文中,我们只是翻译列名,但还需要进一步的数据清理(用下划线代替空格,用小写字母代替大写字母)。
翻译完列名后,我们翻译其余的数据(单元格值)。首先,我们创建一个字典,其中的关键字是英语术语,值是西班牙语的原始术语。
在改变数据框架之前,我们检查由 googletrans 所做的翻译。正如我们所观察到的,有些术语翻译不正确(例如 A2 或 A2 DELE)。在这种情况下,我们以下列方式手动更改它们:
现在,我们可以通过使用熊猫来修改数据帧。DataFrame.replace** 函数,使用之前创建的字典作为函数的输入。**
英语数据框架
正如我们所观察到的,整个数据都被翻译了💪工作完成:)
有趣的读物
- https://pypi.org/project/googletrans/
- http://zetcode.com/python/googletrans/
- https://www . code project . com/Tips/1236705/How-to-Use-Google-Translator-in-Python
感谢阅读!!🍀
用 60 行 Python 语言翻译任意两种语言
作为一名 NLP 工程师,我很快就要失业了😅
介绍
我记得当我在 2015 年建立我的第一个 seq2seq 翻译系统时。从处理数据到设计和实现模型架构,这是一项繁重的工作。所有这些就是把一种语言翻译成另一种语言。现在模型变得更好了,围绕这些模型的工具也变得更好了。HuggingFace 最近将来自赫尔辛基大学的 1000 多个翻译模型整合到他们的变形金刚模型动物园中,它们很不错。制作这个教程让我感觉很糟糕,因为构建一个翻译系统就像从变形金刚库中复制文档一样简单。
总之,在本教程中,我们将制作一个转换器,它将自动检测文本中使用的语言并将其翻译成英语。这很有用,因为有时您将在一个包含来自许多不同语言的文本数据的领域中工作。如果你只用英语建立一个模型,你的性能会受到影响,但是如果你能把所有的文本标准化为一种语言,你可能会做得更好。
非常有才华的 Chema Bescós 将这篇文章翻译成西班牙语,如果英语不是你的第一语言,你可以在这里找到。
💾数据💾
为了探索这种方法的有效性,我需要一个包含许多不同语言的小文本的数据集。来自 Kaggle 的 Jigsaw 多语言有毒评论分类挑战非常适合这一点。它有一个超过 223,000 条标注为有毒或无毒的英语评论的训练集,以及一个验证集中来自其他语言的 8,000 条评论。我们可以在英语训练集上训练一个简单的模型。然后使用我们的翻译转换器将所有其他文本转换成英语,并使用英语模型进行预测。
看一下训练数据,我们看到在六个类别中的每一个类别中都有大约 22 万个英语*示例文本。
训练数据的视图
事情变得有趣的地方是验证数据。验证数据不包含英语,而是包含来自意大利语、西班牙语和土耳其语的示例。
验证数据的示例
🕵️♀️确定了🕵️♀️语
自然地,将任何语言标准化为英语的第一步是识别我们未知的语言是什么。为此,我们求助于来自脸书的优秀的快速文本库。这个图书馆里有很多令人惊奇的东西。这个图书馆名副其实。它真的很快。今天我们只使用它的语言预测能力。
识别任意字符串是哪种语言就这么简单。我对验证集进行了测试,以了解模型的表现。坦率地说,我对它开箱即用的表现感到惊讶。在 8000 个例子中,Fasttext 只错分了 43 个。在我的 MacbookPro 上运行也只用了 300 毫秒。从这两方面来看,这都是非常荒谬的🍌。如果你仔细观察,你会发现在一些西班牙语的错误预测中,它预测了加利西亚语或欧西坦语。这些语言在西班牙及其周边地区使用,源自西班牙。因此,某些情况下的预测失误并没有我们想象的那么糟糕。
Fasttext API 的语言标识在我们的验证集上的混淆矩阵。
🤗变形金刚(电影名)🤗
既然我们可以预测一个给定的文本是哪种语言,那么让我们看看如何翻译它。来自 HuggingFace 的变形金刚库一直让我惊叹不已。他们最近在他们的模型动物园中增加了超过 1000 个翻译模型,每一个都可以用来翻译大约五行代码中的任意文本。我几乎是直接从文档中窃取的。
lang = "fr"
target_lang = "enmodel_name = f'Helsinki-NLP/opus-mt-{lang}-{target_lang}'# Download the model and the tokenizer
model = MarianMTModel.from_pretrained(model_name)
tokenizer = MarianTokenizer.from_pretrained(model_name)
# Tokenize the text
batch = tokenizer([text], return_tensors="pt", padding=True)
# Make sure that the tokenized text does not exceed the maximum
# allowed size of 512
batch["input_ids"] = batch["input_ids"][:, :512]
batch["attention_mask"] = batch["attention_mask"][:, :512]# Perform the translation and decode the output
translation = model.generate(**batch)
tokenizer.batch_decode(translation, skip_special_tokens=True)
基本上,对于任何给定的语言代码对,你都可以下载一个名为Helsinki-NLP/optus-mt-{lang}-{target_lang}
的模型,其中lang
是源语言的语言代码,target_lang
是我们要翻译到的目标语言的语言代码。如果您想将韩语翻译成德语,请下载Helsinki-NLP/optus-mt-ko-de
模型。就这么简单🤯!
我对文档做了一点小小的修改,将 input_ids 和 attention_mask 窗口设置为只有 512 个令牌长。这很方便,因为大多数转换器模型最多只能处理 512 个令牌的输入。这可以防止我们弄错较长的文本。如果你试图翻译很长的文本,这会引起问题,所以如果你使用这段代码,请记住这个修改。
sci kit-学习管道
下载完模型后,让我们轻松地将它整合到 sklearn 管道中。如果你读过我以前的帖子,你可能知道我喜欢 SciKit 管道。它们是构成特征化和模型训练的一个很好的工具。考虑到这一点,让我们创建一个简单的转换器,它将接受任何文本数据,预测它的语言,并翻译它。我们的目标是通过运行以下命令来构建一个与语言无关的模型:
from sklearn import svm
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipelineclassifier = svm.LinearSVC(C=1.0, class_weight="balanced")
model = Pipeline([
('translate', EnglishTransformer()),
('tfidf', TfidfVectorizer()),
("classifier", classifier)
])
这个管道将把任何文本中的每个数据点翻译成英语,然后创建 TF-IDF 特征,然后训练一个分类器。这个解决方案使我们的特性与我们的模型保持一致,并使部署更容易。通过在一个管道中完成特征、训练和预测,它还有助于防止特征与模型不同步。
现在我们知道了我们要努力的方向,让我们来建造这个英语变压器吧!上面的大部分代码你已经看到了,我们只是把它们拼接在一起。😄
- 第 15–18 行—确保 fasttext 模型已经下载并可以使用。如果不是,它会将其下载到 temp
/tmp/lid.176.bin
。 - 第 24 行——建立了可以用赫尔辛基浪漫模型翻译的语言代码。该模型可以很好地处理大量的语言,并且可以为我们节省大量的磁盘空间,因为我们不必为每种语言下载单独的模型。
- 第 27–30 行—定义我们要翻译的语言。我们想要创建一个允许的语言列表,因为每个模型大约 300MB,所以如果我们下载 100 个不同的模型,我们将得到 30GB 的模型!这限制了语言的设置,这样我们就不会在磁盘空间不足的情况下运行系统。如果你想翻译这些代码,你可以将ISO-639–1代码添加到这个列表中。
- 第 32–38 行—定义一个函数来执行快速文本语言预测,就像我们上面讨论的那样。你会注意到我们也过滤掉了
\n
字符。这是因为 Fasttext 自动假定这是一个不同的数据点,如果它们存在,就会抛出一个错误。 - 第 41 行——定义了转换,也是魔法发生的地方。这个函数将把任何语言的字符串列表转换成英语的字符串列表。
- 第 48–50 行—检查当前字符串是否来自我们的目标语言。如果是的话,我们会将其添加到我们的翻译中,因为它已经是正确的语言了。
- 第 54–55 行—检查预测的语言是否能被浪漫模型处理。这有助于我们避免下载一堆额外的语言模型。
- 第 56–65 行—应该看起来很熟悉,它们只是拥抱脸文档的翻译代码。这个部分下载正确的模型,然后对输入文本执行翻译。
就是这样!超级简单,它可以处理任何事情。需要注意的是,这段代码被写得尽可能易读,而且非常慢。在这篇文章的最后,我包括了一个更快的版本,批量预测不同的语言,而不是为每个数据点下载一个模型。
🤑结果🤑
我们现在可以使用以下工具训练和测试我们的模型:
from sklearn import svm
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipelineclassifier = svm.LinearSVC(C=1.0, class_weight="balanced")
model = Pipeline([
('translate', EnglishTransformer()),
('tfidf', TfidfVectorizer()),
("classifier", classifier)
])
model.fit(train_df["comment_text"].tolist(), train_df["toxic"])
preds = model.predict(val_df["comment_text"])
在英文训练集上训练一个简单的 TF-IDF 模型,并在验证集上进行测试,我们得到了 0.15 的有毒评论 F1 分数!太可怕了!预测每一类有毒物质的 F1 值为 0.26。使用我们新的翻译系统预处理所有输入并翻译成英语,我们的 F1 变成了. 51。这几乎是 4 倍的提高!
翻译和未翻译模型之间的性能比较
请记住,这里的目标是简单的翻译,而不一定是 SOTA 在这项任务上的表现。如果你真的想训练一个有毒评论分类模型,获得良好的性能来微调像 BERT 这样的深度 transformer 模型。
如果你喜欢这篇文章,可以看看我的另一篇关于使用文本和 SciKit-Learn 的文章。感谢阅读!: )
BERT 可以在许多 NLP 任务中为您提供最先进的结果,并且只需要几行代码。
towardsdatascience.com](/build-a-bert-sci-kit-transformer-59d60ddd54a5) [## Good Grams:如何为您的问题找到预测性的 N-Grams
找出哪些单词对你的问题有预测性是很容易的!
towardsdatascience.com](/good-grams-how-to-find-predictive-n-grams-for-your-problem-c04a5f320b39)
更快的变压器
正如所承诺的,这里是一个更快版本的英语变压器的代码。在这里,我们按照预测的语言对语料库进行排序,并且对于每种语言只加载一次模型。在此基础上使用 transformer 对输入进行批处理,可以使速度更快。
平移不变性与平移等价性
平移不变性和平移等方差经常被混淆为同一事物,但它们是 CNN 的不同属性。要了解不同之处,请阅读下文。
卷积神经网络已经成为基于图像和视频的任务(如分类、定位、分割等)的首选架构。他们在以前被认为使用基本图像处理技术很难完成的任务中表现出了超人的水平。它使分类任务变得相对容易执行,而不需要像 CNN 变革之前那样向模型输入手动精选的特征。
卷积神经网络的灵感来自诺贝尔奖得主科学家 Hubel 博士和 Wiesel 博士的工作,他们展示了大脑中视觉皮层的工作。他们在一只部分麻醉的猫的视觉皮层中插入微电极,使她不能移动,并在它的视网膜上移动一条亮线。在这个实验中,他们注意到了以下场景:
- 当这条线位于视网膜上的特定位置时,神经元就会放电。
- 神经元的活动根据线的方向而变化。
- 神经元有时只有在线路向特定方向移动时才会放电。
这个经典实验展示了视觉皮层是如何以一种分层的方式处理信息,提取越来越复杂的信息。他们表明,在视觉皮层中有一个代表视野的地形图,,附近的细胞在这里处理来自附近视野的信息。这给了 CNN 的稀疏交互的概念,在这里,网络聚焦于本地信息,而不是获取完整的全球信息。这一特性使得 CNN 在图像相关任务中提供了最先进的性能,因为在图像中,附近的像素比远处的像素具有更强的相关性。此外,他们的工作确定了视觉皮层中的神经元以精确的结构排列。具有相似功能的细胞被组织成列,微小的计算机器将信息传递到大脑的更高区域,在那里形成视觉图像。这类似于 CNN 架构的设计方式,其中较低层提取边缘和其他公共特征,而较高层提取更多特定于类别的信息。总之,他们的工作揭示了视觉皮层神经元如何对图像特征进行编码,图像特征是帮助我们建立对周围世界的感知的物体的基本属性。
图 1 : Hubel 和 Weisel 的实验直观解释。
图 2:
卷积神经网络提供了优于传统全连接层的三个基本优点。首先,它们具有稀疏连接,而不是完全连接的连接,这导致参数减少,并使 CNN 有效地处理高维数据。第二,权重共享发生在相同的权重在整个图像上共享的情况下,导致减少的存储器需求以及平移等变(稍后将解释)。第三,CNN 使用了一个非常重要的二次采样或汇集的概念,其中最突出的像素传播到下一层,丢弃其余的像素。这提供了固定大小的输出矩阵,这通常是分类和对平移、旋转的不变性所需要的。
平移等方差:
平移等方差或仅等方差是卷积神经网络的一个非常重要的属性,在卷积神经网络中,图像中对象的位置不应该是固定的,以便它被 CNN 检测到。这仅仅意味着如果输入改变,输出也会改变。准确地说,如果 f(g(x)) = g(f(x)),则称函数 f(x)与函数 g 等变。如果我们有一个函数 g,它将图像的每个像素向右移动一个像素,即 I’(x,y) = I(x-1,y)。如果我们对图像应用变换 g,然后应用卷积,结果将与我们对 I '应用卷积,然后对输出应用平移 g 的结果相同。当处理图像时,这仅仅意味着如果我们将输入向右移动 1 个像素,那么它的表示也将向右移动 1 个像素。
CNN 的平移等价性是通过权重共享的概念实现的。由于在图像之间共享相同的权重,因此,如果对象出现在任何图像中,则不管它在图像中的位置如何,它都将被检测到。该属性对于诸如图像分类、对象检测等应用非常有用,在这些应用中,对象可能多次出现或者对象可能处于运动中。
卷积神经网络对于某些其他变换,如图像的缩放或旋转变化,并不是自然等变的。需要其他机制来处理这种转换。
图 3 。由于翻译等变性质,检测到各种 cat 实例。
**图 4。**在同一帧中检测多只狗时看到的平移等方差特性。
平移不变性:
平移不变性经常与平移等价性混淆,许多人,甚至是专家都分不清两者的区别。
平移不变性使 CNN 具有平移不变性。平移不变性意味着,如果我们平移输入,CNN 仍然能够检测输入所属的类别。
平移不变性是汇集操作的结果。在传统的 CNN 架构中,有三个阶段。在第一阶段,该层对输入执行卷积运算以给出线性激活。在第二阶段,产生的激活通过非线性激活函数,例如 sigmoid、tanh 或 relu。在第三阶段,我们执行池操作来进一步修改输出。
在池化操作中,我们用附近输出的汇总统计来替换某个位置的 convnet 的输出,例如最大池化情况下的最大值。因为在最大池化的情况下,我们用最大值替换输出,因此即使我们稍微改变输入,也不会影响大多数池化输出的值。在不需要物体的精确位置的情况下,平移不变性是一个有用的属性。例如,如果你正在建立一个检测人脸的模型,你需要检测的只是眼睛是否存在,它的确切位置是不必要的。而在分割任务中,需要精确的位置。
池化的使用可以被视为添加了一个强先验,即该层学习的函数必须对平移不变。当先验正确时,可以大大提高网络的统计效率。
**图五。**显示平移不变性,尽管输入向右移动,但表示也移动。
**图六。**平移不变性的例子
平移不变性和平移等方差的属性在一种称为数据扩充的技术中得到部分利用,当我们拥有较少的训练数据或希望在更丰富的数据集上进行模型训练时,这种技术会很方便。在数据扩充中,我们对从训练集中随机采样的每批数据应用不同的变换,如旋转、翻转、缩放、平移等,并将其提供给模型,以使其对变换更加鲁棒并提高性能。
图 6: 所示的数据增强技术
结论:
卷积神经网络正在解决以前被认为无法解决的各种挑战,并且在大多数情况下,击败了人类水平的性能,正如在 ImageNet challenge 中看到的那样,Resnet 比人类表现得更好。使 CNN 如此伟大的概念并不复杂,但非常直观,有逻辑性,易于理解。
我希望你喜欢这篇文章,如果你有任何疑问、建议或要求,请在下面留下你的评论,或者通过 twitter 或 LinkedIn 与我联系。
参考资料:
- 伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔的深度学习书籍。
- 人类视觉系统架构图 2 取自 knowingneurons.com。
- 为什么等方差优于过早不变性 Geoffrey Hinton(图 4)。
- 图 6 来自 itutorials.com。
新冠肺炎投影模型的透明度、再现性和有效性
没有停滞迹象的模型的指数增长
预测传染病传播的模型在为全世界的公共卫生政策提供信息方面一直发挥着重要作用。但是在历史上,公众和媒体从来没有像新冠肺炎·疫情事件那样敏锐地意识到这些模型的存在和重要性。研究人员和决策者正依靠这些模型在国家和地方层面进行规划。此外,公众正在敏锐地跟踪趋势,希望这些模型可以指导他们何时生活将恢复正常,无论这意味着回到工作和学校,计划下一个假期,还是只是剪头发。在疫情第一阶段过后不久,需要进行快速预测,现成的模型受到了媒体和政府官员的关注。现在越来越清楚的是,这种疫情将在未来几个月甚至几年继续扰乱生活,关于锁定、社交距离和资源分配的关键决策将继续严重依赖其中一些模型。新的模式不断涌现,各种统计数据和预测在社交媒体平台上迅速传播,一些人将此称为“流行病”本身(见卫报文章)。很难知道这些预测中哪些是可靠的,而且来自不同模型的信息经常相互矛盾(见《洛杉矶时报》文章),在危机时期制造了一种进一步的不确定感。
我们相信,在未来,如果可用的预测种类更少,社会将得到更好的服务,但可用的预测将因其可靠性而受到更多审查。这促使我们探索一些可用的模型,包括两个最有影响力的模型,使用三个标准 (1)透明度(2)再现性和(3)有效性 。接下来,我们描述我们在这方面的经验和教训,希望在未来,它可以开始建立一个更好的评估未来模型的框架。我们使用了来自意大利和纽约的数据,这些数据可以在 Github CSSEGISandData 新冠肺炎和 Github NYTimes 新冠肺炎-data 网站上获得,这两个地方的疫情疫情严重,但每天的死亡人数已经达到峰值。我们使用不同数量的数据追溯到峰值,然后检查该模型在预测未来短期趋势方面的表现,并与截至 2020 年 5 月 1 日的观察模式进行比较。
两种有影响力的死亡预测模型的比较评价
可以说有两种模型,一种是由伦敦帝国理工学院(伦敦)****【ICL】**开发的,另一种是由美国、英国和欧洲最有影响力的政策制定机构(IHME)华盛顿大学(西雅图)开发的。特别是,在三月中旬左右,帝国理工学院的模型预测,由于新冠肺炎没有采取任何缓解措施,英国将有 50 万人死亡,美国将有 200 万人死亡。这些预测引起了决策者对实施严格封锁和社会距离措施的紧迫性的关注。IHME 模型从 3 月下旬开始在美国受到关注,尤其是在白宫每日新闻简报开始引用其结果之后。除了死亡之外,该模型还可以预测住院和 ICU 入院,因此很可能已经用于州级资源规划。这一模式最近受到了传染病专家的批评,但它仍然被政府和媒体人员广泛引用。
透明度和再现性
帝国理工学院模型的源代码通过 GitHub 公开。在 11 个欧洲国家应用该模型的代码是可用的,发布的结果是可复制的。然而,对在其他数据集上应用该方法感兴趣的用户将需要更多关于数据处理细节和参数设置的指导性指导。一份可用的报告以透明的方式描述了基础方法,并为各种模型选择和假设提供了充分的理由。该方法使用贝叶斯框架,该框架建立在潜在感染病例的传播模型(见下文)上,并通过借用各国的信息来指定感染病例中的死亡分布。该报告还描述了对各种模型选择和假设的预测的各种敏感性分析。
IHME 模型的源代码也可以通过 GitHub 获得。IHME 模型预测可用于不同的地点,包括美国各州和欧洲经济区国家的网站上。然而,重现这些结果所需的方法和参数的准确规格是不透明的,因此结果是不可重现的。报告提供了对基本方法的简要描述,该方法依赖于将一个相当严格的参数模型与观察到的数据进行拟合。然而,该报告对模型选择和假设给出了有限的理由。特别是,很难跟踪预测中的不确定性是如何量化的,以及社会距离协变量是如何定义的。
在意大利和纽约州进行回顾性验证
图 1 显示,IHME 模型使用高峰前 7 天的数据,对高峰时的死亡时间和强度做出了较好的估计。但是当数据在峰值前 14 天使用时,峰值预测并不可靠。此外,该模型预测死亡人数将在高峰后快速下降。然而,目前的趋势表明,峰值后的下降趋势要温和得多。事实上,观察到的累积死亡人数经常超过置信区间上限。ICL 模型高估了意大利每日死亡人数,尽管观察到的趋势在提供的置信带(阴影区域)内。就纽约而言,预测和观察到的趋势非常相似,但当使用峰值前一两周的数据建立模型时,预测趋势的置信界限非常宽。
****
**图一。**验证 IHME 和 ICL 模型。使用高峰前 14 天或 7 天的“训练”数据(棕色线)或高峰前的所有数据建立模型。在意大利和纽约,观察到的每日死亡高峰分别是 3 月 27 日和 4 月 7 日。训练期后的预测趋势(橙色线)与观察到的趋势(蓝色线)进行比较。阴影区域显示预测趋势周围 95%的置信界限。对于 IHME 模型,置信区域不可用于每日死亡,而是显示累积死亡。数据从累计死亡人数达到百万分之 0.31(IHME 模型拟合使用的标准)的那一天开始显示。
评估用于预测感染病例的传播模型
预测感染病例的模型,而不是包含易感、暴露、感染和清除(SEIR)之间不同过渡状态的死亡模型,早在中国武汉省发生疫情后就已使用[ Wu 等人,2020 年。我们找不到 SEIR 模型的开源代码,这些代码很容易操作来拟合其他国家的数据。相反,我们使用了一个稍微简化的 SIR 模型[ Song et al. 2020 ],这是密歇根大学的研究人员最近开发的。
透明度和再现性
这个 eSIR 模型的源代码可以通过 Github 获得。该模型仅适用于武汉数据集,使用现有代码可再现结果。随附报告[ 宋等人 2020 ]中描述的基本方法易于遵循且合理。
该模型的一个局限性是,它需要指定表示封锁期内疾病动态的未知参数值。但是因为代码很容易访问,我们可以重写一部分代码,使用上周的训练数据对未知参数进行进一步的“调整”。具体来说,我们使用指数衰减函数来模拟首次干预后的传播率下降,其中我们假设在达到阈值后出现衰减平稳期。我们将衰减率和阈值水平视为“调谐参数”。我们拟合原始模型,删除最后一周的训练数据,然后搜索两个参数的组合,该组合给出最后一周内观察到的情况的预测趋势的最佳拟合。
验证
图 2 显示,对参数进行额外调整后的 eSIR 模型可以合理准确地预测报告案例的未来趋势。然而,这种特殊的模型产生了非常大范围的不确定性,并且该范围通常包括长时间内的零个新病例。
****
**图二。**eSIR 模型的验证。使用峰值前 7 天的“训练”数据(棕色线)或峰值前的所有数据建立模型。观察到的每日病例高峰分别是意大利和纽约的 3 月 21 日和 4 月 4 日。训练数据被进一步分割,最后 7 天用于调整模型的锁定后参数。训练期后的预测趋势(橙色线)与观察到的趋势(蓝色线)进行比较。阴影区域显示预测趋势周围 95%的置信界限。数据显示,从当天开始累计死亡人数超过 6 人。
总结发现和注意事项
在两个流行的模型中,我们发现伦敦帝国理工学院(ICL)的模型比 IHME 的模型更透明和可复制。前一个模型有时高估了未来的死亡人数,而后一个模型显然低估了高峰后的死亡人数。两个模型都使用一周前的数据合理地预测了峰值的时间。ICL 模型对纽约州产生了更大范围的不确定性,这可能是因为该模式不符合他们从欧洲国家使用的内部培训数据。eSIR 模型是透明的,我们可以修改他们的代码来提出我们自己的策略,通过数据分割来了解一些锁定后的参数。然而,目前实施的模型产生了非常大的不确定性,以至于置信下限提供的最佳情况可能没有意义。
更强调不确定性
我们主张更加重视围绕预测的不确定性的沟通。ICL 和 eSIR 模型都包含一个灵活的贝叶斯建模框架,可以围绕预测趋势产生非常宽的不确定性范围。IHME 模型产生了较窄的不确定性界限,但由于使用了无法解释观察到的趋势的非常严格的模型,它很可能低估了不确定性。在未来,建模者需要更有力地交流不确定性的水平和来源,并意识到,有时,主要的发现可能是不确定性本身的水平。媒体和政府人员也需要更多地关注不确定性的范围,并对宣传周围有太多不确定性的统计数据持谨慎态度。最佳做法可能是强调更短期的预测,并意识到长期趋势可能出现的最坏情况,正如一些最可靠预测的置信上限所表明的那样。
不要过度使用模特
我们还提倡在使用这些模型评估干预措施的有效性时要更加谨慎,因为估计中存在很大的不确定性。例如,在 COV-IND-19 研究小组的媒体文章中,研究人员使用我们使用的相同 eSIR 模型评估了 3 月 25 日印度封锁的影响。他们预计,在没有封锁的情况下,到 4 月 30 日,病例数将在 35,000 例左右,如果封锁有效,病例数可能低至近 4000 例。事实上,在这篇文章发表的当天(4 月 3 日),印度的病例数为 2567 例,到 4 月 30 日,报告的病例数接近 35000 例。值得称赞的是,尽管作者承认不确定性的范围很大,但决策者和公众并不容易理解不确定性的概念,这种分析可能会产生一种意想不到的印象,即封锁可能并不有效。提升封锁效果的最佳方法是以透明的方式报告经验数据,如报告病例和死亡的增长率,这些数据清楚地表明封锁在印度效果良好,就像在许多其他国家一样(见图 3)。
**图三。**病例/死亡日增长率。显示自病例/死亡数超过 100/50 后的趋势。
我们的分析也有局限性
我们的分析结果也应该谨慎解读。我们只使用了两个高密度区域的数据来检验模型预测的有效性。在强度较低的国家/地区,模式可能有所不同,我们计划在以后的研究出版物中使用额外的数据进行更广泛的分析。我们已经尝试基于可用的文档尽可能最好地实现不同的模型。有可能我们没有在建模者想要的理想环境中实现其中的一些。此外,在我们完成分析后,ICL 和 IHME 模型最近都进行了更新,这些新版本的模型可能提高了有效性。我们也承认,在疫情这样一个非常动态的情况下,模型验证的意义可能存在模糊性,在这种情况下,与预测值相比,干预措施的实施和取消预计会改变观察到的趋势(参见讨论)。尽管如此,我们相信,随着在疫情第一阶段的整个周期中,以及在不同类型的干预措施下,越来越多的数据可用,恰当地定义和展示模型验证将越来越有可能。
需要缓解需要缓解**
展望未来,显然需要付出更多努力,遵循透明、可复制和有效的原则,更好地审查模型。不附带其他定量研究人员可以访问、操作和试验的开源代码的模型是不可靠的。使用这些模型的具体预测还应该附带额外的开源代码、规范和数据,以便其他研究人员可以准确地复制结果。此外,随着越来越多的经验数据可用于流行病第一阶段的整个周期,建模者应考虑使用回顾性数据来验证他们自己的预测。然而,当我们应用这些原则时,重要的是要记住,所有与疫情相关的事情都需要在有限的时间和资源内紧急完成。因此,我们呼吁热衷于对该领域做出贡献的定量研究人员,将他们的努力导向评估和可能调整现有模型。
显然,在未来,如果预测模型要更加精确,它需要纳入除疫情本身的简单时间序列数据之外的其他类型的数据。就像股票市场的预测将具有有限的价值,除非有关住房和劳动力市场、行业前景和消费者行为的信息被纳入模型,我们不能指望新冠肺炎预测模型有更高的精确度,除非其他类型的数据,如移动和基于 GPS 的移动信息或不同类型干预下的公众行为调查数据被纳入。
密码
本文中所有分析使用的代码可以在这里访问
贡献者
这篇文章是合作努力的结果
约翰·霍普金斯大学博士后金
Neha Agarwala,马里兰大学博士生
约翰·霍普金斯大学即将毕业的博士生 Prosenjit Kundu
约翰·霍普金斯大学博士生王怡
赵,约翰·霍普金斯大学博士生
约翰·霍普金斯大学彭博杰出教授 Nilanjan Chatterjee(电子邮件:nilanjan10c@gmail.com)
确认
这篇文章从我们的同事和约翰霍普金斯大学生物统计系的导师那里得到了实质性的改进,包括 Jeff Leek、Thomas Louis、Karen Bandeen-Roche 和 Elizabeth Stuart 教授。
转置卷积去神秘化
转置卷积对于图像分割、超分辨率等应用来说是一个革命性的概念,但有时它会变得有点难以理解。在这篇文章中,我将试图揭开这个概念的神秘面纱,让它更容易理解。
(来源)
介绍
自从卷积神经网络(CNN)流行以来,计算机视觉领域正在经历一个过渡阶段。这场革命始于 Alexnet 在 2012 年赢得 ImageNet 挑战赛,自那以来,CNN 一直统治着图像分类、对象检测、图像分割和许多其他图像/视频相关任务的领域。
随着我们深入网络,卷积运算降低了空间维度,并创建了输入图像的抽象表示。CNN 的这一特性对于图像分类等任务非常有用,在这些任务中,您只需预测特定对象是否出现在输入图像中。但是该特征可能会给诸如目标定位、分割之类的任务带来问题,其中原始图像中的目标的空间维度是预测输出边界框或分割目标所必需的。
为了解决这个问题,使用了各种技术,例如全卷积神经网络,其中我们使用“相同”填充来保留输入维度。虽然这种技术在很大程度上解决了这个问题,但是它也增加了计算成本,因为现在卷积运算必须应用于整个网络的原始输入维度。
**图一。**全卷积神经网络(来源
用于图像分割的另一种方法是将网络分成两部分,即下采样网络和上采样网络。
在下采样网络中,使用简单的 CNN 架构,并产生输入图像的抽象表示。
在上采样网络中,使用各种技术对抽象图像表示进行上采样,以使其空间维度等于输入图像。这种架构以编码器-解码器网络而闻名。
图二。用于图像分割的下采样和上采样网络(来源)。
上采样技术
下采样网络直观且众所周知,但很少讨论用于上采样的各种技术。
编码器-解码器网络中最广泛使用的上采样技术是 :
- 最近邻居:顾名思义,在最近邻居中,我们取一个输入像素值,并将其复制到 K 个最近邻居,其中 K 取决于预期输出。
图三。最近邻向上采样
2.**双线性插值:**在双线性插值中,我们取输入像素的 4 个最近像素值,并根据 4 个最近像元的距离进行加权平均,平滑输出。
**图 4。**双线性插值
3.**钉床:**在钉床中,我们复制输出图像中相应位置的输入像素值,并在其余位置填充零。
**图 5。**甲床上采样
4.**Max-un Pooling:**CNN 中的 Max-Pooling 层取内核中所有值中的最大值。要执行 max-unpooling,首先,在编码步骤中为每个 max-pooling 层保存最大值的索引。然后在解码步骤中使用保存的索引,其中输入像素被映射到保存的索引,在其他地方填充零。
**图 6。**最大-取消上采样
所有上述技术都是预定义的,并且不依赖于数据,这使得它们是特定于任务的。它们不从数据中学习,因此不是一种通用的技术。
转置卷积
转置卷积用于使用一些可学习的参数将输入特征映射上采样为期望的输出特征映射。
转置卷积的基本操作解释如下:
1。考虑一个 2×2 编码的特征图,它需要被上采样为 3×3 的特征图。
**图 7。**输入特征地图
**图 8。**输出特征地图
2.我们取一个大小为 2x2 的核,单位步幅,零填充。
**图九。**大小为 2x2 的果仁
3.现在我们取输入特征图的左上元素,并将其与内核的每个元素相乘,如图 10 所示。
图 10。
4.类似地,我们对输入特征图的所有剩余元素都这样做,如图 11 所示。
图 11。
5.如您所见,上采样后的要素地图中的某些元素相互重叠。为了解决这个问题,我们简单地添加重叠位置的元素。
**图 12。**完整的转置卷积运算
6.最终输出将是最终的上采样特征地图,具有所需的 3×3 空间维度。
转置卷积也称为反卷积,这是不恰当的,因为反卷积意味着消除卷积的影响,这不是我们的目标。
它也被称为上采样卷积,对于它用来执行的任务是直观的,即上采样输入特征图。
它也被称为分数步长卷积 due,因为输出的步长相当于输入的分数步长。例如,输出上的步长 2 是输入上的 1/2。
最后,它也被称为后向步长卷积,因为转置卷积中的前向传递相当于正常卷积的后向传递。
转置卷积的问题:
如下所示,转置卷积会受到棋盘效应的影响。
**图 13。**方格状文物(来源
造成这种情况的主要原因是在图像的某些部分出现不均匀的重叠,从而导致伪像。这可以通过使用可被步幅整除的内核大小来固定或减少,例如,当步幅为 2 时,采用 2×2 或 4×4 的内核大小。
转置卷积的应用;
- 超分辨率:
**图十四。**使用转置卷积的超分辨率(信号源)
2.语义分割:
**图 15。**使用转置卷积实现语义分割(来源)
结论:
转置卷积是现代分割和超分辨率算法的支柱。它们提供了抽象表示的最佳和最一般化的上采样。在这篇文章中,我们探讨了所使用的各种上采样技术,然后试图深入了解转置卷积的直观理解。
我希望你喜欢这篇文章,如果你有任何疑问、疑问或评论,请随时通过 Twitter 或 Linkedin 与我联系。
参考文献:
3.深入钻研深度学习
基于用户搜索行为的旅游细分
图片来源:Pixabay
旅游意向,旅游个性化,K-Modes 聚类
在旅行和旅游业中,细分是针对具有不同旅行意图和动机的不同群体开发行程和营销材料的重要策略。它有助于企业了解组成受众的子群体,以便企业能够更好地定制产品和信息。
旅游业的一个警告是,与网上购物不同,休闲旅游是一种不频繁的购买,大多数休闲旅行者每年只旅行一次或两次,活跃的客户要么不常见,要么预订非常慢。
但好的一面是,人们喜欢旅行,我们中的许多人甚至花更多的时间计划假期,而不是理财。因此,如果你是任何在线旅行社的经常用户,你已经将你的数据交给了该公司,以换取该公司向你提供价值。你已经告诉他们关于你自己,你的旅行意图和动机。你期待对你来说更个性化的旅行选择,而不是你今天得到的一长串选择。
在这个细分模型的原型中,我们试图理解用户行为和意图的细微差别,通过旅行对用户的搜索进行细分,即根据共同特征将旅行分成组的过程。
由于各种原因,我不能分享数据集,但请随意使用您自己的适合这个原型的数据集。或者欢迎你查看 Jupyter 笔记本。
数据预处理和特征工程
在数据处理和特征工程阶段,我做了以下决定:
- 我们将按行程对这些搜索进行细分,因此我删除了重复的 trip _ ids。
- 有超过 31%的搜索用户添加了“watch”,我会使用带有“watch_added_dt”的数据,因为:1)。我们对用户了解得越多,我们就能更好地根据他们的兴趣个性化信息。2).当用户添加“观察”时,这表明他(或她)对这次旅行感兴趣,并且希望接收关于航班票价更新的通知。
- 超过 77%的搜索来自美国,我认为首先对这些旅行(来自美国)进行分类是有意义的。因为:1)。我想把美国公共假日作为一个特色。2).来自不同国家/地区的旅行者的旅行行为可能不同。
- 用中间值填充缺失的“停留”。
- 删除缺少“最低价格”的行,因为我认为价格是旅行细分中最重要的元素之一。
- 增加一个新功能——“预订 _ 窗口”,这是“第一次搜索 _dt”和“出发 _ 日期”的区别。
- 增加一个新功能——“路线”,就是“始发地”到“目的地”。
- 增加一个新功能——“假日”,是由“出发 _ 日期”衍生而来的美国公共假日。
表 1
经过处理后,我们要处理的数据包含 269,203 次旅行,有 25,696 条不同的飞行路线和 773 个不同的出发日期,目的地有 178 个国家,如表 1 所示。
观察:
在数据中,第一价格和最低价格相当接近。
大约。其中 0.3%的行程是在同一天出发和返回(即“停留”= 0)。
另外 0.1%的旅行希望尽快离开(即“第一次搜索日期”和“出发日期”是同一天)。
探索性数据分析
描述一次“旅行”的重要因素是什么?让我们重点关注以下特性。
- 周末或工作日出发
76%的搜索行程是在工作日出发。
- 目的地
图 1
该数据集主要由美国境内的旅行目的地决定(超过 68%)。
- 飞行路线
图 2
观察:
·前 20 条搜索路线中有 19 条是在美国境内行驶的。
:前 20 条航线中有 12 条是从纽约到某地,大多是到国内某个温暖的地方,只有一条是从纽约到巴黎。
- 出发那天是不是假期,如果是,什么假期。
图 3
我有一种感觉,用户不太可能在繁忙的假日旅游季节出行,这可能与旺季价格有关。
- 最低价格。当一个用户开始观察一个价格时,他(或她)应该总是以等待最低价为目标,所以我决定使用最低价而不是第一个价格。
图 4
我们可以看到 73%的旅行价格在 100 到 500 美元之间。
- 他们呆多久。
图 5
其中 73%的旅行停留时间在 0 到 7 天之间。
- 预订窗口
图 6
这些旅行中有 55%在 0 到 90 天的预订窗口范围内。
k 模式聚类
标准的 k-means 算法并不直接适用于像这样的分类数据。因此,我将使用 k-modes 算法,该算法使用简单的匹配相异度来处理分类对象。它根据数据点之间匹配类别的数量来定义分类。
为了使用 k-mode,我将三个数字特征转换为分类特征,即:“最低价格”到“最低价格 _bin”,“停留”到“停留 _bin”,“预订 _ 窗口”到“预订 _ 窗口 _bin”,根据上面三个饼状图中的阈值。
图 7
根据图 7,我应该选择 k=3,也就是三个预测段。
识别细分
- 按目的地地区划分行程。
图 8
- 按飞行路线划分行程。
图 9
- 按美国公共假日划分旅行。
图 10
- 按周末和工作日细分。
图 11
- 按部门划分的最低价格分布。
图 12
- 按航段划分的停留时间分布。
图 13
- 按航段划分的订票窗口分布。
图 14
解释结果
段 0:
- 包含 178,519 次出行的最大路段。
- 它们大多是美国国内的航线,还有一些从美国到墨西哥或安的列斯的航线。
- 这些路线中的大多数可能是从纽约或芝加哥到温暖的地方,如加利福尼亚、佛罗里达或内华达。
- 这些旅行可能在工作日出发。
- 平均价格为 310 美元,平均停留时间约为 6 天,用户的平均预订窗口不到 3 个月。
因此,第 0 段中的旅行听起来像是从东部到阳光明媚的目的地的为期一周的假期。
第 1 段 :
- 该航段包含 48,101 次出行。
- 这一部分的旅行主要是国内旅行。
- 这些旅行很可能在周末出发,包括阵亡将士纪念日周末和劳动节周末;他们周末去度假大约 3 天。
- 因为这是一次周末度假,在美国境内的飞行距离可能很短。
- 平均价格是 319 美元。
因此,航段 1 中的旅行大多是相对短途的快速周末度假。他们可以是廉价的,浪漫的或最后一分钟的,或者只是从城市生活的喧嚣中充电。
第二段 :
- 该航段包含 42,583 次出行。
- 该细分市场中的旅行可能是国际旅行,例如从美国到欧洲或亚洲的旅行。它还包括国内旅行,但旅行到夏威夷很远。
- 对于这种类型的旅行,用户倾向于在工作日搜索出发。
- 平均价格为 743 美元,平均停留时间为 14 天左右。
- 用户倾向于更早地计划长途旅行,平均预订时间大约为 4-5 个月。
我们如何使用这些信息?
通过使用上述知识来创建更有效的内容或推荐,企业将能够基于用户的意图为其提供最佳体验。
例如,一旦用户搜索并观看段 1 中的旅行,在线旅行社将知道该用户可能寻找周末度假,在线旅行社然后可以给出关于周末度假的旅行建议或推荐当前在航班或酒店上有促销的其他类似的周末度假目的地。
Jupyter 笔记本可以在 Github 上找到,好好享受这周剩下的时光吧!
参考资料:
http://citeseerx.ist.psu.edu/viewdoc/download doi = 10 . 1 . 1 . 932 . 2724&rep = rep 1&type = pdf
begingroup$ K-Means 的目标是减少类内方差,因为它计算质心作为均值…
datascience.stackexchange.com](https://datascience.stackexchange.com/questions/22/k-means-clustering-for-mixed-numeric-andcategorical-data) [## nicodv/kmodes
k-modes 和 k-prototypes 聚类算法的 Python 实现。依赖 numpy 完成大量繁重的…
github.com](https://github.com/nicodv/kmodes)
https://www . ka ggle . com/ashydv/bank-customer-clustering-k-modes-clustering
第 1 部分:用 Neosemantics 库将 WikiData 导入 Neo4j
旅游的游客
从 WikiData 导入数据并打开街道地图 API,以在 Neo4j 中创建知识图表
在短暂的暑假之后,我准备了一个新的博客系列。在这第一部分,我们将构建一个位于西班牙的古迹知识图表。你可能知道,我最近对通过维基数据 API 获得的知识财富产生了浓厚的兴趣和敬意。我们将继续完善我们的 SPARQL 语法知识,并从 WikiData API 中获取有关西班牙古迹的信息。我之前并没有意识到这一点,但是收集网上可用的 RDF 数据并将其导入 Neo4j 是一个如此受欢迎的话题,以至于Jesus Barrasa博士开发了一个 Neosemantics library 来帮助我们完成这个过程。
在本系列的下一部分,我们将看看 Neo4j 图形数据科学库中可用的寻路算法。
议程
- 安装新语义库
- 图形模型
- 构造 WikiData SPARQL 查询
- 导入 RDF 图
- 使用 OSM API 进行反向地理编码
- 验证数据
安装新语义库
在这个博客系列中,我们将使用标准的 APOC 和 GDS 库,我们只需在 Neo4j 桌面应用程序中单击一下就可以安装它们。最重要的是,我们将把新语义库添加到我们的堆栈中。它用于在 Neo4j 环境中与 RDF 数据进行交互。我们可以将 RDF 数据导入 Neo4j,或者以 RDF 格式导出属性图模型。
为了安装 Neosemantics 库,我们下载了最新版本并将其保存到 Neo4j 插件文件夹中。我们还需要在 Neo4j 配置文件中添加下面一行。
dbms.unmanaged_extension_classes=n10s.endpoint=/rdf
我们现在准备启动我们的 Neo4j 实例。首先,我们需要用下面的 cypher 过程启动新语义配置。
CALL n10s.graphconfig.init({handleVocabUris: "IGNORE"})
查看一下文档中关于配置选项的信息。
图形模型
纪念碑在我们图表的中心。我们将它们的名称和图像的 URL 存储为节点属性。这些纪念碑受到了各种建筑风格的影响,我们将其表示为与建筑节点的关系。我们将把城市和州的纪念碑保存为一个两级分层位置树。
用 draw.io 创建的图形模型
Neosemantics 库需要对标记为 Resource 的节点的属性“uri”进行唯一约束。我们还将为州和城市节点添加索引。apoc.schema.assert
过程允许我们通过一次调用定义许多索引和唯一约束。
CALL apoc.schema.assert(
{State:['id'], City:['id']},
{Resource:['uri']})
构造 WikiData SPARQL 查询
对我来说,构建新的 SPARQL 查询最简单的方法是使用 WikiData 查询编辑器。它有一个可爱的自动完成功能。它还有助于查询调试。
我们想要检索位于西班牙的古迹的所有实例。我发现在维基数据上找到各种实体最简单的方法就是简单地使用谷歌搜索。然后,您可以在网站上检查该实体的所有可用属性。我第一次看到的 SPARQL 查询如下所示:
SELECT *
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image }
WHERE
子句中的前两行定义了我们要寻找的实体:
// Entity is an instance of monument entity
?item wdt:P31 wd:Q4989906 .
// Entity is located in Spain
?item wdt:P17 wd:Q29 .
接下来,我们还确定我们感兴趣的实体的属性。在我们的例子中,我们希望检索纪念碑的名称、图像、位置和建筑风格。如果我们在查询编辑器中运行这个查询,我们会得到以下结果。
我们已经在 SPARQL 查询的WHERE
子句中定义了想要检索的信息。在用新语义导入数据之前,我们需要对数据格式进行一些调整。第一个也是最关键的是将SELECT
条款改为CONSTRUCT
。这样,我们将得到返回的 RDF 三元组,而不是信息表。你可以在这篇由马克·李约瑟写的博客中读到更多关于SELECT
和CONSTRUCT
的区别。
有了 Neosemantics 库,我们可以预览使用n10s.rdf.preview.fetch
过程后我们存储的图模型会是什么样子。我们将从检查一个空的CONSTRUCT
语句的图表模式开始。
WITH '
CONSTRUCT
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label
?architectureName .
?item wdt:P18 ?image} limit 10 ' AS sparql
CALL n10s.rdf.preview.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD nodes, relationships
RETURN nodes, relationships
结果
一个问题是节点没有标签。您可能还会注意到,关系类型提供的信息并不多,因为如果您不知道 WikiData 属性映射,P149 或 P31 没有多大意义。另一件事情是,图像的 URL 被存储为一个单独的节点,这一点在这个可视化中并不明显。如果您还记得之前的图形模型,我们决定将图像 URL 保存为 monument 节点的属性。
我不会讲太多细节,但是在CONSTRUCT
子句中,我们可以定义我们的图表模式在 Neo4j 中应该是什么样子。我们还使用以下语法定义了要将图像的 URL 保存为纪念碑节点的属性,而不是单独的节点:
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr)
我们现在可以用更新后的CONSTRUCT
语句预览新的查询。
WITH ' PREFIX sch: <http://schema.org/>
CONSTRUCT{ ?item a sch:Monument;
sch:name ?monumentName;
sch:location ?location;
sch:img ?imageAsStr;
sch:ARCHITECTURE ?architecture.
?architecture a sch:Architecture;
sch:name ?architectureName }
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr) } limit 100 ' AS sparql CALL n10s.rdf.preview.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD nodes, relationships
RETURN nodes, relationships
结果
导入 RDF 图
现在,我们可以将图表导入 Neo4j。我们使用n10s.rdf.import.fetch
而不是n10s.rdf.preview.fetch
过程,并保持查询的其余部分不变。
WITH 'PREFIX sch: <http://schema.org/>
CONSTRUCT{ ?item a sch:Monument;
sch:name ?monumentName;
sch:location ?location;
sch:img ?imageAsStr;
sch:ARCHITECTURE ?architecture.
?architecture a sch:Architecture;
sch:name ?architectureName }
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr) }' AS sparql
CALL n10s.rdf.import.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD terminationStatus, triplesLoaded
RETURN terminationStatus, triplesLoaded
让我们从一些探索性的图查询开始。我们将首先计算图表中纪念碑的数量。
MATCH (n:Monument)
RETURN count(*)
我们已经将 1401 个纪念碑导入到我们的图表中。我们将继续计算按建筑风格分组的纪念碑的数量。
MATCH (n:Architecture)
RETURN n.name as monument,
size((n)<--()) as count
ORDER BY count DESC
LIMIT 5
结果
罗马式和哥特式建筑风格影响了大多数纪念碑。当我探索 WikiData 时,我注意到一种架构风格可以是其他架构风格的子类。作为练习,我们将把架构层次关系导入到我们的图表中。在我们的查询中,我们将迭代存储在图中的所有架构风格,并从 WikiData 中获取任何父架构风格,并将其保存回我们的图中。
MATCH (a:Architecture)
WITH ' PREFIX sch: <http://schema.org/>
CONSTRUCT { ?item a sch:Architecture;
sch:SUBCLASS_OF ?style.
?style a sch:Architecture;
sch:name ?styleName;}
WHERE { filter (?item = <' + a.uri + '>)
?item wdt:P279 ?style .
?style rdfs:label ?styleName
filter(lang(?styleName) = "en") } ' AS sparql
CALL n10s.rdf.import.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD terminationStatus, triplesLoaded
RETURN terminationStatus, triplesLoaded
我们现在可以看一些架构层次的例子。
MATCH (a:Architecture)-[:SUBCLASS_OF]->(b:Architecture)
RETURN a.name as child_architecture,
b.name as parent_architecture
LIMIT 5
结果
似乎现代主义是新艺术的子范畴,新艺术是装饰艺术的子范畴。
空间丰富
起初,我想在维基数据中包含一些市政信息,但是事实证明,这些信息相对较少。不过不用担心,我后来意识到我们可以使用反向地理编码 API 来检索这些信息。APOC 有专门的反向地理编码程序。默认情况下,它使用开放的街道地图 API,但我们也可以定制它来与其他提供商合作。查看文档了解更多信息。
首先,我们必须将位置信息转换为空间点数据类型。
MATCH (m:Monument)
WITH m,
split(substring(m.location, 6, size(m.location) - 7)," ") as point
SET m.location_point = point(
{latitude: toFloat(point[1]),
longitude: toFloat(point[0])})
查看 OSM 反向地理编码 API 的示例响应。
MATCH (m:Monument)
WITH m LIMIT 1
CALL apoc.spatial.reverseGeocode(
m.location_point.latitude,
m.location_point.longitude)
YIELD data
RETURN data
结果
{
"country": "España",
"country_code": "es",
"isolated_dwelling": "La Castanya",
"historic": "Monument als caiguts en atac Carlista 1874",
"road": "Camí de Collformic a Matagalls",
"city": "el Brull",
"municipality": "Osona",
"county": "Barcelona",
"postcode": "08559",
"state": "Catalunya"
}
开放街道地图 API 有点有趣,因为它在城市、城镇和村庄之间有所不同。此外,位于加那利群岛的纪念碑没有国家可用,但却是加那利群岛的一部分。我们将把群岛作为一个州,把城市、城镇和村庄放在一个标签下。出于批处理的目的,我们将使用apoc.periodic.iterate
程序。
CALL apoc.periodic.iterate(
'MATCH (m:Monument) RETURN m',
'WITH m
CALL apoc.spatial.reverseGeocode(
m.location_point.latitude,m.location_point.longitude)
YIELD data
WHERE (data.state IS NOT NULL OR
data.archipelago IS NOT NULL)
MERGE (s:State{id:coalesce(data.state, data.archipelago)})
MERGE (c:City{id:coalesce(data.city, data.town,
data.village, data.county)})
MERGE (c)-[:IS_IN]->(s)
MERGE (m)-[:IS_IN]->(c)',
{batchSize:10})
该查询将花费很长时间,因为默认的限制延迟设置是 5 秒。如果您没有时间等待,我已经将空间结果保存到 GitHub 中,您可以在不到五秒钟的时间内使用以下查询轻松导入它们。
LOAD CSV WITH HEADERS FROM "https://raw.githubusercontent.com/tomasonjo/blogs/master/Traveling_tourist/traveling_tourist_cities.csv" as row
MATCH (m:Monument{uri:row.uri})
MERGE (c:City{id:row.city})
MERGE (s:State{id:row.state})
MERGE (m)-[:IS_IN]->(c)
MERGE (c)-[:IS_IN]->(s);
我们将首先检查是否有任何遗迹的空间价值缺失。
MATCH (m:Monument)
RETURN exists ((m)-[:IS_IN]->()) as has_location,
count(*) as count
结果
我们已经检索了几乎所有遗迹的空间信息。创建位置层次结构树时需要注意的是,树中的每个节点与其父节点只有一个传出关系。如果这个规则被打破,位置树的结构完整性将丢失,因为一些实体将具有不止一个位置。查看我的位置树帖子获取更多关于如何绕过这个问题的信息。
MATCH (c:City)
WHERE size((c)-[:IS_IN]->()) > 1
RETURN c
幸运的是,我们在这里没有遇到这个问题。我们现在可以探索我们的空间丰富的结果。我们将看看位于加泰罗尼亚的按照建筑风格分组的古迹数量。
MATCH (s:State{id:'Catalunya'})<-[:IS_IN*2..2]-(:Monument)-[:ARCHITECTURE]->(architecture)
RETURN architecture.name as architecture,
count(*) as count
ORDER BY count DESC
LIMIT 5
结果
让我们快速看一下维基百科对教育目的的乡土建筑的定义。
乡土建筑是以使用当地材料和知识为特征的建筑,通常没有专业建筑师的监督。
我们还可以看看各州最常见的纪念碑建筑风格。我们将使用 Neo4j 4.0 中引入的子查询语法。
MATCH (s:State)
CALL {
WITH s
MATCH (s)<-[:IS_IN*2..2]-()-[:ARCHITECTURE]->(a)
RETURN a.name as architecture, count(*) as count
ORDER BY count DESC LIMIT 1
}
RETURN s.id as state, architecture, count
ORDER BY count DESC
LIMIT 5
结果
结论
如果你遵循了这篇文章中的步骤,你的图表应该看起来像上面的图片。我总是对只用 cypher 从各种 API 获取数据是如此容易印象深刻。如果您想调用任何自定义端点,您仍然可以使用apoc.load.json
过程。
在下一部分,我们将深入研究寻路算法。与此同时,试试 Neo4j ,加入 Twin4j 简讯。
和往常一样,代码可以在 GitHub 上获得。
将线性回归视为不仅仅是一个黑盒
超越线性回归()。拟合(x,y)
线性回归是最基本的机器学习模型之一,但它的应用无处不在:从预测房价到预测电影收视率。即使当你坐出租车去上班时,你也有一个线性回归问题要处理。
简单来说,线性回归是指预测值和特征值之间的线性关系。线性回归的数学方程如下:
其中 Y 为预测值, θ₀ 为偏差参数, θ₁ 为参数值, x 为特征值。
为了说明这些参数的含义,假设您乘坐出租车去上班。你进入出租车后不久,出租车费就不会从 0 开始了。而是从一些值开始,比如说 5 美元。这 5 美元代表回归方程中的偏差项( θ₀ )。预测值 Y 是你应该支付的价格,特征值 x 是行驶距离,***【θ₁】***是随着行驶距离的增加你需要多支付多少的斜率。
让我们在出租车费用和出行距离之间生成一个随机的线性外观数据,以形象化上面的图示。
import numpy as npnp.random.seed(1)
x = 2 * np.random.rand(100,1)
y = 4+ 2 * x + np.random.rand(100,1)
出租车费和旅行距离之间有一个线性趋势。因此,我们可以将出租车费和出行距离作为线性函数进行关联:
现在的问题是我们如何获得***【θ₀】******【θ₁】***的最优值,使得我们的线性回归模型表现最佳?为此我们可以使用 Scikit-learn。
用 Scikit-learn 构建线性回归模型
如果您有一个线性回归问题要解决,就像上面的研究案例一样,找到 θ₀ 和 θ₁ 的最优值的最简单方法是通过调用 Scikit-learn 库中的LinearRegression
函数。这个问题可以用四行代码解决,如下所示:
from sklearn.linear_model import LinearRegressionlm = LinearRegression()#train the model
lm.fit(x,y)print(lm.intercept_, lm.coef_)## Output: [4.49781698] [[1.98664487]]
由此我们可以得出出租车费和出行距离之间的关系如下:
有了这个关系,现在你可以估算出你的位置和目的地之间的距离,现在你可以预测你应该付多少钱的出租车费。
线性回归不仅仅是一个黑箱
机器学习库可以让我们的生活更容易解决不同的问题,这令人惊叹。仅用四行代码,您就已经优化了一个线性回归模型,而无需了解任何内幕。
虽然这些机器学习库令人惊叹,但它们肯定为你隐藏了一些实现细节,例如,我们将线性回归问题视为黑盒。
知道引擎盖下是什么以及 Scikit-learn 如何提出解决方案*taxi _ fares = 4.49+1.98 * travel _ distance 岂不是太棒了?*他们怎么知道 4.49 是偏差项的最佳值,1.98 是斜率的最佳值?
如果我们知道在引擎盖下发生了什么,我们将对在任何给定问题中使用的适当模型有深刻的理解,并且我们可以更有效地调试错误。另外,对回归问题如何工作有一个很好的理解将是我们理解神经网络算法背后的数学和直觉的一个很好的基础。
要找到线性回归问题的最佳模型,通常有两种方法:
- 使用称为梯度下降的迭代优化方法。
- 使用“封闭形式”的方程称为正规方程。
线性回归的梯度下降
简而言之,梯度下降算法通过调整模型参数 θ (偏差项和斜率)来迭代地最小化关于训练数据的误差或成本函数,从而找到线性回归问题的最优解。
让我们形象化地描述一下梯度下降是如何工作的,以使其更加清晰。
梯度下降从随机初始化参数值 θ 的初始值开始。然后,这个 θ 值将在每次迭代中被调整,使得误差或成本函数被最小化。一旦梯度下降算法找到 θ 最小值,代价函数就不会再变小。这意味着算法已经收敛。
需要注意的是,并非所有的成本函数都有一个完美的碗形,如上图所示。大多数情况下,成本函数有各种不规则的形状和平台,如下所示:
成本函数的形状不规则性使得梯度下降算法很难收敛,因为该算法很有可能会陷入局部最优而无法找到全局最优。
幸运的是,线性回归问题中的成本函数总是凸的,这意味着没有局部最优解,只有一个全局最优解。因此,无论你如何初始化你的初始值,几乎可以保证梯度下降算法将在全局最小解中结束。
但问题是,如果没有局部最优解,为什么“几乎”可以保证梯度下降最终会达到全局最小?
因为梯度下降中有一个重要的参数你需要提前定义,就是学习率。
学习率决定了算法在每次迭代中的步长。如果学习率太小,很有可能在迭代结束时达不到全局最小值。如果学习率太高,算法将在每次迭代中围绕谷值跳跃,并且算法将发散。
学习率过低(左)和过高(右)
梯度下降的实现
在所有关于梯度下降的解释之后,让我们使用研究案例“出租车费 vs 旅行距离”来实现它。
对于线性回归问题,我们应该最小化梯度下降算法的代价函数是均方误差(MSE)。
其中:
在上面的数学符号中,是代价函数, m 是观测值或数据的个数, hθ 是来自线性回归的预测值, θ₀ 是偏差项, θ₁ 是斜率, x 是特征值我们可以用代码实现上述等式:
*def computeCostFunction(x, y, theta, num_observations):
linear_function= np.matmul(x,theta)
error = np.subtract(linear_function,y)
error_squared = error.transpose()**2
cost_function = (0.5/num_observations)*(np.sum(error_squared))
return cost_function*
下一个重要步骤是计算每个模型参数的成本函数的梯度。这种梯度背后的直觉是,如果你调整参数***【θⱼ】*的值,成本函数将改变多少。
对于均方误差,其偏导数有一个封闭形式的解。所以,如果你不是微积分专家,这不是问题。
一旦你知道了每个参数 θⱼ 的梯度,现在你可以更新每个参数 θⱼ 进行一次迭代。这就是学习率 α 在梯度下降算法中发挥作用的地方。
最后,我们可以用代码实现上述等式:
**intercept_x = np.ones((len(x),1)) #add bias term
x = np.column_stack((intercept_x,x))alpha = 0.05 #learning rate
iterations = 25000
theta = np.random.randn(2,1) #random initialization of parameters
num_observations = 1000def gradientDescent(x, y, theta, alpha, num_observations, iterations):
cost_history = np.zeros((iterations,1))
for i in range (iterations):
linear_function= np.matmul(x,theta)
error = np.subtract(linear_function,y) gradient = np.matmul(x.transpose(),error)
theta = theta - (alpha * (1/num_observations) * gradient)
cost_history[i] = computeCostFunction(x, y, theta, num_observations)
return theta, cost_historytheta, cost_history = gradientDescent(x, y, theta, alpha, num_observations, iterations)print(theta)#Output: array([[4.49781697],[1.98664487]])**
梯度下降的输出看起来与用 Scikit-learn 获得的输出相同。不同的是,你现在知道什么是引擎盖下。
有一种更直接的方法可以找到线性回归问题的最优解,那就是正规方程。
正规方程
在线性回归中,正规方程是找到参数最佳值的封闭解。使用这种方法,没有迭代方法,没有偏导数,也没有参数更新。一旦你计算了正规方程,你就完成了,你得到了线性回归模型的最优解。
上式中, θ 为回归参数的最优值, x 为特征, Y 为预测值。我们可以用代码实现上面的等式:
**def normalEquation(x, y):
x_transpose = x.transpose()
x = np.matmul(x_transpose, x)
x_inverse = np.linalg.inv(x)
x_final = np.matmul(x_inverse,x_transpose)
theta = np.matmul(x_final,y)
return thetatheta = normalEquation(x, y)print(theta)#Output: array([[4.49781698],[1.98664487]])**
同样,结果与我们从 Scikit-learn 库和梯度下降优化中得到的结果相匹配。现在你可能会问一个问题:如果法方程非常简单,那么我们为什么不应该用它来代替梯度下降呢?让我们讨论一下。
正规方程还是梯度下降?
在解决线性回归问题时,正规方程和梯度下降都有各自的优点和缺点。
利用正规方程,可以直接求解线性回归问题,不需要初始化初始参数值 θⱼ ,也不需要计算偏导数。所有这些都可以通过矩阵矢量化来完成。这使得一切都很快,即使观察或训练数据的数量很大。此外,您不需要对法线方程的每个参数进行归一化来得出最优解。
使用正规方程的主要缺点是它的计算复杂性。这是因为我们需要用这种方法求矩阵的逆矩阵。如果你有一个多元线性回归问题,有大量的特征可供选择,法方程的计算成本将非常昂贵。
使用梯度下降,如果在多元线性回归问题中有大量的特征选择,计算复杂性就不是问题。
然而,如果你有大量的观察或训练数据,梯度下降会变得很慢。这是因为在每次迭代中,梯度下降使用所有的训练数据来计算成本函数。
此外,您始终需要归一化或缩放每个要素,以使梯度下降收敛得更快。下图显示了归一化特征和忘记归一化特征的情况。
线性回归中的归一化要素(左)和非归一化要素(右)
如果对要素进行归一化,线性回归中的成本函数会形成一个完美的碗形,从而加快算法的收敛速度。同时,如果您忘记对要素进行归一化或它们不共享相同的比例,则会形成拉长的碗形。该算法最终仍会以全局最小解结束,但需要更长的时间来收敛。
现在你知道使用法方程和梯度下降算法的优缺点了。作为提示,下表总结了这两种算法的性能。
如何提高梯度下降的性能?
梯度下降的主要缺点是,它使用全部训练数据来计算每次迭代的成本函数。如果我们有大量的观察或训练数据,这将是有问题的。为了解决这个问题,通常采用随机梯度下降(SGD)或小批量梯度下降。
在 SGD 算法中,该算法不是使用整个训练样本,而是在每一步的训练样本中随机选取一个实例。然后,将基于这个随机实例计算梯度。这使得计算时间更快,成本更低,因为在每一步中只有一个实例需要在内存中。
在小批量梯度下降中,不是使用整个训练示例(如梯度下降)或使用随机实例(如 SGD ),而是基于称为小批量的随机实例的小子集来计算梯度。将整个训练数据分成几个小的小批量使得矩阵运算和计算过程更快。
对于简单或多元线性回归问题,通常使用梯度下降就足够了。然而,对于更复杂的应用,如神经网络算法,小批量梯度下降将是首选。
A/B 测试中的处理分配策略
A/B 测试中常见处理分配策略的问题及克服方法概述。
在我之前的帖子中,我给出了规划 A/B 测试的一步一步的过程,但是没有涉及实现部分。本周,我将重点关注 A/B 测试实现的一个组成部分——治疗分配。
有效的随机对照实验的要求之一是随机处理分配。尽管它很重要,但不幸的是,这也是大多数人似乎不太关心的部分。也许因为随机的概念看起来很简单,我只是随机地将我的实验对象分配到控制组和治疗组,就这样,对吗?在商业环境中,实际情况远非如此,因为有些需求使得随机性难以实现。
在这篇博文中,我将试图强调其中一些需求,人们用来满足这些需求的常用策略,这些策略带来的问题,以及最后一个满足所有需求的简单而优雅的解决方案。
一致的治疗分配
给予实验对象的治疗每次都必须是一致的。
我在这里举几个例子:
- 如果网飞想要衡量其新推荐引擎的效果,那么每个用户(实验对象)都应该像以前一样从相同的推荐引擎接收推荐。
- 如果我想测试一个新的移动应用设计是否有更快的加载时间,那么每个设备(实验对象)每次都需要加载相同的移动应用设计。
如果我们在每次实验对象暴露时随机分配治疗,那么我们将无法保证这一点。(例如,用户 A 可能每次都登录并看到不同的处理。)
为了便于解释,并且考虑到大多数实验都是以用户为实验对象的事实,从今以后,我将把用户视为实验对象。
可量测性
满足第一个要求是相当容易的,我们只需记录下哪个用户第一次接触实验时接受了哪种治疗。在后续的登录或者后续的曝光中,我们只是查找这个用户过去的待遇,分配相同的待遇。
这对于一个小公司来说可能是可行的,但是随着你规模的扩大,你会发现这种方法根本不具备很强的可扩展性。我们可以想象,在我们有几千万用户,几百次实验的情况下,要做到这一点几乎是不可能的。
为了做到真正的随机,跨实验的分配应该是独立的
为了解决上述两个问题,公司常用的策略之一是利用用户 ID 的最后一个字符或数字。在这种情况下,治疗将是一致的和完全可扩展的,因为治疗仅依赖于固定的用户 ID。
然而,这带来了另一个问题。这个实验本身确实是随机的。但是随着我们做越来越多的实验,我们会发现不同实验的治疗任务现在是相互关联的。
这里举个例子。
+-------------------+-----------------+-------------------+-------+
| | **Exp 1 : Control** | **Exp 1 : Treatment** | **Total** |
+-------------------+-----------------+-------------------+-------+
| **Exp 2 : Control** | 2107 | 2929 | **5036** |
| **Exp 2 : Treatment** | 2916 | 2048 | **4964** |
| **Total** | **5023** | **4977** | **10000** |
+-------------------+-----------------+-------------------+-------+
想象一下,如果我们有 10,000 个用户,我们做了两个实验。在第一个实验(实验 1)中,我们测试了订阅费的减少是否会增加付费会员的转化率,并发现了一个显著的结果。在第二个实验(实验 2)中,我们测试了一个额外的特性是否会增加用户的购买次数,结果发现这种影响在统计上并不显著。
以下是可能出现这种情况的原因:
- 付费用户会因为打折而购买更多的东西。
- 实验 1 中处理组的更多用户被分配到实验 2 中的控制组,这意味着实验 2 控制组有更多的付费用户。
- 如果两组都服用安慰剂,实验 2 对照组的用户自然会比治疗组购买更多的东西。
- 因此,即使实验 2 中的额外特征确实增加了购买的数量,我们也不能准确地测量效果,因为实验 2 中的处理分配与实验 1 中的处理分配相关。
为了真正随机,在不同实验中的处理分配之间不应该有相关性。
这种相关性多久发生一次?
为了检验这一点,我执行了以下模拟。
- 生成 10,000 个用户 id,其中每个 id 是长度为 10 的十六进制字符串(例如: d3ef2942d7 )。
import pandas as pd
import randomexp_df = pd.DataFrame(['%010x' % random.randrange(16**10) for x in range(10000)], columns=['user_id'])
2.创建 20 个实验。对于每个实验,从 16 个可能值(0 - 9,a - f)中随机选择 8 个作为对照。如果用户 ID 的最后一个字符在控制中,则将他们分配到控制组,否则,将他们分配到处理组。
import numpy as np# number of experiments
j = 20# get all 16 possible values
random_list = exp_df['user_id'].apply(lambda x: x[-1]).unique()control_list = [set(np.random.choice(random_list, 8, replace=False)) for x in range(j)]
treatment_list = [set(random_list) - x for x in control_list]
for k in range(j):
exp_df[f'exp_{k}'] = exp_df['user_id'].apply(lambda x: x[-1]).isin(control_list[k]).map({True:'control', False: 'treatment'})
结果数据帧的外观示例。
3.对于每一对实验,生成如上例所示的列联表,并进行卡方检验。
from scipy import stats# initialize list to store chi-square results all experiment pairs
chi2_res = []for cols in combinations(exp_df.columns[1:], 2): target_cols = list(cols)# generate contingency table
aggregate_df = exp_df[target_cols]\
.groupby(target_cols)\
.size()\
.to_frame()\
.reset_index()\
.pivot(index=target_cols[1],
columns=target_cols[0])# store chi-square test result
chi2_res.append(stats.chi2_contingency(aggregate_df)[1])# number of pairs that fail the chi-square test at alpha = 0.01
print((np.array(chi2_res) < 0.01).sum())
直观地说,如果处理分配是真正独立的,我们应该期望看到以下情况(假设 50%的样本分配给对照,50%分配给处理)。
+-------------------+-----------------+-------------------+-------+
| | **Exp 1 : Control** | **Exp 1 : Treatment** | **Total** |
+-------------------+-----------------+-------------------+-------+
| **Exp 2 : Control** | 2500 | 2500 | **5000** |
| **Exp 2 : Treatment** | 2500 | 2500 | **5000** |
| **Total** | **5000** | **5000** | **10000** |
+-------------------+-----------------+-------------------+-------+
卡方检验告诉我们实际计数离独立案例有多远。距离越远,卡方检验统计值越高,因此有更强的证据表明两个实验的处理分配之间存在某种关联。
我将上面的模拟运行了 100 次,看看在每个模拟中有多少对实验没有通过卡方检验,得到了平均值62%(190 对中的 118 对)!换句话说,如果我们使用这种方法,平均来说,我们将观察到 62%的实验对没有通过卡方检验。我还尝试了 5 次或 10 次实验(而不是最初的 20 次),得到了类似的结果(约 60%)。
在 100 次模拟中未通过卡方检验的实验对数量的分布。
奋力营救
满足上述所有要求的解决方案实际上非常简单。我们所要做的就是使用一个散列函数。散列函数是可以用来将任意大小的数据映射到固定大小的值的任何函数。该属性确保满足前两个要求(一致性和可伸缩性)。
为了满足最终的需求,我们只需要散列用户 ID 和实验 ID 的连接,而不是只散列用户 ID。
下面是如何使用 python 实现这一点的示例。
import mmh3def treatment_assignment(user_id, exp_id, control_bucket):
# calculates the number of buckets
num_buckets = len(control_bucket) * 2
# this generates a 32 bit integer
hash_int = mmh3.hash(user_id + exp_id)
# get the mod of the 32 bit integer
mod = hash_int % num_buckets
if mod in control_bucket:
return 'control'
else:
return 'treatment'
# create 50 random integer as control group
control_bucket = np.random.choice(np.arange(0,100,1), 50, replace=False)
treatment_assignment('d3ef2942d7', 'exp_1', control_bucket)
根据您的控制桶和实验 ID,上述函数将返回treatment
或control
。我们也可以把它扩展到两个以上变量的实验。
我用这种方法再次运行了上面的模拟,下面是结果。
模拟 190 个实验对中未通过卡方检验(α= 0.01)的实验对的数量。
我们可以看到,100 次模拟中的 73 次导致 2 对或更少的实验(190 对中)无法通过卡方检验,这证明这是一种分配治疗的健壮方法,而不违反任何要求。
结论
在这篇博文中,我详细概述了实验中处理分配的要求。我列出了我见过的一些常见的实践,并陈述了它们为什么违反了一些需求。违反任何要求都意味着实验结果不再有效,这就是为什么我们应该开始更多地关注治疗方案。
最后,我给出了一个简单的解决方案,它满足了适用于两个以上变量实验的所有要求。
参考
[1]好哈希难求(offer up)
https://blog . offer up . com/A-good-hash-is-hard-to-find-60 E8 A 201 E8 ce
树算法讲解:球树算法 vs. KD 树 vs .蛮力
了解最近邻搜索的结构化数据算法背后的内容
来源: pixabay 。
这三种算法都用于最近邻搜索。球树和 KD 树算法是用于数据点的空间划分和它们到特定区域的分配的树算法。换句话说,它们用于在多维空间中构建数据。
不过先从底层说起: 为什么叫树算法?什么是树?— 跳过,如果你已经知道了!
一棵树是一种结构化数据的分层方式。由于存在诸如队列的线性数据结构,其中数据被一个接一个地分配,所以树是一种常见的数据结构。树被应用于计算机科学的许多不同领域,从图形、数据库到操作系统。它们不仅与自然界中的植物学朋友有共同的名称,而且还有一些特征。作为植物树,计算机科学中的树有根、叶和分支。然而,与普通树相比,这些部分的分配是自底向上的。根在树顶,叶在下端。
树的结构。
父节点= 是另一个节点之上的节点,例如,根节点是其下内部节点的父节点
子节点 =顾名思义,父节点的子节点,以及父节点下的子节点。子节点可以再次成为下面节点的父节点。
根节点 =最上面的节点,树的原点
叶节点 =也称为外部节点,可以看作是一个“死胡同”,它是最后一个节点,下面没有子节点
内部节点 =也称为内部节点或分支节点。它是一个上下都有连接的节点(子节点和父节点)
你还没上媒体?每月仅需 4.16 美元。
开始使用
为了更好地理解这在计算机科学话题中的表现,你可以在下面找到一段 HTML 代码。树有助于构建网站,网站通常可以用树来描述。
**<html>
<head>**
**<meta charset=utf-8" />**
**<title>**Ball Tree vs. KD Tree**</title>**
**<nav>**
**<a href="/r/">**R**</a>**
**<a href="/js/">**JavaScript**</a>**
**<a href="/python/">**Python**</a>**
**</nav>**
**</head>****<body>**
**<h1>**What is a tree?**</h1>**
**<ul>**
**<li>**List item one**</li>**
**<li>**List item two**</li>**
**</ul>**
**<h2>**How does a tree look like?**</h2>**
**</body>**
**</html>**
html 代码的树形可视化。
KD-Tree 算法和 Ball 算法都是构建这种树的二进制算法。在这个上下文中,二进制意味着每个父节点只有两个子节点。典型地,该算法应用于最近邻搜索。
球树算法
球树算法可以被视为一个度量树。度量树考虑数据点所在的度量空间来组织和构造数据点。使用度量标准,点不必是有限维的或在向量中(Kumar,Zhang & Nayar,2008)。
该算法将数据点分成两个聚类。每个集群被一个圆(2D)或一个球(3D)包围。这个球通常被称为超球。
“一个超球是距离称为其中心的给定点恒定距离的点集。”— 维基百科
从簇的球体形式,衍生出名称 球树算法 。每个簇代表树的一个节点。让我们看看算法是如何执行的。
选择的子节点之间的距离最大,通常在树的每一层使用以下结构。
首先,设置整个数据点云的质心。与质心距离最大的点被选为第一个群集和子节点的中心。离第一聚类中心最远的点被选为第二聚类的中心点。然后,将所有其他数据点分配给距离中心最近的节点和聚类,可以是聚类 1 或聚类 2。任何点只能是一个集群的成员。球体线可以彼此相交,但是这些点必须明确地分配给一个簇。如果一个点正好在两个中心的中间,并且随后到两边的距离相同,则该点必须被分配到一个聚类中。集群可能不平衡。这基本上是球树算法背后的概念。将数据点分成两个群/球的过程在每个群内重复,直到达到定义的深度。这导致嵌套集群包含越来越多的圆。
球树算法的可视化。
如上图所示,树的深度为 2。质心 1 是算法的开始。一个球体(2D)被放置在所有数据点(灰色)周围。从中心开始,选择聚类的最远点,这里是数字 3 或数字 9。这是星团 1 的新中心,这里是紫色星团的 3 号。离第三个点最远的点是聚类 2 的中心。这是橙色星团的 9 号。然后,包括紫色球体的所有数据点被考虑用于新质心 2 的计算。对位于橙色球体中的所有数据点进行同样的操作,得到质心 3。最远的点再次成为新群的中心。数据点编号 3 是距离质心 2 和新簇中心最远的点。
在质心 2 的另一侧,它和数据点 1 之间的最大距离 ist。它是第二个集群的中心。然后对橙色侧也执行该步骤,再次产生两个集群。然而,橙色的一面是不平衡的。
生成的树如下所示(M 是质心为 1 的球体和包含所有数据点的起始球体)。从那里,集群被划分为深度为 2:
产生的球树。
KD 树算法
KD 树算法是最常用的最近邻算法之一。数据点在每个节点被分成两组。像前面的算法一样,KD 树也是一个二叉树算法,总是以最多两个节点结束。选择的分割标准通常是中间值。在下图的右侧,您可以看到数据点的确切位置,左侧是它们的空间位置。
数据点及其在坐标系中的位置。
KD 树算法首先使用第一轴的中值,然后在第二层中使用第二轴的中值。我们从 x 轴开始,
按升序排序的 x 值是:1,2,3,4,4,6,7,8,9,9。接下来,中位数是 6。
数据点然后被分成较小的和较大的,等于 6。这导致(1,2) (2,3) (3,4) (4,5) (4,6)在左侧,而(6,5) (7,9) (8,7) (9,6) (9,1)在另一侧的簇上。在坐标系中画出 6 的中值,就可以看到这两个可视化的集群。
将 6 的 x 中值绘制到坐标系中。
现在我们要用 Y 轴。我们已经有了两个集群,所以我们需要分别查看它们。在左边,我们得到了排序后的 y 值:2,3,4,5,6。中位数是 4。这会在值 4 处产生一条分隔线,坐标系如下所示:
中间值 4 分隔 X 中间值(=6)左侧的数据点。
这些值以 4 分隔,第一个分类包含(2,3) (1,2)。第二组包含点(4,6) (3,4) (4,5)。
在 x-median 的另一边,4 是目前的 5 个点(包括点(5,6))。按照排序顺序,y 值为 6,7,8,9,9。这导致中值为 8,并且第一个聚类包含(9,1)和(6,5)。第二个聚类包含(8,7)(7,9)(9,6)。
最终的坐标系如下所示。数据点被分成深度为 2 (X 和 Y)的 4 个聚类。
最终的空间分离。
但是,这棵树看起来像什么?让我们看看结果树是如何划分的。
KD 树。
比较和总结
由于考虑了所有数据点,暴力可能是最准确的方法。因此,没有数据点被分配给错误的聚类。对于小数据集,蛮力是合理的,然而,对于增加的数据,KD 或球树是更好的选择,因为它们的速度和效率。
KD 树及其变体可以被称为“投影树”,这意味着它们基于点在一些低维空间中的投影来对点进行分类。(库马尔、张和纳亚尔,2008 年)
对于低维数据,KD 树算法可能是最好的解决方案。如上所述,KD 树的节点划分是轴对齐的,不能采取不同的形状。所以分布可能没有正确映射,导致性能下降。
对于高维空间,球树算法可能是最好的解决方案。它的性能取决于训练数据的数量、维度和数据的结构。由于没有清晰的结构,具有许多噪声数据点也可能导致糟糕的性能。
感谢您的阅读,希望您喜欢!
请继续关注 Marius Hucker 的新文章。如果您还没有注册,您将创建一个中型帐户…
medium.com](https://medium.com/subscribe/@hucker.marius)
参考文献
k 近邻(KNN)是一个简单的机器学习算法,可用于分类和…
github.com](https://github.com/JKnighten/k-nearest-neighbors/wiki/KNN-and-BallTree-Overview) [## 球树深入浅出的解释——Linux 大叔
2015 年 1 月 20 日在这篇博客和接下来的几篇博客中,我将讨论 k-近邻(k-NN)算法的实现…
ashokharnal.wordpress.com](https://ashokharnal.wordpress.com/tag/ball-tree-explained-in-simple-manner/) [## Python 中最近邻搜索的基准测试
我最近提交了一个 scikit-learn pull 请求,其中包含一个全新的球树和快速最近邻的 kd 树…
jakevdp.github.io](https://jakevdp.github.io/blog/2013/04/29/benchmarking-nearest-neighbor-searches-in-python/)
树增强混合效应模型
GPBoost:结合树提升和混合效果模型
本文展示了如何使用 GPBoost 算法将树提升(有时也称为“梯度树提升”)与混合效果模型结合起来。提供了方法论以及如何使用 Python 应用 GPBoost 库的背景知识。我们展示了如何(I)训练模型,(ii)调整参数,(iii)解释模型,以及(iv)进行预测。此外,我们还比较了几种可供选择的方法。
介绍
树提升凭借其众所周知的实现,如 XGBoost、LightGBM 和 CatBoost,被广泛用于应用数据科学。除了最先进的预测准确性之外,树提升还具有以下优势:
- 非线性、不连续性和复杂高阶相互作用的自动建模
- 对预测变量中的异常值和多重共线性具有稳健性
- 预测变量单调变换的尺度不变性
- 预测变量中缺失值的自动处理
混合效应模型是一种用于聚类、分组、纵向或面板数据的建模方法。其中,它们的优势在于,它们允许更有效地学习为回归函数选择的模型(例如,线性模型或树集合)。
如 Sigrist (2020)所述, 组合的梯度树提升和混合效果模型通常比(I)普通梯度提升、(ii)标准线性混合效果模型和(iii)将机器学习或统计模型与混合效果模型相结合的替代方法表现更好。
建模分组数据
**分组数据(也称为聚类数据、纵向数据、面板数据)**在许多应用中,当对一个感兴趣变量的不同单位进行多次测量时,会自然出现分组数据。例子包括:
- 人们希望研究某些因素的影响(例如,学习技巧、营养、睡眠等。)对学生的考试成绩,每个学生做几个测试。在这种情况下,单元(即分组变量)是学生,感兴趣的变量是考试分数。
- 一家公司收集其客户的交易数据。对于每个客户,都有几笔交易。这些单元就是客户,而感兴趣的变量可以是交易的任何属性,比如价格。
基本上,这种分组数据可以使用四种不同的方法建模:
- 忽略分组结构。这很少是一个好主意,因为重要的信息被忽略了。
- 分别为每个小组(即每个学生或每个顾客)建模。这也不是一个好主意,因为相对于不同组的数量,每组的测量数量通常很小。
- **在您选择的模型中包含分组变量(如学生或客户 ID ),并将其视为分类变量。**虽然这是一种可行的方法,但它有以下缺点。通常,每组的测量数量(例如,每个学生的测试数量、每个客户的交易数量)相对较少,而不同组的数量(例如,学生数量、客户数量等)相对较多。).在这种情况下,该模型需要基于相对少的数据学习许多参数(每个组一个),这使得学习效率低下。此外,对于树,高基数分类变量可能会有问题。
- 在混合效果模型中使用所谓的随机效果对分组变量进行建模。这通常是两种方法之间明智的妥协。第三。以上。具体而言,如下文和SIG rist(2020)所示,在树提升的情况下,与其他方法相比,这是有益的。
方法论背景
对于 GPBoost 算法,假设响应变量 y 是潜在的非线性均值函数 F(X)和所谓的随机效应 Zb 的和:
y = F(X) + Zb + e
在哪里
- y 是响应变量(又名标签)
- x 包含预测变量(即特征), F()是一个潜在的非线性函数。在线性混合效应模型中,这只是一个线性函数。在 GPBoost 算法中,这是树的集合。
- Zb 是假设遵循多元正态分布的随机效应
- e 是一个误差项
使用 GPBoost 算法训练该模型,其中训练意味着使用树集合学习随机效应的(共)方差参数(又名超参数) 和回归函数 F(X】。在模型被学习之后,随机效应 Zb 可以被估计(或预测,因为它经常被称为)。简而言之,GPBoost 算法是一种 boosting 算法,它迭代地学习(共)方差参数,并使用梯度和/或牛顿 boosting 步骤向树集合添加一棵树。与现有 boosting 算法的主要区别在于,首先,它考虑了由于聚类而导致的数据之间的依赖性,其次,它学习随机效应的(共)方差参数。有关该方法的更多详细信息,请参见 Sigrist (2020) 。在 GPBoost 库中,可以使用(加速)梯度下降或 Fisher 评分来学习(共)方差参数,使用 LightGBM 库来学习树。特别是,这意味着 LightGBM 的全部功能都是可用的。
如何在 Python 中使用 GPBoost 库
在下文中,我们将展示如何使用 Python 中的 GPBoost 库来应用组合的树增强和混合效果模型。本文中使用的完整代码可以在这里作为 Python 脚本找到。注意,还有一个等价的 R 包。更多相关信息可在这里找到。
装置
pip install gpboost -U
模拟数据
我们在这里使用模拟数据。我们采用众所周知的非线性函数 F(X) 。为了简单起见,我们使用一个分组变量。但是同样可以使用几种随机效应,包括分层嵌套效应、交叉效应或随机斜率效应。样本的数量是 5000,不同组或聚类的数量是 500。我们还生成测试数据来评估预测的准确性。对于测试数据,我们既包括已知的观察组,也包括新的未观察组。
import gpboost as gpb
import numpy as np
import sklearn.datasets as datasets
import time
import pandas as pd# Simulate data
ntrain = 5000 # number of samples for training
n = 2 * ntrain # combined number of training and test data
m = 500 # number of categories / levels for grouping variable
sigma2_1 = 1 # random effect variance
sigma2 = 1 ** 2 # error variance
# Simulate non-linear mean function
np.random.seed(1)
X, F = datasets.make_friedman3(n_samples=n)
X = pd.DataFrame(X,columns=['variable_1','variable_2','variable_3','variable_4'])
F = F * 10**0.5 # with this choice, the fixed-effects regression function has the same variance as the random effects
# Simulate random effects
group_train = np.arange(ntrain) # grouping variable
for i in range(m):
group_train[int(i * ntrain / m):int((i + 1) * ntrain / m)] = i
group_test = np.arange(ntrain) # grouping variable for test data. Some existing and some new groups
m_test = 2 * m
for i in range(m_test):
group_test[int(i * ntrain / m_test):int((i + 1) * ntrain / m_test)] = i
group = np.concatenate((group_train,group_test))
b = np.sqrt(sigma2_1) * np.random.normal(size=m_test) # simulate random effects
Zb = b[group]
# Put everything together
xi = np.sqrt(sigma2) * np.random.normal(size=n) # simulate error term
y = F + Zb + xi # observed data
# split train and test data
y_train = y[0:ntrain]
y_test = y[ntrain:n]
X_train = X.iloc[0:ntrain,]
X_test = X.iloc[ntrain:n,]
学习和预测
下面的代码显示了如何训练模型并进行预测。如下所示,学习的方差参数接近真实值。注意,当进行预测时,可以对均值函数 F(X)和随机效应 Zb 进行单独的预测。
# Define and train GPModel
gp_model = gpb.GPModel(group_data=group_train)
# create dataset for gpb.train function
data_train = gpb.Dataset(X_train, y_train)
# specify tree-boosting parameters as a dict
params = { 'objective': 'regression_l2', 'learning_rate': 0.1,
'max_depth': 6, 'min_data_in_leaf': 5, 'verbose': 0 }
# train model
bst = gpb.train(params=params, train_set=data_train, gp_model=gp_model, num_boost_round=31)
gp_model.summary() # estimated covariance parameters
#Covariance parameters:
# Error_term Group_1
#Param. 0.92534 1.016069
# Make predictions
pred = bst.predict(data=X_test, group_data_pred=group_test)
y_pred = pred['response_mean']
np.sqrt(np.mean((y_test - y_pred) ** 2)) # root mean square error (RMSE) on test data. Approx. = 1.26
参数调谐
仔细选择调整参数对于所有升压算法都很重要。可以说,最重要的调优参数是提升迭代的次数。太大的数值通常会导致回归问题中的过度拟合,而太小的数值则会导致“欠拟合”。在下文中,我们展示了如何使用交叉验证来选择提升迭代的次数。其他重要的调整参数包括学习速率、树深度和每片叶子的最小样本数。为了简单起见,我们不在这里对它们进行调优,而是使用一些默认值。
# Parameter tuning using cross-validation (only number of boosting iterations)
gp_model = gpb.GPModel(group_data=group_train)
cvbst = gpb.cv(params=params, train_set=data_train,
gp_model=gp_model, use_gp_model_for_validation=False,
num_boost_round=100, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
# Best number of iterations: 31
更新 :从版本 0.4.3 开始,GPBoost 现在有了一个函数(grid_search_tune_parameters
),可以使用随机或确定性网格搜索进行参数调整。详见本 Python 参数调优演示 。
特征重要性和部分相关性图
特征重要性图和部分相关性图是用于解释机器学习模型的工具。这些可以如下使用。
# Plotting feature importances
gpb.plot_importance(bst)
特征重要性图
单变量部分相关图
from pdpbox import pdp
# Single variable plots (takes a few seconds to compute)
pdp_dist = pdp.pdp_isolate(model=bst, dataset=X_train,
model_features=X_train.columns,
feature='variable_2',
num_grid_points=50,
predict_kwds={"ignore_gp_model": True})
pdp.pdp_plot(pdp_dist, 'variable_2', plot_lines=True)
多元部分相关图
# Two variable interaction plot
inter_rf = pdp.pdp_interact(model=bst, dataset=X_train,
model_features=X_train.columns,
features=['variable_1','variable_2'],
predict_kwds={"ignore_gp_model": True})
pdp.pdp_interact_plot(inter_rf, ['variable_1','variable_2'], x_quantile=True, plot_type='contour', plot_pdp=True)
# ignore any error message
用于可视化交互的二维部分相关图
SHAP 价值观
SHAP 值和依赖图是模型解释的另一个重要工具。这些可以按如下方式创建。注意:为此需要 shap 版本> =0.36.0。
import shap
shap_values = shap.TreeExplainer(bst).shap_values(X_test)
shap.summary_plot(shap_values, X_test)
shap.dependence_plot("variable_2", shap_values, X_test)
SHAP 价值观
变量 2 的 SHAP 相关图
与替代方法的比较
在下文中,我们使用上述模拟数据将 GPBoost 算法与几种现有方法进行比较。我们考虑以下替代方法:
- **线性混合效应模型(‘Linear _ ME’)**其中 F(X)是线性函数
- 忽略分组结构的标准梯度树提升(‘Boosting _ Ign’)
- 标准梯度树提升,包括作为分类变量的分组变量(‘Boosting _ Cat’)
- 混合效果随机森林(‘MERF’)(详见此处和 Hajjem et al. (2014) )
我们根据使用均方根误差(RMSE)和计算时间(以秒为单位的时钟时间)测量的预测准确性来比较这些算法。结果如下表所示。产生这些结果的代码可以在下面的附录中找到。
GPBoost 和替代方法的比较。
我们看到 GPBoost 和 MERF 在预测准确性方面表现最好(几乎一样好)。此外,GPBoost 算法比 MERF 算法快大约 1000 倍。线性混合效应模型(’ Linear_ME ‘)和忽略分组变量的树提升(’ Boosting_Ign ')的预测精度明显较低。将分组变量作为分类变量(“Boosting_Cat”)的树提升也显示出比 GPBoost 或 MERF 更低的预测准确性。注意,在这个例子中,测试数据包含已经在训练数据中观察到的现有组和没有在训练数据中观察到的新组(各 50%)。如果测试数据仅包含现有组,Boosting_Cat 和 GPBoost / MERF 之间的差异更大;参见 中的实验 Sigrist (2020) 。
注意,为简单起见,我们只进行一次模拟运行(更详细的比较见【SIG rist(2020))。除了 MERF,所有计算都是使用 gpboost Python 包 0.7.6 版完成的。此外,我们使用 MERF Python 包版本 0.3。
结论
GPBoost 允许组合混合效果模型和树提升。如果应用线性混合效应模型,应调查线性假设是否确实合适。GPBoost 模型允许放宽这一假设。它可以帮助您找到非线性和相互作用,并实现更高的预测准确性。如果您是 XGBoost 和 LightGBM 等 boosting 算法的频繁用户,并且您有潜在高基数的分类变量,GPBoost(它扩展了 LightGBM)可以使学习更加高效,并产生更高的预测准确性。
据我们所知,GPBoost 库在计算速度和预测准确性方面目前是无与伦比的。其他优势是 GPBoost 支持一系列模型解释工具(可变重要性值、部分相关图、SHAP 值等)。).此外,除了分组或聚类随机效果外,还支持其他类型的随机效果,如高斯过程。
希望这篇文章对你有用。关于 GPBoost 的更多信息可以在配套文章 Sigrist (2020) 和 github 上找到。
参考
Hajjem、f . Bella vance 和 d . la rocque(2014 年)。聚类数据的混合效果随机森林。统计计算与模拟杂志, 84 (6),1313–1328。
柯国光,孟,陈,王,陈,马,刘天元(2017)。Lightgbm:一种高效的梯度推进决策树。在神经信息处理系统的进展(第 3146–3154 页)。
皮涅罗和贝茨博士(2006 年)。S 和 S-PLUS 中的混合效果车型。斯普林格科学&商业媒体。
西格里斯特,F. (2020)。高斯过程增强。arXiv 预印本 arXiv:2004.02653 。
附录
替代方法比较代码
results = pd.DataFrame(columns = ["RMSE","Time"],
index = ["GPBoost", "Linear_ME","Boosting_Ign","Boosting_Cat","MERF"])
# 1\. GPBoost
gp_model = gpb.GPModel(group_data=group_train)
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train, gp_model=gp_model, num_boost_round=best_iter)
results.loc["GPBoost","Time"] = time.time() - start_time
pred = bst.predict(data=X_test, group_data_pred=group_test)
y_pred = pred['response_mean']
results.loc["GPBoost","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 2\. Linear mixed effects model ('Linear_ME')
gp_model = gpb.GPModel(group_data=group_train)
X_train_linear = np.column_stack((np.ones(ntrain),X_train))
X_test_linear = np.column_stack((np.ones(ntrain),X_test))
start_time = time.time() # measure time
gp_model.fit(y=y_train, X=X_train_linear) # add a column of 1's for intercept
results.loc["Linear_ME","Time"] = time.time() - start_time
y_pred = gp_model.predict(group_data_pred=group_test, X_pred=X_test_linear)
results.loc["Linear_ME","RMSE"] = np.sqrt(np.mean((y_test - y_pred['mu']) ** 2))
# 3\. Gradient tree-boosting ignoring the grouping variable ('Boosting_Ign')
cvbst = gpb.cv(params=params, train_set=data_train,
num_boost_round=100, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train, num_boost_round=best_iter)
results.loc["Boosting_Ign","Time"] = time.time() - start_time
y_pred = bst.predict(data=X_test)
results.loc["Boosting_Ign","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 4\. Gradient tree-boosting including the grouping variable as a categorical variable ('Boosting_Cat')
X_train_cat = np.column_stack((group_train,X_train))
X_test_cat = np.column_stack((group_test,X_test))
data_train_cat = gpb.Dataset(X_train_cat, y_train, categorical_feature=[0])
cvbst = gpb.cv(params=params, train_set=data_train_cat,
num_boost_round=1000, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train_cat, num_boost_round=best_iter)
results.loc["Boosting_Cat","Time"] = time.time() - start_time
y_pred = bst.predict(data=X_test_cat)
results.loc["Boosting_Cat","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 5\. Mixed-effects random forest ('MERF')
from merf import MERF
rf_params={'max_depth': 6, 'n_estimators': 300}
merf_model = MERF(max_iterations=100, rf_params=rf_params)
print("Warning: the following takes a lot of time")
start_time = time.time() # measure time
merf_model.fit(pd.DataFrame(X_train), np.ones(shape=(ntrain,1)), pd.Series(group_train), y_train)
results.loc["MERF","Time"] = time.time() - start_time
y_pred = merf_model.predict(pd.DataFrame(X_test), np.ones(shape=(ntrain,1)), pd.Series(group_test))
results.loc["MERF","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
print(results.apply(pd.to_numeric).round(3))
Python 的树形图基础
一种非常简单的展示构图的方式
不缺乏可视化来显示单个值如何组成一个整体。有些可能非常复杂和具体;其他的,比如我们今天要探索的,非常简单易用。
从馅饼到华夫饼图表,展示整体比例的想法通常是讲故事的良好开端。您可以从构成开始,然后逐个探索不同的值,或者比较不同实体或不同时期的不同结构。
树形图
美国计算机科学家、马里兰大学教授本·施奈德曼在 20 世纪 90 年代首次使用了这种可视化技术。
Ben Shneiderman 和他的两个树状图艺术项目作品
他的可视化变得非常流行,你可以在很多工具和语言中找到不同的实现和不同的算法来创建它们,比如 Tableau、PowerBi、Python、R 等等。
方形的
在接下来的例子中,我们将使用 Python 和 Matplotlib、Squarify 和 Pandas 来创建和设计我们的树形图。稍后我们将快速了解 Plotly 能做什么。
import matplotlib.pyplot as plt
import squarify # pip install squarify
import pandas as pd
Squarify 没有什么复杂的,我们可以简单地向它传递一个数字列表,它就会计算出布局。
sizes = [50, 25, 12, 6]squarify.plot(sizes)
plt.show()
小菜一碟。
现在,你不能从仅仅看这些随机的方块中得到太多的信息。也许你可以对整体构成有一些见解,但除非你熟悉数据本身,否则你无法识别那些方块。
让我们更具体一些,给我们的树形图添加一些标签。我们也可以去掉轴,让颜色柔和一点。
sizes=[50, 25, 12, 6]
label=["50", "25", "12", "6"]squarify.plot(sizes=sizes, label=label, alpha=0.6 )
plt.axis('off')plt.show()
旗帜
酷!你注意到颜色是如何变化的吗?我们降低了α,这就是为什么它们不那么亮,但不仅如此,它们还改变了位置。
将颜色分配给方块的方式有一些随机性——别问我为什么。
尽管随机性很有用,会带来一些意外的惊喜,但它并不总能让你得到一个有意义的配色方案。为了避免这种情况,我们可以定义我们的颜色列表。
sizes=[50, 25, 12, 6]
label=["50", "25", "12", "6"]
color=['red','blue','green','grey']squarify.plot(sizes=sizes, label=label, color=color, alpha=0.6 )
plt.axis('off')plt.show()
定义我们的颜色以获得更清晰的图像只是其中的一个方面。
颜色可以添加另一种编码,例如,如果我们在不同的地方有一家公司,我们可能希望看到每个公司对我们的总资产有多少贡献,这将是我们的树形图中方块的大小。
然后,我们可以根据利润、生产数量、成本或任何我们想要的东西来绘制这些地点的彩色地图。
让我们举一个更容易理解的例子来说明这个想法,假设我们在两个地方有四家公司。我们可以在不同的地方使用不同的颜色,在同一个地方使用相似的颜色。
sizes=[50, 25, 12, 6]
label=["BC 1", "OT 1", "OT 2", "OT 3"]
color=['red','#1C9FB0','#32A0CE','#1C51B0']squarify.plot(sizes=sizes, label=label, color=color, alpha=0.6 )
plt.axis('off')plt.show()
现在我们可以知道每个人贡献了多少,我们也可以很容易地区分 BC 和 OT。
可量测性
我们可以使用 Pandas 数据框而不是列表,这为我们可以在数据中执行的各种操作打开了大门,并大大增加了可伸缩性。
让我们看看这种可视化可以扩展到什么程度。我将使用英国按品种分类的农场动物数量数据集。
df = pd.read_csv('data/animal-population-by-breed-on_1-march-2010.csv')# convert to numeric and drop na
df['Number of Animals'] = pd.to_numeric(df['Number of Animals'], errors='coerce')
df.dropna(inplace=True)df.head()
现在,我们可以将“动物数量”作为我们的尺寸,将“品种”作为我们的标签。
fig, ax = plt.subplots(1, figsize = (12,12))squarify.plot(sizes=df['Number of Animals'],
label=df['Breed'],
alpha=.8 )plt.axis('off')
plt.show()
呃,这可不好。标签太多!
让我们尝试对数据框进行排序,并标记最大的方块。
df.sort_values('Number of Animals', ascending=False, inplace=True)
df
fig, ax = plt.subplots(1, figsize = (12,12))squarify.plot(sizes=df['Number of Animals'],
label=df['Breed']**[:5]**,
alpha=.8 )plt.axis('off')
plt.show()
厉害!有了最重要的人群的分类数据和标签,我们可以得到一些见解。
Plotly
有了 Plotly,我们可以在树形图上更进一步,分组数据,添加工具提示,颜色图非常简单。
让我们快速地看一个例子,只是为了说明可能性。我将使用一个视频游戏销售数据集和 Plotly Express。
首先,让我们加载数据并删除空行。
df = pd.read_csv('data/vgsales.csv')
df.dropna(inplace=True)
df
现在我们来绘制它!我们将传递数据框、包含类别的字段的路径、方块的大小和颜色的强度。
Plotly 将使用颜色图绘制我们的绘图,添加颜色条、工具提示,并根据方块的大小调整标签的大小。
import plotly.express as pxfig = px.treemap(df,
path=['Platform', 'Genre'],
values='Global_Sales',
color='NA_Sales'
)fig.show()
好的,标签不是每个方块都可读的,但是我们确实有工具提示来帮助我们找出那些我们不能读的,并且我们在这里添加了比以前更多的信息。
我个人并不热衷于互动;老实说,我很难想出如何将它添加到故事中——我的意思是,我们开始探索树状图,作为讲故事的开始和简化构图的方式,但这种可视化并不那么友好。
随着我们的视觉化增加了交互性和复杂性,我会提出相反的建议。通过显示单个方块或组,从您发现的具体见解开始,然后在最后,展示您的完整树形图,让用户自己探索它。
看看 Nadieh Bremer 的用和弦图讲故事。她的最终视觉化相当复杂。尽管如此,她在向你展示大图之前处理细节的方式非常棒,让数据更容易理解。
这里有一个我们创建的交互式 viz 的链接,在这里你可以找到代码。
感谢阅读我的文章。我希望你喜欢它。
资源: Treemaps 马里兰大学;
Python 图库—Treemap;
Datacadamia—Treemaps;
plottly—Treemaps;
Plotly — HTML 导出;
Github—Squarify;
树状图,为什么和如何
用树形图讲故事
图片由 Pixabay 提供
渐渐地,硬盘上的文件数量从几十个增加到几百个,从几百个增加到几千个。同样,文件夹和目录的数量几乎呈指数级增长。有必要找到一些图表,让相对简单的方式来可视化新的 利维坦的内容。
这个解决方案来自于 Ben Shneiderman ,马里兰大学的计算机科学教授和人机交互实验室 (HCIL)的创建者。他在寻找“目录树结构的紧凑可视化”(#1)。很明显,目录的结构对应于一个分层的树形结构,但是现有的图非常庞大,因此效率很低。用他自己的话来说:“树形结构的节点连接图变得太大而无用,所以我探索了在空间受限的布局中显示树的方法”(#2)。
最终结果是一个名为 Treemap 的图表。令人意想不到的是,它目前的应用大大超出了最初的目的。
为什么:树形图是一个基于矩形的可视化,它允许你表示一个层次有序(树形结构)的数据集。概念上的想法是在一个物理限制的空间中比较数量和显示一些层次结构的模式。为此,不同大小和颜色的矩形用于从不同角度显示数据集。目标不是指出精确的数值,而是将数据集“分解”成其组成部分,并快速识别其较大和较小的组成部分。
图 1:用 Plotly 创建的树形图。
它们还用于表示等级、各部分之间的差异以及非等级“扁平”结构中的相对比例。从这个意义上说,它们被认为是传统饼图的替代品,传统饼图通常用于可视化“部分到整体”的关系。与饼状图最大的不同在于,树形图允许你在一个相对较小的空间内比较组成 嵌套矩形的方案中整体的各个部分。与饼状图相比,饼状图的一个优势在于,它们可以在一个结构中包含数十或数百个部分,这个结构可能是分层的,也可能不是。
物理空间的有效利用和智能色彩管理使它们成为在各种商业分析应用以及金融、商业、政府、选举和类似领域中可视化大量信息的出色工具:标准普尔 500 指数;Merval(布宜诺斯艾利斯证券交易所);各省或各州的选举结果;按地区、国家或部门分列的出口;按产品划分的销售额;等等。以下树形图显示了 2017 年牙买加按行业(农业、石油、化工、矿产等)划分的出口情况。) (#3).
图 2:2017 年牙买加出口。资料来源:commons.wikimedia.org(# 3)。
由于前面提到的原因,树图是商业智能(BI)表示中最常用的可视化元素之一,尽管当可视化的目标是在数据集的组件之间进行精确比较时,不应使用树图。
**如何实现:**它们由一系列嵌套的矩形(瓷砖)组成,其面积与它们所代表的数据成比例。较大的矩形(层次的较高层)指示数据树的根或主分支,并被细分成较小的矩形,指示树的每个分支所具有的节点(层次的较低层)的大小。数据集的数值应该是正的,并且对应于矩形的面积。
色彩的巧妙运用让新的维度融入到图表中。通常的程序是在不同的矩形中使用颜色来表示第二个分类或定量值。因此,通过单一颜色的强度变化来表示等级;通过两种颜色强度的变化来显示不同的正负定量值。下图显示了高于和低于阈值的值,这些值以两种颜色的不同强度排列。
图 3:用 Squarify 创建的树形图。
从上面可以推断出,恰当地使用颜色可以让我们使用树形图来表示损失、销售额下降或其他非正值。总是指不是由矩形面积表示的第二定量值。
下面的树状图显示了标准普尔 500 (#4)。它是纽约证券交易所(NYSE)和纳斯达克(NASDAQ)500 只广泛持有的股票的指数或集合。矩形的大小表示指数中每家公司的相对规模。不同的颜色和不同的颜色强度显示了特定一天的阳性、阴性和“中性”结果的刻度:非常亮的红色表示向下的大偏移,非常亮的绿色表示向上的大偏移。毫无疑问,树状图是在相对较小的空间内显示大量信息的最合适的可视化方式。
图 4:标准普尔 500(排名第四)。
几个算法允许你通过彩色矩形形状的嵌套图形显示分层数据:方形、切片和方块,以及条状。 Squarified 是商业数据可视化工具实现最多的算法。它基于一种策略,即设法使每个区块尽可能地呈正方形,以便于它们之间的比较。它还试图从图的左上到右下以递减的方式排列数据集的连续成员(块、切片)。最后,该算法在台式机、笔记本电脑和手机上有很好的视觉呈现。
用树状图讲述故事时需要考虑的一些注意事项:
- 请记住,树状图很难阅读,因为它们依赖于观众通过比较区域来解码数字信息的能力。不要忘记,人类擅长估算距离,却不擅长计算面积。由于在树形图中显示了大量的区域(矩形、瓦片),这个缺点在树形图中被放大了。在使用树形图之前,有必要评估条形图或饼状图是否没有以较低的视觉强度讲述相同的故事。
- 当真正重要的信息有相似的数值时,这些图表是不可取的。在这种情况下,算法会生成非常相似的区域块,很难在仪表板中进行比较。
- 树形图不能用于显示可能取负值(如损失)的数值变量。请记住,这种类型的限制是所有那些使用区域来编码数量变量的图表所共有的。
- 如果你想在交互式演示中使用它们,要非常小心。请记住,如果比较区域相对困难,那么比较交互变化的区域就更加复杂。
- 另一个很难进行好的比较的问题是缺少一个共同的基线,这是条形图所没有的缺点。
- 当大块和小块之间的尺寸差异非常大时,通常不可能在较小的矩形中包含文本。标准的解决方案是使用工具提示在用户悬停在矩形上时提供与这些矩形相关的信息。
当然,了解 Stephen first 对这个问题的看法是很重要的:“当传统的图形(如条形图)无法使用时,因为在单个图形甚至单个屏幕上的一系列图形中有太多的项目要用条形来表示,树形图通过优化使用屏幕空间来解决这个问题。因为它们依赖于预先注意的属性来编码我们无法精确比较的值(面积和颜色),所以我们保留这种方法用于无法使用其他更精确的可视化或没有必要精确的情况。”(#5).
用 Python 绘制树状图
正如的 Python 图库 (#6)所指出的,“dataviz 的可能性相当差”。要用 Python 绘制树形图,必须导入 Squarify 库 ( pip install squarify )并使用 squarify.plot 函数,通过 alpha 属性指示值、标签、颜色和透明度。与一些商业工具生成的图相比,生成的图非常有限。
一个更好的替代方法是通过 treemap 指令使用 Plotly ,其语法远优于其 Squarify 等价指令。除了指示值、标签和颜色之外,在 plotly.express 中的 treemap 语法还包括 path 参数,它允许你定义从根到分支(#7)的扇区层次。
plotly.express.treemap(*data_frame=None*, *names=None*, *values=None*, *parents=None*, *ids=None*, *path=None*, *color=None*, *color_continuous_scale=None*, *range_color=None*, *color_continuous_midpoint=None*, *color_discrete_sequence=None*, *color_discrete_map={}*, *hover_name=None*, *hover_data=None*, *custom_data=None*, *labels={}*, *title=None*, *template=None*, *width=None*, *height=None*, *branchvalues=None*, *maxdepth=None*)
结论
当数据集以具有根、分支和节点的树形布局的分层顺序构造时,树形图是一种合适的可视化类型。它允许我们在有限的空间内以非常有效的方式显示大量数据的信息。可视化的主要目的是在不同层级之间进行不精确的比较。它们也用于对非层次数据进行“整体的一部分”分析。它们不应该用于得出准确的结论,带有负面的定量数据,或者当数字差异不允许进行适当的比较时。
图片由来自 Unsplash 的 David Kovalenko 提供
如果你对这篇文章感兴趣,请阅读我以前的(https://medium.com/@dar.wtz):
Mekko 图表,为什么&如何
为什么和如何
towardsdatascience.com](/mekko-charts-f38311c576e2)
仪表图和项目符号图,为什么和如何,用仪表讲故事
为什么&如何,用量表讲故事
towardsdatascience.com](/gauge-bullet-charts-cfe171ca3094)
参考文献
#1.本·施奈德曼(1992 年)。“用树状图显示树木:二维空间填充方法”。美国计算机学会图形汇刊。11: 92–99.doi:10.1145/102377.115768。
#2.— Ben Shneiderman,2006 年 4 月 11 日,使用树形图可视化发现商业智能,http://www.perceptualedge.com/articles/b-eye/treemaps.pdf
#3.—https://commons . wikimedia . org/wiki/File:Jamaica _ Export _ treemap . jpg
#4.—https://www.mergersandinquisitions.com/what-is-the-sp-500/
#5.—Stephen first,2009,《现在你看到了:定量分析的简单可视化技术》,分析出版社;第一版(2009 年 4 月 1 日)
#6.——【https://python-graph-gallery.com/treemap/
#7.—https://plotly . com/python-API-reference/generated/plotly . express . treemap . html
2020 年及以后人工智能的趋势
在企业中实现真实、不可见和增强的三种方式
无论是机器撰写的新闻文章,人工智能主导的网络安全还是情绪检测的关键发展,2019 年无疑为人工智能(AI)世界带来了前沿成就。展望 2020 年代,我们能期待什么?
对企业和消费者来说,人工智能肯定会继续以充满希望的方式发展。迄今为止围绕人工智能的宣传正迅速被更切实的现实所取代,为组织提供了以结果驱动的方式收获人工智能回报的机会。
以下是人工智能将如何帮助商业领袖和决策者拓展其企业的边界,以提高业务绩效:
人工智能:不再只是一句流行语
如今,AI 对大多数工作场所来说并不陌生。根据 Forrester 的研究,53%的全球数据和分析决策者表示,他们已经实施、正在实施或正在扩展或升级某种形式的人工智能的实施。
2019 年,我们看到围绕人工智能的对话发生了转变。组织开始关注从人工智能中获得的商业价值,供应商就人工智能成熟度教育客户。与此同时,公司开始寻找数据科学家以外的角色,以支持他们的人工智能之旅。
今年,我们将继续关注用人工智能解决“真正的”挑战,而不是大肆宣传变得模糊不清的“人工智能”将会有更多关于你可以从哪里开始使用人工智能以及将人工智能应用于商业需要哪些团队技能的讨论。您将听到更多关于如何通过采用实现业务投资回报的信息。
更多的企业将意识到,让合适的人负责他们的人工智能项目将产生更大的效益。例如,与没有首席数据官的公司相比,拥有专门首席数据官(CDO)或首席分析官(CAO)的公司使用人工智能、机器学习(ML)和/或深度学习的可能性已经增加了大约 1.5 倍。
通过在工作场所更加强调人工智能,每个部门和角色的员工都会注意到人工智能对企业越来越重要。
数据科学团队将继续实现欢迎具有跨学科经验的成员的好处,这将进一步扩大非技术人员的数量,在人工智能的使用中发挥更大的作用——这是由更容易获得 ML 工具包和 AutoML 功能推动的。
随着人工智能的采用越来越多,越来越多的员工实际体验到人工智能如何增强、改善甚至从根本上转变他们的战略和工作角色,人工智能将不再被归入企业的 R&D 翼。它将触及未来公司的几乎每一个部分。
隐形 AI 实现
2019 年,我们看到了狭义 AI 的巨大进步,包括卫星图像、自然语言处理(NLP)和计算机视觉等领域。展望未来,期待看到更多人工智能研究的突破,以及人工智能模型的商品化。
人工智能将越来越多地嵌入到领域驱动的解决方案中,使其变得无形而无所不在。人工智能在边缘计算中的趋势性使用就是一个例子。在更有效地利用资源的推动下,信息处理和内容收集将更接近信息源。
边缘人工智能还可以降低延迟,提高数据隐私性。人工智能驱动的推理、模式匹配和预测将融入边缘的领域应用,使用户界面更加智能。
IDC 预测,到 2022 年,75%的企业将在技术和流程开发中嵌入智能自动化。同一份报告预测,到 2024 年,人工智能将成为新的用户界面,50%的用户触摸将通过计算机视觉、自然语言、增强现实和虚拟现实来增强。人工智能将无处不在,但从未见过。
增强人工智能:人类驱动的未来
今年,你将看到更多人类与人工智能的合作。人工智能将越来越多地以人为本的方式设计,允许增强能力。
人在回路不仅仅是紧急情况下的自动防故障装置;这就像今天的自动驾驶汽车背后有一个人类司机。然而,人类在人工智能系统中的作用远不止是在出现问题时待命。
人类的输入是必要的,以减轻诸如人工智能偏差等问题,并提高模型日常决策的质量。组织已经开始关注这种需求:根据 IDC 的数据,在未来四年,我们将看到 75%的企业重新培训和发展员工,以解决新的技能集,优化人与人工智能的交互。
然而,权力越大,责任越大。Forrester 警告今年有三个公关灾难可能会“撼动科技界的声誉”:深度假货的兴起、面部识别的不当使用和过度个性化。有了人在回路系统,组织可以确定这种人工智能技术可能发生不道德转向的点,并将其引导回正确的道路上。
很明显,2020 年为人工智能在企业中的应用带来了巨大的希望。然而,企业和消费者必须明白,快速涌现的人工智能能力不是可以盲目信任的解决所有问题的黑匣子。
纠正这些错误观念对公司来说至关重要,因为他们将继续利用技术的力量。借助为持续人工输入而设计的系统,企业可以使用人工智能来解决障碍,并在 2020 年及以后创造价值。
这篇文章最初是 发表在《分析》杂志的 上。增加了插图。标题照片由 在上画出。