TowardsDataScience-博客中文翻译-2022-十七-

TowardsDataScience 博客中文翻译 2022(十七)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

数据团队:干掉你的服务台

原文:https://towardsdatascience.com/data-teams-kill-your-service-desk-985371ac927c

我们能否超越服务台陷阱,同时仍然满足业务利益相关者的需求?

照片由 海梅丹塔斯 Unsplash

抓住 Excalibur,杀死你的宿敌:服务台——数据团队永远的敌人。

在我们花了两年时间构建 Workstream.io 的过程中,我与许多数据领导者谈论过他们最讨厌的工具:用于管理他人请求的“服务台”。

我广义地使用术语“服务台”,因为数据团队有许多不同的方式来完成这项工作。

我们的一个客户有一个叫做“数据吸收”的松弛通道名字本身就能告诉你很多人的感受。其他团队使用谷歌表单,实现了像 JIRA 服务台这样的工具,或者,最糟糕的是,利用一些可怕的组合。

团队首先讨论防止或减少这些请求的方法。我们应该让请求变得更难吗?也许是我们的团队结构加剧了这个问题?我们陷入服务陷阱了吗?我们需要在适当的文档和培训上投入更多吗?我们是否应该采取更多措施来实现自助式分析?

人们提出了这些问题,采取了一些举措,但另一方面却是一个可怕的事实:其他人的问题依然存在。数据团队发自内心的、情绪化的、近乎原始的反应也是如此。

我们的利益相关者呢?他们也仍然讨厌提出要求。

为什么我们都有这种感觉,为什么我们仍然对此无能为力?

因为服务体验糟透了

数据团队讨厌他们的服务台,因为我们希望专注于我们认为高价值、战略性的工作。(我们这样做是正确的)。

然而,这种厌恶远不止是工具本身。毕竟,当前一代的“服务台软件”是对之前任何产品的重大改进:锅炉房呼叫中心,那里的代理人努力支持沮丧、愤怒的客户,很少得到他们想要的答案。一个随机的电话号码,你打电话给你的内部 IT 团队,并留下语音邮件说你的电脑坏了。我们今天讨厌的支持系统——比如票务或聊天——天生就是比它们的前辈好得多的体验。

因此,虽然我们可能会抱怨 JIRA 服务台,及其笨重的事务性用户界面,但我们的厌恶是更深层次的:服务体验——除了 Zappos 等少数明显的例外——通常很糟糕。

因此,当我们重新利用那些旨在促进这些体验的工具时,我们向自己和我们的同伴发出了很多关于我们应该如何感受和行为的信号。我们的参考点变得相当字面上,当你被留在等待几个小时,或当辱骂,愤怒的客户尖叫古怪的要求和淫秽的时候。

与我们的服务经历相关的包袱太多了,我们无法打破这个循环。这意味着我们都不幸地以不快乐告终,我们的团队无法充分利用在我们的数据上所做的投资。

因为数据团队既不是产品团队,也不是服务团队

在我们的社区中有一场关于数据团队本质上是产品团队还是服务团队的激烈辩论。与传统的客户服务或产品开发这两个截然不同的职能不同,数据团队集支持、产品和工程职能于一身。同一个团队研究和确定产品范围,计划和执行开发工作,对错误进行分类,并管理其他人的大型复杂集合的期望。

现实是,每个数据团队都必须平衡他们的“产品工作”(定义、界定和构建具有长期价值的数据产品)和他们的“服务工作”(响应业务的诊断需求)。虽然后者可能在极少数情况下是令人兴奋的,但当请求是战略性的时,它更经常地令人讨厌,无论是需要拉一个数字,还是对一份报告的行动反馈。

我们都凭直觉知道这一点,这就是为什么即使是关于数据团队作为产品团队的最引人注目的讨论也包括部署令人讨厌的服务台来管理其他人不必要的突发奇想和干扰。

作为推论,也正是这种平衡产品和支持工作的需要,让我们认识到,对于数据团队来说,敏捷是“最糟糕的政府形式——除了我们已经尝试过的所有其他形式。”

为什么敏捷不是数据团队的完美的标准论点是,数据工作更具迭代性,没有执行分析或洞察的预定义终点。这是真的。然而,我也认为敏捷并不是一个完美的选择,因为数据工作的工作流程比以前更加相互关联、协作性更强、模块化程度更低。

服务台和敏捷框架都是为了让可预测系统——具有可测量的标签输入和可测量的功能(或答案)输出——更加高效。但是处理不可预测的复杂系统的出色工作也是如此——例如,同一个团队同时扮演被动的支持者和主动的建设者。这是一个复杂的世界,有两个(或更多)竞争的重心。

尽管它们都有不足之处,但我们都实现了它们——认为如果我们能够管理期望,或者设置明确的优先级,这些工具可以互补。如果我们聪明,我们就能打破这个循环。但最终,我们在某些方面失败了,仍然责怪我们的服务台。毕竟,它代表了所有我们讨厌的东西,并使我们远离我们热爱的东西(构建数据产品)。

因为服务台关乎权力动态

我们经常认为服务台的外形是我们成功的决定性因素。但事实真的是这样吗?

一种选择是在一个宽松的渠道中管理和交付请求,这可以优化协作和速度。对话发生在人与人之间,并且对跟随的任何人都是公开的。这种模式的论点是,数据对话与您组织中的任何其他对话没有什么不同,它们应该被如此对待。但是消息传递是出了名的饶舌,糟糕的搜索让对话无法追踪,并且期望几乎即时的响应时间是常态。

因此,最成熟的数据团队,那些管理复杂的技术和人员系统的团队,以及那些执行最雄心勃勃的项目的团队,通常会利用一些正式的票务解决方案。这种方法有助于管理对响应时间的期望,并将团队从妨碍深入工作的持续干扰中解放出来。然而,它牺牲了人们接收答案的速度,换取了传递答案者的效率。

这两种形式都忽略了一点,即数据团队不为其他人服务,其他人也不为他们服务。他们都忽略了一点,那就是我们都在互相服务,互相依赖。

我们讨厌服务台,因为它的设计迫使我们决定谁应该行使权力:我们自己,还是其他人?有没有办法走出这个悲惨的循环?

杀死服务台的 3 种方法

最终,团队的成功不在于我们提交请求或发布专题报道的效率,而在于我们对推动组织前进的数据建立共同意识的能力。

正如斯坦利·麦克克里斯特尔将军在他的书《团队的团队》(Portfolio/Penguin,2015 年)中解释的那样,现代世界是“复杂的”,活动的速度和相互依存性使得“不可能说出哪些事件可能导致什么样的结果。”简单来说,传统的系统和团队是线性和模块化的。系统被设计成将输入转化为输出,无论是将矿石转化为酒吧,将汽车零件转化为汽车,还是将客户问题转化为解决方案。人员可以被分成执行特定任务的模块化子团队,在这些子团队中,完成一个人的角色只需要相对较少的关于过程中的前一步或下一步的上下文。

相比之下,复杂系统是多维的,相互依赖的。输入同时来自多个维度,输出遵循任意向量。为了应对这个世界,团队成员既需要的共同意识——对他人工作的充分理解和同情——也需要的执行能力——这意味着他们可以利用自己的知识做出独立的决策。

这就是数据驱动团队所处的世界:一个复杂的世界。它是数据人和其他人之间的空间,在这里每个人都需要访问共享意识需要在任何时候执行。这是一个传统管理系统无法设计、甚至无法理解的世界。

基于麦克克里斯特尔将军的概念,这里有 3 条建议,可以在满足业务利益相关者需求的同时,超越服务台陷阱:

  1. 主动构建与您的团队同呼吸共命运的文档。大多数数据团队已经为最终用户维护了一些文档。这种文档通常以传统格式存在——作为 Google doc 的一部分,或者与数据资产本身完全分离的企业内部网。但是,正如 Fishtown Analytics 的联合创始人 Drew Banin 在他在 Coalesce 2020 上发表的开创性演讲“后现代数据堆栈”中指出的那样,内容和对话需要保持一致,以便每个人都可以轻松访问和消费:“在未来……我们将有专门的媒体来讨论上下文中的数据。这有两种形式。一个是向消费者推送数据……在这个 feed 中,消费者将能够订阅与他们相关的信息。另一个媒介…是您和您的同行将能够就您的数据在上下文中进行对话。”基于他的想法,我相信这将包括:
  • 常见问题。来自业务用户的重复问题是数据团队的负担,也是对您时间的巨大浪费。有了更好的文档,那些重复的问题可以被推广并公开给任何用户去发现。
  • 培训内容:这可能包括录制的视频或书面的操作指南,可能是关于如何自助服务、如何最好地利用过滤器等基本功能,或者了解底层数据。
  • 生命周期和认证状态:让最终用户清楚资产处于其生命周期的哪个阶段,以及它是否得到您的团队的积极支持。
  • 状态页面:考虑使用数据质量和可观察性解决方案,甚至 dbt,为您最关键的数据资产构建和维护“状态页面”。这让最终用户无需询问您就能了解数据是否可信。

2。投资了解你的用户。真正了解您的用户至关重要,否则您如何构建满足他们需求的解决方案?

这从一开始就开始了——在你建造任何东西之前。在你考虑解决方案之前,或者在你努力的方向之前,你需要了解业务经历的问题。一旦你开始做一些事情,一定要和其他人合作,获得他们的反馈并建立信任。

随着时间的推移,投资去理解你的用户——不管是轶事式的还是主动的。进行用户访谈,了解人们最常用的是什么,以及它如何影响他们的决策。他们在与您的数据交互时遇到的常见难题是什么?

然后你想用数字对主观反馈进行三角测量。挖掘任何可用的资源,以了解您的环境中哪些资产是趋势性的,并且经常被使用。

通过所有这些,你可以更好地理解你的工作最有价值,以及你的路线图应该指向哪里。如果你能理解你的产品是如何被使用的,你就能对数据如何适应日常决策有独到的见解。

3。考虑实施数据管理员解决方案,根据需要随时随地为每个人提供有关其数据的知识。数据管理员顺利地将一个人的理解传递给另一个人。它将用户准确地定位到他们正在阅读的内容,并验证和联系其来源。这有助于他们理解其他人问了什么问题,不仅仅是答案是什么,还有接下来做了什么。它把每一条数据和每一个输出都当作一个人们需要搭载的产品,并教他们如何正确使用它。

结论

在未来 10 年,每个组织都将使用多种工具来消费和分析数据。如果我们继续受到票据、优先积压和权力斗争的约束,数据团队将永远无法发挥其全部潜力。为了以真正数据驱动的方式运营,我们需要超越“我们与他们”的误解,并找到与“其他人”合作的新方式。服务台是我们最常被集体诋毁的工具和活动之一,它将被我们喜爱的东西所取代,这有助于我们更有效地合作。

我们需要在塑造我们的未来中发挥积极作用,以确保每个人(数据从业者和其他人)始终能够从我们最重要的资产——数据——的集体知识中获得力量。

数据与工程师的比率:对欧洲 50 家顶级科技公司的深度调查

原文:https://towardsdatascience.com/data-to-engineers-ratio-a-deep-dive-into-50-top-european-tech-companies-58abc23e36ca

你知道,当顶级科技公司的开放数据角色数量接近工程时,数据正迎来它的时刻。但是最好的科技公司到底雇佣了多少数据人员呢?

我对欧洲 50 家顶尖科技公司的分析显示,数据与工程师的比例中位数是 1:4。

这与我在与数据领导者交谈时听到的一致,但正如您在下面的图表中看到的,比例范围从低于 1:10 到 1:2,跨度不小!

查看下面所有的比率(见这篇文章底部我是如何收集数据的细节)

作者图片

当我与高增长公司交谈时,大多数公司都试图快速招聘数据人员,因此我怀疑这一比例只会增加。

在我工作的 Monzo,我们在短短两年内将数据团队的规模扩大了一倍多。Glovo 拥有 74 个开放的数据职位(和 102 个开放的工程职位),正在寻找几乎和工程师一样多的新数据人员。

那么…为什么会发生数据雇佣军备竞赛呢?

  • 数据变得越来越复杂,对数据团队的期望也越来越高,因为销售、营销、运营和产品团队都被期望以数据驱动的方式运营
  • 公司开始看到数据团队的真正胜利——从具有现实世界影响的机器学习模型到大规模 A/B 测试,再到将数据直接引入运营工具。数据团队现在有有意义的 ROI 故事可以展示,并且经常被其他团队要求加速招聘
  • 数据是你取胜的方式,如果你做得不对,你就无法竞争(至少在某些行业)

数据是秘方

数据驱动不仅仅是一个时髦的术语。这是现代公司的制胜之道。引用 a16z 的马丁·卡萨多几周前在的话

过去,这种差异很大程度上来自软件。如今,您与众不同的方式是您管理数据的方式。如果你在开发一款遛狗应用,你赢的不再是软件,而是匹配算法、定价模式或识别欺诈的能力

绘制一个设计和数据与工程师比率的矩阵,讲述了一个有趣的故事,说明了公司及其商业模式的关注点。

作者图片

Snyk、TravelPerk 和 Onfido 等公司的数据团队相对于其工程团队规模较小,而 Glovo 和 Deliveroo 等公司的数据人员数量是工程师的两倍多。那是偶然的吗?大概不会。

如果你是一家像 TravelPerk 这样的 B2B SaaS 公司,你可能是数据驱动的。您有指导决策的仪表板,每个人都可以通过您的 BI 工具访问自助数据,您可以使用一些机器学习模型并进行一些 A/B 测试来改进转换漏斗。

但如果你是像 Deliveroo 这样的市场,你需要支持的数据。你的成功取决于你在预测需求、指导餐馆选择最佳价格、帮助乘客决定最佳路线或安排乘客在正确的时间上班方面的能力。在这样一个激烈的市场中,多家资金雄厚的公司争夺“市场份额”,只有那些最能利用数据的公司才能生存下来。

我的猜测是,未来所有的商业模式都将支持数据——不仅仅是市场。

如果您想了解更多信息,我已经对数据团队占员工总数的比例进行了深入分析。

按商业模式统计

如果我们将这些公司分为 B2B、B2C、金融科技和市场/实物商品,它们几乎完美地排列在一起,只有少数例外。两组之间的对比非常明显;例如,市场/实物商品的数据与工程师比率比 B2B 公司高 2-3 倍。

作者图片

为什么它们如此整齐地聚集在一起?让我们看看每种商业模式。

B2B(即 Onfido、Travelperk、Contentful) :这是数据与工程师比率最低的类别。这里的公司拥有更少、更大的客户,更难进行 A/B 测试和大规模部署机器学习模型。差异化通常是通过销售和工程努力实现的。

Fintech(即 Wise、Monzo、Revolut) :这些公司的数据与工程师比率较高,跨越 0.2x—0.3x。Monzo、Revolut 和 eToro 等公司的数据与工程师比率较高,而 N26、Starling 和 Tide 等公司的数据投资较少。金融科技公司通常能够利用数据直接改善其核心业务,例如预测欺诈、决定谁有资格获得贷款或简化客户运营。一个例子是 Monzo 的欺诈检测机器学习模型获得了一个奖项的提名。

B2C(即 Cazoo、Gymshark、Trustpilot) :这些公司更加分散,往往比 B2B 公司的数据与工程师比率更高,设计师与工程师比率最高。许多公司拥有数百万客户,能够很好地运行大规模 A/B 测试,部署机器学习模型,并通过数据推动他们的业务。一个很好的例子是 Cazoo 最近的例子建立了一个模型来预测广告支出的回报,以了解哪些渠道可以分配预算。

市场/实物商品(即 HelloFresh、Glovo、VOI) :这是当今数据与工程师比率最高的数据战场。这些公司的利润率很低,使用数据是他们保持竞争优势的方式。Deliveroo 就是一个很好的例子,它创建了一个调度引擎,自动将最佳骑手组合与客户订单匹配起来。

投资未来

如果你是一名数据或工程领导者,发现你的数据与工程比率远低于你的同行,你会怎么做?

可能还好吧。您的数据设置可能很简单,并且您已经获得了适量的自助服务功能,目前正在投资构建您的核心产品。

也许你还没有获得数据的全部价值,并且你怀疑是否要投入更多。您的数据团队可能会淹没在临时的请求中,因为没有足够的人来支持基础工作,或者您的工具可能已经过时。如果是这样的话,你就没有尽可能地以数据为导向,扩大数据团队可能是个好主意。

也许你落后了。如果您的竞争对手支持数据并直接使用数据来创造成本效益或更好的客户体验,您也应该这样做。

也许你可以领导。如果你在 B2B 领域,没有竞争对手经营数据业务,你能做到这一点脱颖而出吗?

如果你对你的公司有任何有趣的见解要分享,请通过 LinkedIn 联系我。

关于数据

我查看了 LinkedIn 上的关键词,包括了所有匹配项(例如,产品工程师将与工程师术语相匹配)

  • 数据:数据分析师、数据科学家、机器学习、数据工程师、数据经理、分析工程师、产品分析师、商业智能、数据主管/经理/总监/副总裁
  • 工程:工程师(不含数据工程师),技术/技术负责人
  • 设计:设计(er),用户体验,UX,用户研究

我故意没有包括所有分析师角色,这意味着诸如财务分析师、销售分析师和策略分析师等角色不被算作数据角色,尽管您可以将他们的一些工作归类为数据工作。

利用 PySpark 中的窗口函数进行数据转换

原文:https://towardsdatascience.com/data-transformation-using-the-window-functions-in-pyspark-c044e8be4de6

用一个用例来演示

菲利普·米尔托普在 Unsplash 上拍摄的照片

背景

我在一家保险公司做精算师。出于各种目的,我们(安全地)在数据仓库中为我们的投保人收集和存储数据。一个例子是索赔支付数据,需要对其进行大规模的数据转换,以获得对下游精算分析有用的信息。

为了证明这一点,我们销售的一种受欢迎的产品以收入流的形式提供理赔,以防投保人因受伤或疾病而无法工作(“收入保障”)。对于三个(综合)投保人 A、B 和 C,其收入保障索赔项下的索赔付款可以以如下表格格式存储:

表 1:索赔支付,按投保人 ID 进行颜色编码。按作者分类的表格

对该数据帧的直接观察是,对于一些字段存在一对一的映射,但对于所有字段并不存在。特别是,“投保人 ID”和“每月受益”之间,以及“索赔号”和“索赔原因”之间存在一对一的映射。然而,“投保人 ID”字段与诸如“支付起始日期”、“支付截止日期”和“金额”等字段之间的映射是一对多的,因为索赔付款随着时间的推移而累积并附加到数据帧。

如果我们想从特定的投保人窗口提取信息,该怎么办?例如,每个投保人的最后一次付款日期或付款次数。这可能很难实现(特别是使用 Excel,它是大多数人寿保险精算师的主要数据转换工具),因为这些字段取决于跨多行的值,如果不是特定投保人的所有行的话。为了直观起见,下表中添加了这些字段:

表 2:通过“窗口”提取信息,按投保人 ID 进行颜色编码。按作者分类的表格

从机械上来说,这首先涉及对特定投保人的“投保人 ID”字段应用过滤器,这为该投保人创建了一个窗口,对该窗口中的行应用一些操作,并在所有投保人中迭代。基于表 1 中的数据框架,本文演示了如何使用 PySpark 中的窗口函数轻松实现这一点。

一个保险用例

概括来说,表 1 具有以下特征:

表 1:索赔支付,按投保人 ID 进行颜色编码。按作者分类的表格

  • 索赔付款以表格形式记录。但是,任何字段都不能用作每笔付款的唯一关键字。
  • 保单 A、B 和 C 的月福利分别为 100、200 和 500 英镑。然而,支付的金额可能少于每月津贴,因为索赔人可能无法在某个月的整个期间工作。
  • 对 B 来说,索赔付款似乎在 20 年 2 月 15 日停止,然后在 20 年 3 月 1 日恢复。这种付款缺口对于估算索赔期限很重要,需要考虑到这一点。

让我们使用窗口函数来导出投保人级别的两个度量值,索赔的持续时间赔付率。这些措施定义如下:

  1. 每个投保人在 2010 年 6 月 30 日索赔的时间(索赔的持续时间),考虑到支付中的任何缺口。例如,这是 b 的 2.5 个月(或 77 天)。
  2. 在索赔期间,保单项下每月平均支付给投保人的金额("赔付率")。比如,这是投保人 A 和 B 的 100%,投保人 c 的 50%。

对于人寿保险精算师来说,这两个指标与索赔准备金相关,因为索赔持续时间影响未来付款的预期数量,而赔付率影响这些未来付款的预期支付金额。

下面提供了如何使用窗口函数导出这两个测量值的分步指南。

索赔期限

步骤 1 —导入库并启动 Spark 会话

import numpy as npimport pandas as pdimport datetime as dtimport pysparkfrom pyspark.sql.window import Windowfrom pyspark.sql import functions as Ffrom pyspark.sql import SparkSession## Initiate Spark sessionspark_1= SparkSession.builder.appName('demo_1').getOrCreate()df_1 = spark_1.createDataFrame(demo_date_adj)

步骤 2—定义窗口

由于我们是在保单持有人层面获取信息,因此主要关注窗口将是定位每个保单持有人信息的窗口。在下面的 Python 代码中:

  • Window_1 是“投保人 ID”上的窗口,进一步按“支付开始日期”排序。
  • Window_2 仅仅是“投保人 ID”上的一个窗口。

尽管 Window_1 和 Window_2 都提供了对“投保人 ID”字段的查看,但 Window _ 1 furhter 按照“支付日期”以升序对特定投保人的索赔付款进行排序。这对于使用“滞后”窗口函数推导支付差额非常重要,这将在步骤 3 中讨论。

## Customise Windows to apply the Window Functions toWindow_1 = Window.partitionBy("Policyholder ID").orderBy("Paid From Date")Window_2 = Window.partitionBy("Policyholder ID").orderBy("Policyholder ID")

第 3 步-索赔持续时间的窗口函数

“with_Column”是用于在数据帧中创建新列的 PySpark 方法。

创建以下各列是为了导出特定投保人索赔的持续时间。按此顺序:

  • 首次支付日期—这是特定投保人在 Window_1(或 Window_2)期间的最小“支付开始日期”。
  • 最后支付日期—这是特定投保人在 Window_1(或 Window_2)期间的最大“支付日期”。
  • 每次付款的索赔持续时间-这是每条记录的索赔持续时间,计算方法为每行的最后一次付款日期减去第一次付款日期
  • 每个投保人的索赔持续时间 —这是对特定投保人在 Window_1(或 Window_2)期间的上述“每次付款的索赔持续时间”列求和,得出一个行前总和(即索赔的总持续时间)。
df_1_spark = df_1.withColumn("Date of First Payment", F.min("Paid From Date").over(Window_1)) \.withColumn("Date of Last Payment", F.max("Paid To Date").over(Window_1)) \.withColumn("Duration on Claim - per Payment", F.datediff(F.col("Date of Last Payment"), F.col("Date of First Payment")) + 1) \.withColumn("Duration on Claim - per Policyholder", F.sum("Duration on Claim - per Payment").over(Window_2)) \ 

如前所述,对于投保人而言,理赔付款之间可能存在支付缺口。换句话说,在预定义的时间窗内,特定支付的“支付开始日期”可能不会紧跟在先前支付的“支付截止日期”之后。您应该能够在表 1 中看到投保人 b 的情况。

出于精算分析的目的,需要确定投保人的支付缺口,并从最初计算为首次和最后一次支付日期之间的差异的索赔持续时间中扣除。

支付差额可以使用下面的 Python 代码得出:

.withColumn("Paid To Date Last Payment", F.lag("Paid To Date", 1).over(Window_1)) \.withColumn("Paid To Date Last Payment adj", F.when(F.col("Paid To Date Last Payment").isNull(), F.col("Paid From Date")) \.otherwise(F.date_add(F.col("Paid To Date Last Payment"), 1))) \.withColumn("Payment Gap", F.datediff(F.col("Paid From Date"), F.col("Paid To Date Last Payment adj")))

使用视觉效果来解释上述步骤可能更容易。如下表所示,调用窗口函数“F.lag”以返回“支付到最后付款日期”列,对于投保人窗口,该列是前一行的“支付到最后付款日期”,如蓝色箭头所示。然后将其与当前行的“支付起始日期”进行比较,得出支付差额。正如预期的那样,我们对投保人 b 有 14 天的支付缺口。

出于计算支付缺口的目的,使用 Window_1,因为索赔支付需要处于“F.lag”函数返回期望输出的逻辑顺序中。

表 3:得出支付差额。按作者分类的表格

在下面添加点睛之笔,给出索赔的最终期限,现在与投保人 ID 是一对一的。

.withColumn("Payment Gap - Max", F.max("Payment Gap").over(Window_2)) \.withColumn("Duration on Claim - Final", F.col("Duration on Claim - per Policyholder") - F.col("Payment Gap - Max"))

下表显示了用上面的 Python 代码创建的所有列。

表 4:在 PySpark 中创建的所有列。按作者分类的表格

支付比率

赔付率被定义为投保人的实际赔付金额除以索赔期间的月收益。这衡量的是某一特定投保人每月支付的福利金额。

利用之前导出的索赔持续时间,可以使用下面的 Python 代码导出赔付率。

.withColumn("Amount Paid Total", F.sum("Amount Paid").over(Window_2)) \.withColumn("Monthly Benefit Total", F.col("Monthly Benefit") * F.col("Duration on Claim - Final") / 30.5) \.withColumn("Payout Ratio", F.round(F.col("Amount Paid Total") /  F.col("Monthly Benefit Total"), 1))

如下表所示,输出符合预期。要在 PySpark 会话中显示输出,只需添加 显示()的末尾代码。

表 5:支付率。按作者分类的表格

其他窗口功能的例子

还有其他有用的窗口函数。这篇文章提供了一个很好的总结。

例如,您可以使用下面的窗口函数 F.row_number()为每个投保人的支付次数设置一个计数器,您可以对其应用窗口函数 F.max()以获得支付次数。

.withColumn("Number of Payments", F.row_number().over(Window_1)) \

与精算师更相关的另一个窗口函数是 dense_rank() 函数,如果在下面的窗口中应用该函数,则能够捕获同一投保人在不同索赔原因下的不同索赔。这种方法的一个应用是大规模识别索赔是以前原因的复发还是对投保人的新索赔。

Window_3 = Window.partitionBy("Policyholder ID").orderBy("Cause of Claim")....withColumn("Claim_Cause_Leg", F.dense_rank().over(Window_3))

擅长

正如我在之前的文章中提到的,Excel 已经成为澳大利亚大多数人寿保险精算师的首选数据转换工具。与本文中讨论的一个用例类似,本练习中所需的数据转换很难用 Excel 实现。

鉴于其可伸缩性,将 PySpark 用于涉及大型数据集的商业应用程序实际上是显而易见的。也就是说,对于这种情况,确实存在一个 Excel 解决方案,它涉及到高级数组公式的使用。

简要概述在 Excel 中创建窗口的步骤:

  1. 根据表 1,通过“投保人 ID”和“支付日期”字段对数据帧进行手动排序。将“投保人 ID”字段复制并粘贴到新的工作表/位置,然后进行重复数据删除。
  2. 引用原始表(即表 1),分别应用带有最小值/最大值的行公式,以返回特定投保人第一次和最后一次索赔付款的行引用(这是一个数组公式,需要合理的时间来运行)。例如,如下表所示,这是投保人 a 的第 4–6 行。
  3. 根据上面的行引用,使用地址公式返回特定字段的范围引用。例如,保单持有人 A 的 G $ 4:$ 6 如下表所示。
  4. 将间接公式应用于步骤 3 中的范围,以获得首次付款日期和上次付款日期。

表 6: Excel 演示。按作者分类的表格

结论

通过一个实例,本文展示了 PySpark 中各种窗口函数的使用。

根据我自己使用数据转换工具的经验,PySpark 在很多方面都优于 Excel,比如速度和可伸缩性。这个用例支持在某些数据转换任务中脱离 Excel 的情况。

如果您喜欢阅读数据科学技术的实际应用,请务必关注或浏览我的 Medium profile 以获取更多信息!

Gallia 的 Scala 中的数据转换:版本 0.4.0 已经过时

原文:https://towardsdatascience.com/data-transformations-in-scala-with-gallia-version-0-4-0-is-out-f0b8df3e48f3

介绍这个增压版本的新功能

Unsplash 上的 Shubham Dhage 拍摄的照片

这是我之前对 Gallia 的介绍的后续文章,Gallia 是 Scala 中的模式感知数据转换库。本文将关注最新版本中包含的最重要的变化: 0.4.0 (适用于 Scala 2.122.13 )。

目录

从 Apache Avro/Parquet 读取/写入

回想一下上一篇文章,Gallia 中的典型处理过程如下:

它产生:

**<root>
 title            _String
 air_date         _String
 doctor           _Inttitle                   | air_date         | doctor
----------------------- | ---------------- | ------
THE ELEVENTH HOUR       | 3 April 2010     | 11    
THE DOCTOR'S WIFE       | 14 May 2011      | 11 
...**

****注:一个 TSV 版本的剧集数据集可作为要点在此

阿帕奇 Avro

类似地,Avro 文件现在可以通过以下方式使用:

这产生了完全相同的结果。

相反,可以用.writeAvro(“./mydata.avro”)将结果写入 Avro 文件

备注:

  • 原始文件本身是否是 Avro 文件并不重要,因为 Gallia 中的输入和输出是完全独立的
  • 上述说明的一个直接后果是,人们可以纯粹出于转换目的使用 Gallia:Avro->JSON,Avro- > TSV,Avro- > Parquet,Parquet- > Avro 等。但是有一点需要注意:数据必须符合 Gallia 的数据模型(例如,不使用 Avro 的映射,不使用 2+D 数组等)。
  • 将来,streamAvro/writeAvro方法将被通用的stream/write方法所取代,扩展作为参考(参见 I/O 部分)

阿帕奇拼花地板

要处理拼花文件而不是 Avro,代码完全相同,除了:

  • 模块名称是gallia-**parquet**而不是gallia-avro
  • 进口是import gallia.**parquet**._而不是import gallia.avro._
  • 方法名是stream**Parquet**/write**Parquet**而不是streamAvro/writeAvro

备注:

从案例类中阅读

让我们考虑下面的案例类:

 **case class **Person**(**name**: String, **age**: Int, **income**: Int)**

它现在可以作为一个单一的实体被摄取:

 **val peter = gallia.***aobjFromDataClass***(
      **Person**(**name** = "Peter" , **age** = 29, **income** = 80000))**

同时,其集合可以如下摄取:

举个例子,如下:

会产生预期的:

**<root>
  name             _String
  age              _Int
  hourly_rate      _Doublename   | age | hourly_rate      
------ | --- | ------------------
Peter  | 29  | 38.31417624521073
Joanna | 29  | 14.367816091954023
Samir  | 28  | 38.31417624521073**

****注意:虽然 Gallia 还不允许写入 case 类,但它将在下一个版本中发布——参见 NEXT_RELEASES (将通过反射和宏允许)

工会类型

0.4.0 中也增加了对联合类型的部分支持。

用法示例如下:

这会产生以下输出:

**<root> [... schema unchanged]name  | age
----- | ------------
Peter | 29
Samir | TWENTY-EIGHT**

因为toUpperCase是一个纯字符串操作,所以具有整数值的实体 age 保持不变。

上面的例子给出了一个简单的例子,但是当然还有更复杂的例子。例如,当多个嵌套实体是一个联合的一部分时:

它产生以下输出:

**<root> [... schema unchanged]{ "data":           "Michael (27yo)" }
{ "data": { "name": "**PETER**", "age": 29 }}
{ "data":           "Joanna (29 years old)" }
{ "data": { "name": "Samir", "dob": "March 14, 1971" } }**

只有值"Peter"是大写的,因为它是唯一具有 age 条目的嵌套实体。

备注:

  • 参见 union_types.md
  • 更多的例子可以在 UnionTypeTest.scala 中看到
  • Gallia 中的联合类型在这一点上仍然被认为是试验性的,并不是所有的操作都支持它们(但是基本的操作支持它们)。
  • 支持联合类型的主要原因之一是帮助数据清理/特性工程工作。事实上,在遗留数据集中,用不同类型捕获字段的情况非常普遍(想想true"Yes"之类的)

元架构

增加对联合类型支持的结果是 Gallia 能够提供自己的元模式:

这意味着 Gallia 可以自己进行模式转换。例如,下面是嵌套字段重命名的样子(这里从 fF ):

备注:

  • Gallia 实际上并没有在内部以这种方式使用元模式
  • 模式是数据的特例,即元数据(或“关于数据的数据”),因此 Gallia 的元模式也是元元数据。而且既然 Gallia 的 metaschema 也可以用来自己建模,那么它也是自己的meta schema。因此它也是元元元数据。很明显。**

复杂聚合

让我们重复使用早先中people手柄。汇总数据的一个非常简单的方法是:

**people.**group**("name" ~> "names").**by**("age")
  .printJsonl()**

它产生以下内容:

**{ "age": 29, "names": [ "Peter", "Joanna" ] }
{ "age": 28, "names": [ "Samir" ] }**

也可以(不一定)通过以下方式实现:

**people.**aggregateBy**("age").as("names")
    .**using** { _.strings("name") }
  .printJsonl()**

这产生了与上面相同的结果,但是显示了对aggregateBy的简单使用。

虽然 Gallia 中有更多的内置聚合器可用( SUM BYMEAN BY ,…),但是要在单个操作中实现这样的处理,就需要使用aggregateBy构造:

**people.aggregateBy("age").as("names")
    .using { _.strings("name").**concatenateStrings** }
  .printJsonl()**

它产生:

**{ "age": 29, "names": "**PeterJoanna**" }
{ "age": 28, "names": "**Samir**" }**

实际上是:

**people.aggregateBy("age").as("names")
    .using { _.strings("name")
      .**mapV** { _.reduceLeft(_ + _) } }
  .printJsonl()**

因此,它可以根据需要进行定制,例如:

**people.aggregateBy("age").as("names")
    .using { _.strings("name")
      .mapV { _.reduceLeft(
        **_.toUpperCase + "|" + _.toUpperCase**) } }
  .printJsonl()**

生产:

**{ "age": 29, "names": "**PETER|JOANNA**" }
{ "age": 28, "names": "**SAMIR**" }**

但是,aggregateBy构造的真正威力在于能够实现以下更具定制性的聚合类型:

**people.aggregateBy("age").using { **group** =>
    **(**"names"       **->** **group**.strings("name")**,**
     "mean_income" **->** **group**.ints   ("income").mean**)** }
  .printJsonl()**

这导致:

**{"age": 29, "names": [ "Peter", "Joanna" ], "mean_income": 55000.0}
{"age": 28, "names": [ "Samir" ],           "mean_income": 80000.0}**

****注意:上面使用的基于元组的实体创建只是更显式的gallia.headO("names" -> ...)的简写,最多可用于 5 个条目

复杂的转换/协同转换

让我们切换到一个更严肃的领域来突出这些新特性。考虑以下数据集:

****注意:提醒一下bobjbobjs构造是一种方便的机制,允许构造那些模式很容易推断的实体。因此bobj("f" -> 1)相当于更显式的aobj("f".int)(obj("f" -> 1))

通过数据类的转换(用于嵌套实体)

Gallia 现在提供了通过 case 类(“数据类”)转换嵌套实体的能力。例如,考虑:

**case class **Change**(
   **chromosome**: String,
   **position**  : Int   ,
   **from**      : String,
   **to**        : String) {

  def **shorthand**: String=
    s"${**chromosome**}:${**position**};${**from**}>${**to**}"

}**

它对上面的mutations数据集中的变化嵌套实体进行建模,并封装一个操作,该操作为遗传变化产生一个简写符号(例如"3:14532127;C>GG")。

下面的代码将把变更实体转换成它的速记副本:

**mutations
  .transformDataClass[**Change**]("**change**")
    .using(_.**shorthand**)
  .display()**

它产生:

**[...]patient_id | age | change
---------- | --- | ----------------
p1         | 23  | 3:14532127;C>GG
p2         | 22  | 4:1554138;C>T
p3         | 21  | Y:16552149;AA>GT**

注意事项:

  • 这将通过.transformDataClass[**Option**[Change]].transformDataClass[**Seq**[Change]]、和 .transformDataClass[**Option**[**Seq**[Change]]应用于嵌套实体的可选/必需和单个/多个的任何其他组合
  • Gallia 的后续版本将利用宏使这种机制更加有效(目前依赖于反射)

通过数据类的协同转换(针对当前级别)

现在让我们考虑下面的 case 类,它模拟了当前级别的字段子集(这次与嵌套实体相反):

**import java.time.Yearcase class **Demographics**(
    **age**: Int,
    **sex**: String) {

  deftoNewDemographics =
    **NewDemographics**(
      **year_of_birth**    = Year.*now*().getValue - age,
      **has_Y_chromosome** = sex == "male")

}**

以及对期望的模型变化建模的以下 case 类:

**case class **NewDemographics**(
    **year_of_birth**   : Int,
    **has_Y_chromosome**: Boolean)**

以下代码通过使用 origin case 类中的封装方法来共同转换这两个字段:

**mutations
  .cotransformViaDataClass[**Demographics**]
    .usingWithErasing(_.**toNewDemographics**)
  .display()**

这导致了预期的结果:

**<root>
 patient_id       _String
 year_of_birth    **_Int**
 has_Y_chromosome **_Boolean**
 change ...{ "patient_id"      : "p1",
  "change"          : { ... },
  "year_of_birth"   : **1999**,
  "has_Y_chromosome": **true** }
...**

备注:

  • 按照现在的情况,分别转换年龄和性别更有意义,但是这里的目标是强调一个联合转换,其中的字段可以任意混合和匹配
  • .usingWithErasing删除原始条目,而.using会保留它们

通过定制处理进行联合转换

Gallia 现在还为真正的定制处理提供了改进的机制,尽管这通常不是一个好主意(因为我们失去了模式/数据将相应地共同发展的保证)。例如,为了重现上面的共同转换,我们可以创建下面的对象,扩展gallia.ObjToObj:

下面的代码将利用它:

**mutations
  .custom(**CustomDemographicsTransformation**)
  .display()**

这将产生相同的输出。

新支持的类型

Gallia 现在支持其他类型:

  • 枚举
  • 二进制数据
  • 时间类型

枚举

枚举可以这样创建/使用:

它产生:

**<root>
  choice           _Enm(List(rock, paper, scissors, **spock**))choice
------
**paper****

二进制数据

可以像这样创建/使用二进制数据:

它产生:

**bin
-----------
base64:**Ym9v****

备注:

  • "Ym9v""boo"编码到 Base64 中,因为我们在'f'字节(0x66)的地方放了一个'b'字节(0x62)。
  • Base64 输出仅用于序列化,内存中的表示在整个处理过程中保持不变

时间类型

Gallia 中支持的时态类型与它们的 Java 对应物相匹配:java.time.{LocalDate, LocalTime, LocalDateTime, OffsetDateTime, ZonedDateTime, Instant}

它们可以这样创建/使用(例如使用LocalDateTime):

它产生:

**<root>
   hungover         _LocalDateTime**hungover**
-------------------
**2022-01-01**T00:00:00**

结论

这就结束了我们对 0.4.0 带来的主要变化的浏览,至少是那些将改善客户端代码体验的变化。

其他值得注意的新增内容包括:

有关此版本带来的变更和改进的更完整列表,请参见 CHANGELOG.md

随时欢迎反馈!

数据独角兽很少见,那就雇佣这 3 个人吧

原文:https://towardsdatascience.com/data-unicorns-are-rare-so-hire-these-3-people-instead-80d3c3808af8

数据架构

如果数据工程师就像水管工,你还需要哪些技能来构建你的数据堆栈?

当考虑在公司中构建数据架构时,经常出现的一个问题是:

您应该雇佣谁来构建(并在以后维护)您的数据架构?

不管公司里的一些人是否已经与数据相关,我认为 3 种主要技能必须由不同的个人掌握:

  • 数据工程
  • 数据分析
  • 数据管理

正如我在以前的文章中所说,任何数据堆栈都由四个核心组件和两个基础组件组成:

作者图片(原始来源)

从提取到利用数据源提供给业务涉众的信息,所需的技能范围非常广泛。虽然第一个核心数据块(提取、加载、存储)需要非常专业的技能,但构成可靠数据堆栈其余部分的数据块(转换、利用、治理)需要更多面向业务的技能。

一个人有可能完全掌握任何数据堆栈的全部内容吗?

我相信,即使对于一个人来说构建和维护这样一个架构是可能的,这也可能是不可取的。我来解释一下原因。

为了正确地进行建筑工作,每个工人都必须专攻他们的专业领域。这并不意味着工人必须忽视其他专业领域的同事。恰恰相反:无论应用领域如何,开放的交流是成功的关键之一。但是,如果我们拿建筑工程中的管道部分来说,每个工人的专业技能和他们之间的密切合作是如何使建筑工程取得成功的呢?

当我们谈论数据架构的时候,我发现通过具体的举一个在建房屋安装管道的例子来与建筑业进行比较是很有趣的。在本文中,我将介绍我认为对实现和维护数据堆栈起决定性作用的 3 个主要角色。

团队合作——由Unsplash上的看不见的历史拍摄的照片

数据工程师就像水管工

在构建一个可持续的数据架构所需的广泛技能的一个方面,提取数据并将其加载到给定的基础设施(数据湖和/或数据仓库)需要硬技能。其中包括数据库管理知识、计算和存储资源监控、掌握用于处理大数据的工具(例如 Apache Spark、Python)以及 DevOps 任务的基础知识。

在建造房子的过程中,数据工程师将成为水管工,因为他们掌握着将源(字面意思,数据源)连接到数据将被转换和利用的进一步工具的艺术。有趣的是,一个名为“数据科学的管道工”的媒体出版物收集了关于数据工程的文章。管道工的工作是确保管道连接正确,这样干净的水就能流入、流经指定的管道,并从指定的出水口流出。同样,数据工程师的工作是将正确的数据管道连接到正确的“数据入口”。这些是数据堆栈的“提取”和“加载”块。

至于“存储”部分,数据工程师可能是负责管理数据存储方式的人。或者,他们可以通过与其他技术角色(如开发人员或公司技术基础设施领域的专业开发人员)合作来实现这一目标。借用我们的管道比喻,如果给定建筑工地的管道工负责管理房子的水箱,而不仅仅是水管,情况就会是这样。

数据分析师就像项目负责人

在建筑工地上,如果管道工工作正常,水应该从进水口通过管道流到水槽。现在怎么办?管道工程业主的职责是确保未来的住户将充分受益于引入水槽的水。管道工程业主有责任确保水流向正确的终点,并且终点位于对未来住户有意义的地方。

这就是数据分析师在我们的数据架构中发挥作用的地方。从数据工程师交付的地方接管数据流是他们的任务之一。根据数据角色之间的职责分配,数据工程师或数据分析师可以接管“转换”块。最重要的是,每一条数据的责任都讲清楚了。

然后是“杠杆”块,对应于数据分析师的主要任务:将原始(或转换后的)数据转化为对业务利益相关者有用的见解。就像管道工程项目负责人要确保水以足够高的质量水平输送到正确的终点一样,数据分析师要确保向业务用户提供可靠的数据,并且他们能够理解这些数据。

数据经理就像是现场监督员

如同在施工现场一样,要有一个人负责做好各专业之间的协调。从水处理厂到房子,这通常是现场主管的责任,与管道工和项目业主密切合作,检查房子是否与其余的水网络连接。

通过对数据架构的“治理”部分负责,数据经理保证了数据角色之间的协作和协调,而且在涉及到数据相关问题时,还保证了整个公司的协作和协调——可能涉及广泛的主题。在出现问题时,数据经理集中信息和决策,以便以最好的方式集体解决问题。至于数据质量,就像现场主管一样,数据经理的职责是确保通过管道从源到输出的数据流的质量。

我这里说的数据经理,可以对应公司内部的几个职称:数据管家、数据团队负责人、数据负责人等。对于小公司来说,这个角色甚至可能不会作为一个职位头衔出现,因为它更像是管理数据团队的经理的任务之一。

结论

最后,上图显示了健壮数据堆栈的关键数据角色:

图片由作者提供,图标由 Gan Khoon Lay 提供(来源)

正如我在引言中提到的,我相信有效的数据架构是那些从不断扩展的数据技能和技术领域中选择专攻一个专业领域的人之间伟大协作的结果。这就是为什么我把安装管道设施比作在一所房子里。

然而,我完全支持相互好奇和公开交流最佳实践。根据我自己的经验,虽然我习惯于关注数据堆栈的数据分析部分,但我喜欢学习和深化我的数据工程知识。这只会提高我对“大数据图景”的理解,并使我在执行典型的面向数据分析师的任务时变得更加相关。

你喜欢读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!

https://marie-lefevre.medium.com/membership

高质量的数据伴随着高质量的验证

原文:https://towardsdatascience.com/data-validation-for-pandas-b24613959364

以下是如何在数据管道的每个阶段确保熊猫数据帧的质量

照片由来自佩克斯的哈里森坎德林拍摄。

通过挑选正确的垃圾,你可以在垃圾中建造一座城堡。

大多数数据科学项目都是这样——建造城堡🏰从垃圾桶里。在数据湖中数千个数据集的池中,您需要挑选正确的数据集并修复几乎正确的数据集。

你需要一个强大的数据集验证工具。

数据质量是任何现代分析项目的一个基本方面。但是我验证数据集的老派技术有更多的漏洞🐛比蝴蝶还多。

我写自己的验证码;有很多异常处理。

尝试不同的逻辑将花费大量的时间来重新编码😦。此外,我需要编写专门的文档来理解我的验证逻辑。

但是我最近发现了一个 Python 库,它去掉了大部分样板文件。有了它,您可以为模式中的 Pandas 数据帧编写更直观的验证逻辑。

它删除了我大量的代码。相反,几行代码提供了更强大的处理验证错误的方法。

来自吉菲的动画。

让我们学习如何使用熊猫验证工具包 Pandera 来确保高质量的数据。

在本帖中,我们将讨论

  • 使用 YAML 配置验证 Pandas 数据框架:
  • 可在数据管道中的任何位置重用的验证批注;
  • 定义动态验证,以及。
  • 用复杂假设验证数据框架。

但是在我们做任何事情之前,让我们在你的计算机上安装 Pandera。

pip install pandera

让我们也创建一个虚拟数据集来与示例一起工作。

用 YAML 配置验证熊猫数据帧

我喜欢 YAML 的配置!它们易于理解并且可以灵活扩展。做一个或者改装一个不需要 130 的智商。

YAML 文件是分层的键值映射。把它们想象成 Python 中没有花括号的字典。项目列表可以放在以连字符开头的单独的行中。您可以使用缩进来扩展子配置。

要更深入地了解 YAML 文件,请参阅 Alek Sharma 的本指南

下面的 YAML 文件对我们的数据集做了一些基本的检查。配置不是很直接吗?

请阅读代码上的注释,以便更好地理解验证检查。

对于数据集中的每一列,我们指定字段是否可以有空值、重复项等。我们还可以测试字段值是否在预定义的列表中,或者确认逻辑,比如小于特定的数量。

让我们在项目中使用它来验证我们的虚拟数据集。

上面的代码从文件系统中读取 yaml 文件,并从中创建一个模式。然后,我们可以使用 schema 对象来验证数据帧。

上面的验证调用将返回 df,因为什么也没有发生。😧

但是让我们稍微改变一下我们的 YAML 文件。请注意,新版本有一个额外的验证来检查年龄变量是否小于 60。

如果再次尝试验证示例数据帧,您将看到以下错误。

Pandera 引发的验证错误。图片由作者提供。

这个错误是工具性的。除了提高它,Pandera 还打印出不符合要求的记录。

您可以使用普通的 Python 代码来实现这个逻辑。但是对于我们和任何阅读我们代码的人来说,使用 YAML 文件更舒服。

使用验证注释方便地测试管道中的数据帧。

在复杂的管道中,您需要在不同的点测试您的数据帧。通常,我们需要在转换前后检查数据完整性。

您可以像以前一样使用已加载模式的验证方法。但是,Pandera 有一个更好的方法,使用函数注释。

Pandera 有 check_input 和 check_output 注释。当用这些来注释一个函数时,它将测试函数参数和返回值中的数据帧。

要使用它,我们需要定义两个模式;一个用于输入(参数),另一个用于返回数据帧。

除了前面的 YAML 文件,让我们创建另一个如下指定的文件并加载它。姑且称之为schema2

下面是一个使用注释来验证函数的输入和输出数据帧的示例。请注意,Pandera 假设第一个参数是 dataframe。如果您有多个参数,请确保第一个是您的数据帧

Pandera 对上述功能进行了两次测试。它在函数执行之前用schema 测试输入数据集。此外,在功能执行后不久,用schema2 测试输出数据帧。

玩弄schema2,诱发一些错误。

动态定义模式以实现快速验证。

从 YAML 文件加载并不是创建模式的唯一方式。然而,这是我最喜欢的方式。您可以完全在 Python 代码中创建模式。

下面是我们第一个 YAML 文件的 Python 版本。注意相似之处。

在 Python 代码中定义模式有一个额外的好处。除了内置检查之外,您还可以定义自定义检查。

例如,下面的代码检查 order_value 列的总和是否大于 1000。

但是您很少需要编写定制代码,因为最常用的代码已经包含在模块中了。你只要抓住正确的。

用复杂的假设验证管道中的数据框架。

在所有伟大的功能中,这是我最喜欢的。

检查数据帧中的常见异常是没问题的。但是,做完整的假设检验是一个游戏改变者。我们可以根据更复杂的假设来验证数据集。

例如,在我们的购物数据集中,我们可以检查男性顾客是否比女性顾客购买更多。

下面的代码可以做到这一点。注意,要使这个工具工作,您需要安装假设和 scipy。

pip install hypothesis scipy pandera[strategies,hypothesis]

请注意,我们要测试的关系是“大于”由于我们的样本数据集没有证据支持这一假设,它将失败。但是如果你把关系改为“相等”,测试就通过了。此外,“等于”是默认设置。所以你可以把它拿掉。

最后的想法

验证数据帧从未如此简单。

我曾经为我需要执行的每个检查编写专用代码。当你在许多数据集上工作时,这是一件棘手的事情。当您在数据管道上工作时,经常会遇到这种情况。

您需要在每个数据集上编写类似的代码。通常我的代码分布在几个笔记本上。

但随着潘德拉的发现,我可以在一个地方管理他们。我更喜欢把它们放在 YAML 的档案里。但是我们也可以在 Python 代码中定义它。

它从我们的代码中提取了大量的样板代码。它使我们的代码更具可读性,更不容易出错,并提供更多信息。

此外,我们还可以在数据集列上使用假设检验,这非常棒。

感谢阅读,朋友!在LinkedInTwitterMedium上跟我打招呼。

还不是中等会员?请使用此链接 成为会员 因为,不需要你额外付费,我为你引荐赚取一小笔佣金。

满怀期望的数据验证和 Argo 工作流

原文:https://towardsdatascience.com/data-validation-with-great-expectations-and-argo-workflows-b8e3e2da2fcc

远大前程来自库伯内特土著

最近在工作中,我们一直在讨论我们梦想中的机器学习管道会是什么样子。作为背景研究,我们遇到了 Great Expectations 包,作为我们管道中的数据验证步骤,它看起来很有希望。在这篇文章中,我将探索使用 Great Expectations 作为 Argo 工作流的一部分,在数据预处理期间运行数据验证。

声明:我不是专家!对我来说,这是一个学习过程,因此我在这里实施的可能不是最简化的解决方案。Great Expectations 可以直接与 Prefect 和 Dagster 集成,所以理论上它更容易与这些工具一起使用。我只是喜欢阿尔戈。

凯瑟琳·麦考马克Unsplash 上拍摄

1。设置我们的环境

对于这个项目,我在一个小型集群上运行所有的东西。我不打算详细介绍每个方面,我假设您已经安装了 kubectl 和 helm 3。

首先启动您的 minikube 集群:

minikube start --cpus 4

对于其他项目,我使用 4 个 CPU 来运行我的程序,但是对于我们的例子来说,这并不是必需的,因此“CPU”参数是可选的。

接下来安装 Argo 工作流:

kubectl create ns argo
kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-workflows/master/manifests/quick-start-postgres.yaml

我们需要等待一段时间,让一切都启动并运行起来。如果您看到一些“错误”和“CrashBackLoopOff”状态,不要担心,通常它会在 5 分钟内自动解决。

每次我安装 Argo 工作流时,我都喜欢运行一个示例工作流,只是为了确保前面没有错误。让我们通过端口转发访问我们的工作流用户界面。

kubectl port-forward svc/argo-server 2746:2746 -n argo

现在进行到 https://localhost:2746。你会收到 Chrome 的安全警告(幸好这不是生产系统)。你应该看到的是阿尔戈工作流程开始屏幕。

图一。Argo 工作流程开始屏幕。随意探索侧面板选项 Argo 项目真的很酷。图片作者。

单击“提交新工作流”,然后单击“使用完整工作流选项进行编辑”。它会给你一个 Hello World 的例子,你可以通过点击“+ Create”来运行。如果一切顺利,您应该会看到如下内容:

图二。一次成功的试运行。如果您在这一点上遇到了问题,请随时留下您的评论。排除故障的最佳方法是查看 pod 日志。图片作者。

我们仍然需要做一些整理工作来远程访问我们的 Argo 服务器。奇怪的是,如果我遵循 Argo 的文档,它不能工作,所以这里有一个修改后的版本,可以让你从 Hera 提交工作流。

kubectl create role hera --verb=list,update,create --resource=workflows.argoproj.io -n argokubectl create sa hera -n argokubectl create rolebinding hera --role=hera --serviceaccount=argo:hera -n argoSECRET=$(kubectl -n argo get sa hera -o=jsonpath='{.secrets[0].name}')kubectl -n argo get secret $SECRET -o=jsonpath='{.data.token}' | base64 --decode

现在记下这个令牌。这样我们就有了一个 Argo 工作流的工作装置!下一步是让 Minio 实例在 minikube 中运行。这可以很容易地实现使用舵图表。

helm repo add bitnami [https://charts.bitnami.com/bitnami](https://charts.bitnami.com/bitnami)
helm repo update
helm install ge-minio bitnami/minio --namespace argo

在这种情况下,我选择将它安装到 argo 名称空间中,只是为了让自己的东西更整洁一些。最后一个命令将生成一些关于如何获得您的 Minio 凭证的输出。运行这些命令并将输出存储在某个地方。现在我们已经准备好创建我们的项目桶了。要检查您是否有正确的凭证,并感受一下 Minio,请将它的服务进行端口转发,并导航到 localhost:9001。

kubectl port-forward svc/ge-minio 9001:9001 -n argo

在这里,我创建了一个名为“远大前程”的桶。

最后,让我们安装一个包来用 python 创作 Argo 工作流。

pip install hera-workflows

现在我们设置好了!

2。管道组件

说完这些,让我们来谈谈我们的示例数据验证管道。

图片作者。

  1. Faker :快速生成虚假数据集的 python 包。
  2. 远大前程:一个用于数据验证的 python 包。
  3. Minio :类似亚马逊 S3 的对象存储。

这些组件中的每一个都有非常棒的文档,并且有一些博客比我能提供的更详细,但是我会涵盖对这个项目很重要的几点。

通过 Faker,我们希望生成一个熊猫数据框架的例子,它可以作为数据科学管道的输入数据。我们可以使用 faker 很简单地做到这一点:

现在我们有了一个数据集,其中包括带有一堆随机生成的信息的虚拟人,以及用 NumPy 生成的两个数字特征列。

现在是时候以编程方式从这些数据中生成期望了。我个人觉得这有点复杂,但我想我们会成功的!

现在我们用煤气做饭。我们有能力生成假数据,从中创建一个验证套件,并运行该验证套件。添加 Minio 只是压缩/ge-store 目录,并使用 python SDK 将文件放入我们的 bucket 中。在 Minio 文档中提供了一个很好的概述。

3。阿尔戈管道

在最后一部分,我们将使用 Hera 来编写和运行我们的测试管道。为了正确使用 Hera,我们需要一个 Docker 镜像来覆盖我们所有的依赖项。为了运行我们的管道,我制作了一个简单的图像 lambertsbennett/argo-ge:v1。它基于 Python 图像,因此非常大。

现在让我们从编写管道 1 开始,以生成和存储我们的期望。

在许多小错误之后,这里是将我们的远大前程套件作为一个 tarball 存储在我们的 Minio bucket 中的代码。

现在让我们在第二个管道上工作,生成一些 faker 数据,从 Minio 中提取我们的验证套件,然后运行我们的数据验证。我们将重用第一个管道步骤来生成假数据,以及我们对文件 io 所做的大量工作。

现在,如果我们导航到 Minio 并下载我们的“ge-results.tar.gz ”,深入了解一下,我们可以看到我们数据验证工作的详细结果。在/uncommitted/data _ docs/local _ site 下的归档文件中,我们可以看到一个 index.html。

图 4。我们的巨大期望的结果。哦,不,我们失败了!图片作者。

这个期望套件中有一些非常严格的规则,所以我们的验证失败了。

我希望这是一本有趣的读物。该代码可在 Github @https://github.com/lambertsbennett/argo-great-expectations上获得。《远大前程》看似很强大,其实很复杂。幸运的是,他们的文档非常棒。在未来,我将使用基于 kubernetes 的栈研究更多的 MLOP/数据工程/数据科学主题,并希望写更多关于它的文章。感谢您的阅读,如果您有问题或改进的想法,请留下您的评论!

Web 应用程序上多个服务提供商的旅行路线数据可视化——使用 LeafletJS + NodeJS 构建

原文:https://towardsdatascience.com/data-visualisation-of-travel-routes-by-multiple-service-providers-on-web-app-built-with-leafletjs-dee2117647e9

哪条路是❝ route❞?移动响应地图布局。包括完整的源代码。

M 随着当今“地方病”时代全球消费者需求的激增,任何数字商品,如电子商务购买、食品配送请求以及打车服务,都变得越来越有利可图。除了其无纸化(即数字化)的性质,上述每种服务的另一个共同特征是其依赖于最优路线规划以获得经济效率。

随着Gojek优步等主要市场参与者发现自己在这个竞争激烈的领域与新进入者打交道,在打车市场取得成功的基石无疑一直是地理空间技术&工具的有效使用。

作者插图|显示 Grab/优步司机在地图提供商建议的路线上导航的经典场景

地理空间辅助项目的理由

对于日常事务,我们大多数人直觉地求助于谷歌地图来获得❝可导航的❞旅行路线,以便到达我们的目的地。

然而,在宏观规模或商业层面上,大型企业(如物流运输)很少(如果有的话)将此应用于他们的实践,因为他们很清楚可航行路线≠最佳路线

因此,为了更深入地了解旅行推销员问题并理解建议路线的潜在决策,我决定将 3 个不同的路由 API 集成到一个 web 平台上,以便更容易地对建议的旅行路线进行跨服务交叉比较。

网络应用功能

(可通过https://SG-routing-app . glitch . me访问,具有移动响应布局。)

集成了 3 个路由 API

用例演示:在新加坡的街道上导航

  • 运输方式:汽车
  • 原点:哈博中心
  • 目的地:武吉提玛路/公爵夫人路

其他特性

  • 在 3 种路由服务之间切换
  • 语音合成导航指令与❝ 朗读 ❞功能

应用程序预览(移动 Android 布局)

作者截图|从左到右,地图服务商分别是— OnemapGraphhopper此处

显然,对于相同的输入起点&目的地,所有 3 个地图服务提供商都为假定的驾驶员返回了不同的导航路径。这个简单的概念验证证明了每条旅行路径背后的运行算法各不相同,并且在很大程度上取决于正在处理的业务用例。假设企业的目标是实现利润最大化,那么合理的假设是,定制条件(例如,免费、最少遇到红绿灯)最好设置到位,以便用获得的总收入抵消运输产生的总成本

参考消息:我的 GitHub repo 上的应用源代码 sg-routing-app (请随意🔱或者⭐它,并为您自己的用例进行调整!)

额外的概念验证(网络图论)

出于纯粹的好奇,在我将网络图背后的理论应用于各种领域的自我探索中,我决定根据开放街道地图(OSM) 的开源街道网络,与谷歌地图的返回路线并排绘制最短路径,以获得相同的起点-终点地理坐标:

  • 产地:新加坡海滨大道 1 号 098585 |纬度:1.264313825,液化天然气:103.8220734
  • 目的地:新加坡阿尔卑斯大道 101 号,邮编 498793 |纬度:1.375412026,液化天然气:104.025137
  • 模式:驱动

图片作者|左边返回一条由 Python 包 networkx 计算的路径|右边返回由 Google Maps 返回的路线(一定要注意返回了多条可能的路线)

结果:对于几个地点, Google Maps 建议的路线中至少有一条与图论计算的最短路径的坐标重叠!

总体评价和评论

因为决定出行成本的因素(过路费、路线距离、所提供商品的性质等。)因地而异,一个基本假设是❝ 最佳路径 ❞,尽管是最短的路径,但毕竟可能不是为驾驶员实现最大成本节约的路径。

虽然全球地图服务提供商如 Google Maps 会返回一条全球可导航的路径,但事实上,许多商务旅行者会将他们的最终交付路线建立在更加以位置为中心的地图服务(可以是商业的或内部开发的)上,以便不仅满足客户需求,而且以尽可能低的成本实现其业务 KPI。

现在你知道了!非常感谢你坚持到这篇文章的结尾!❤希望这篇文章对你有用,如果你想了解更多地理信息系统(GIS)、数据分析& Web 应用相关的内容,请随时关注我的媒体。会非常感激—😀

— 🌮请给我买个玉米卷🎀˶❛◡❛)

https://geek-cc.medium.com/membership

数据可视化:超越图表

原文:https://towardsdatascience.com/data-visualization-going-beyond-charts-49cb52c4e0d4

对于大多数数据科学家来说,从原始、杂乱的数据到清晰的叙述和可操作的见解的路径是通过可视化。给你的数据赋予具体的形状,使它更容易被人脑理解;它也打开了讨论和解释的空间。

这些年来,我们已经发布了许多关于基本和有效的数据可视化技术的教程,所以本周我们将深入探讨一下。我们选择了一些优秀的近期文章,涵盖了更高级和更具体的用例,如果您想扩展您的工具包并在此过程中获得新的想法,请开始阅读。

Mick Haupt 在 Unsplash 上拍摄的照片

如果你正在寻找一些关于数据科学和机器学习的其他主题的引人入胜的读物,你就在正确的地方。以下是我们最近最喜欢的一些。

感谢您本周与我们共度时光,感谢您的支持——无论是通过阅读我们作者的作品,与您的网络分享,还是(对那些刚刚迈出这一步的人而言)成为媒体成员

直到下一个变量,

TDS 编辑

数据可视化理论:导论

原文:https://towardsdatascience.com/data-visualization-theory-an-introduction-a077c0d80498

数据科学理论

数据科学需要的关键数据可视化理论

作者图片

讲述故事是任何数据科学家工作中非常重要的一部分。在本文中,我将介绍数据可视化的关键理论。

介绍

作为一名数据科学家,知道如何高效地讲述故事是最难掌握的技能之一。尽管数据可视化理论没有机器学习等其他数据科学领域那么有吸引力,但它可以说是数据科学家角色中最重要的部分。我看到各地的数据科学家通过使用本文中的一些理论,可以显著改善可视化效果。

在本文中,我想讨论数据可视化的一些关键方面。我将谈论视觉编码信息的方法,以及心理学家在这个领域的历史中发展的一些关键原则。最后,我用一些著名的可视化方法来帮助塑造这个领域。

在整篇文章中,我将给出可视化数据的好的和坏的方法的例子,我希望读完这篇文章后,你也可以避免像这篇文章上面的怪物一样进行可视化。

什么是数据可视化?

数据可视化的最终目的是讲述一个故事。我们正试图尽可能高效地传递有关数据的信息。可视化意味着以图形化的方式对数据信息进行编码。做好这一点可以让读者能够快速准确理解我们试图传递的信息。

视觉编码

有许多方法可以对信息进行编码。在数据可视化中,我们使用可视编码。我将快速浏览一些:

视网膜视觉编码

最常见的信息编码方式是通过视网膜编码。这些编码很快被我们的视网膜接收到,是向读者传递信息的一种方式。

图一。视网膜编码(作者图片)

以上是 5 种视网膜编码,包括阴影、颜色、大小、方向和形状。你可以在一个图表中使用它们,但是读者很难快速掌握所有的信息。每个图表 1 或 2 个视网膜编码是最佳的。

想想什么样的编码最适合什么样的变量。例如,颜色对于一个连续的变量(例如:年龄或体重)来说效果很差,但是对于一个离散的变量(例如:性别或国籍)来说效果很好。稍后我将更详细地讨论这一点。

其他视觉编码

除了视网膜编码,还有其他视觉编码可用于可视化数据。例如,“空间”编码利用大脑皮层的空间意识来编码信息。这种编码可以通过尺度、长度、面积和体积中的位置来实现。

最好的编码是什么?

但是编码数据的最好方法是什么呢?简单的答案是,这取决于你想要实现什么。请看下面的图表,它显示了连续变量编码的有效性:

图 2:连续变量的视觉编码效果。图片由 t . Munzner 提供

上图显示了连续变量编码(体重、身高等)的有效性。).最好的是普通规模的仓位,最差的是成交量。想一想本文开头的 3D 饼状图,单纯根据体积来知道每个类别中的数值有多难。

T.Munzner 建议用视觉编码对最重要的变量进行编码,这样效率最高,如上图所示。请记住,这是针对连续变量的,虽然颜色可能不是连续变量的最佳代表,但它肯定适用于离散变量:

图 3:离散变量的视觉编码效果。图片由 T. Munzner [1]

对于离散变量(性别、种族等。),空间区域编码是最有效的,颜色是第二好的视觉编码。

让我们看一个例子:

图 4 中国和美国人口的立方体积(图片由作者提供)

上图显示了中国和美国的人口。种群用体积编码,而类别用颜色编码。颜色在这里对离散变量非常有用,然而,体积是一个可怕的选择,因为读者很难确切知道每个国家有多少人口。

图 5:中国和美国人口的柱状图(图片由作者提供)

现在我们来看一个条形图。这里我们对连续变量使用 2 种编码,对离散变量使用 1 种编码。对于连续变量(总体),所使用的编码是普通标度中的位置和长度。对于离散变量(类成员),我们使用空间区域代替颜色,这样效果最好。

堆积条形图和饼图

有证据证明这一切都是真的吗?还是这都是主观的?

William S. Cleaveland 和 Robert McGill 进行了一系列实验,试图量化不同数据可视化编码方式的效率2

图 6:威廉·s·克里弗兰和罗伯特·麦吉尔实验中的视觉化类型【2】

在 William S. Cleaveland 和 Robert McGill 的实验中,给了 55 名受试者 20 张上述类型的图表,并要求他们回答两个问题:

  • 哪个酒吧比较大
  • 较大条形中较小条形占多大百分比

在饼图和条形图上也进行了类似的实验:

图 7:威廉·s·克里弗兰和罗伯特·麦吉尔实验的角度与长度可视化类型2

结果如下:

图 8威廉·s·克里弗兰和罗伯特·麦吉尔【2】的可视化类型的错误率

产生最小误差的可视化是比例条形图。请注意,堆积条形图比较起来要困难得多,误差也大得多。回头看看编码,非对齐刻度上的位置不如对齐刻度上的位置有效。

图 8 的第二部分显示,条形图的平均误差是饼图的一半(2⁰'⁹⁶ ≈ 2)。这再次在图 2 中示出,在图 2 中,角度在对信息进行编码时远不如普通尺度上的位置有效。

回到本文开头的 3D 饼图,您可以开始明白为什么角度并不是编码数据的最佳方式,而音量也毫无意义。条形图能达到同样的效果,但效果更好。

格式塔理论

格式塔原理(德语中的形状)是由 20 世纪的心理学家开发的,用来理解人类视觉感知的模式。我不会谈论所有的格式塔原则,但是,我想提几个。

为了展示格式塔理论,我将画一个蓝色圆圈的网格,然后我将每个原则添加到网格中。想想这些原则是如何相互作用的。

类似

格式塔相似性突出了大脑对事物进行分组的能力。相似性可能由于任何视觉编码而发生,如位置、形状、颜色、大小等。

图 9:格式塔相似性(作者图片)

看上面的图片,你会看到不同的群体。这是你的大脑将形状联系在一起。

非相似性

图 10:格式塔非相似性(作者图片)

在这里,大多数人的眼睛会立刻跳到左边的点上。非相似性是强大的,但也是危险的。把那个点放在网格外面,你几乎看不出我把其他形状留在了网格里面。

连通性

图 11:完形连接(作者图片)

连通性甚至更强大,你的眼睛会立刻瞄准那条线。事实上,由线连接的 6 个点具有不同的形状,但我们仍然将它们视为一组。连通性可以压倒相似性和非相似性。

所以在进行可视化时,格式塔原理和它们的相互作用是很重要的。

塔夫特信息与墨水比率

这是数据可视化理论中我最喜欢的部分。爱德华·塔夫特是美国统计学家,也是数据可视化的先驱。塔夫特的一些原则很有争议。Tufte 对于可视化的好处有一个非常激进的观点。

等式 1: Tufte 的数据与油墨比率

Tufte 认为,在设计图表时,应该最大化数据与油墨的比例。我们应该只用墨水来表示数据,并且尽可能少用墨水。

图 12:可视化的 Tufte 数据-油墨比(作者提供的图像)

Tufte 严格相信这个数据的比例,所有不需要可视化数据的墨水都应该完全避免。在上图中,我明显夸大了一点,但是你明白了。左边的图杂乱无章,分散了读者对我们真正想看的东西的注意力。右边的图更好,显示了没有添加噪声的数据。

著名的可视化

我想以几个著名的形象化例子来结束我的演讲。

第一个代表了 1812 年至 1813 年拿破仑对俄罗斯的革新中“大陆军”的规模。军队带着大约 60 万人出发了,其中一小部分成功了。

图 13:拿破仑向莫斯科进军。图片由查尔斯·约瑟夫·密纳德拍摄

这种可视化令人印象深刻的是,它捕捉了 6 个方面的数据:部队人数,距离,温度,地理位置,旅行方向,以及位置与时间。

第二个我想展示的是佛罗伦萨·南丁格尔的作品,她是英国的社会改革家,也是 19 世纪的统计学家。图表显示了东部军队的死亡原因。

图 14:东部士兵的死亡原因——南丁格尔玫瑰——图片由佛罗伦萨·南丁格尔 [4]拍摄

这张图显示了东部军队中的大多数死亡是可以避免的(蓝色部分)。她的目标是展示军队医院卫生的重要性。她的运动奏效了,经过 10 年的卫生改革,印度军队的死亡率下降了 3 倍多。

你对这些可视化有什么看法?你会用不同的方式可视化数据吗?

什么使可视化变得好?

我认为记住 Tufte 的数据-墨水比率是很重要的,但在一定程度上。有效地使用图表上的墨水量以不分散读者的注意力是很重要的,但是它不应该损害图表的可读性。想象一下 3D 饼图中数据与油墨的比例。想想所使用的视觉编码的种类(音量、角度、颜色)。条形图能达到同样的效果,但效率更高。

图 15:条形图与饼图(作者图片)

在某些情况下,用效率来创造印象是有意义的。nightingale rose 使用角度和面积作为视觉编码,这两者都是低效的。在她的案例中,这种方法效果很好,因为可预防的死亡数量是巨大的。她的图表强调了这一点,但没有透露准确的数字,这有助于形成公众舆论。

结论

在本文中,我首先讨论在进行可视化时使用的各种视觉编码,甚至没有意识到这一点。我谈论它们的有效性,以及哪些是你应该避免的。然后我会多谈一些理论,包括格式塔理论和塔夫特。最后,我向大家展示了一些著名的可视化技术,它们帮助塑造了数据可视化领域。

支持我👏

希望这对你有所帮助,如果你喜欢,你可以 关注我!

你也可以成为 中级会员 使用我的推荐链接,获得我所有的文章和更多:https://diegounzuetaruedas.medium.com/membership

你可能喜欢的其他文章

信息论应用于 Wordle

利用人工智能检测欺诈

参考

[1] T. Munzner,语义学者,2015。可在:https://www . semantic scholar . org/paper/Visualization-analysis-% 26-design-munz ner/5521849729 AAA 387 cfeef 0d 12 3c 91170 D7 bbfd 0

2 William S. Cleaveland 和 Robert McGill,《图形感知:图形方法的理论、实验和应用》, 2004 年。美国统计协会杂志。可用:http://Euclid . psy . yorku . ca/www/psy 6135/papers/clevelandmcgill 1984 . pdf

[3]拿破仑俄国战役地图,维基百科。可用:查尔斯·约瑟夫·密纳德——维基百科

[4]统计和卫生改革,东部军队死亡原因图表,维基百科:可用:佛罗伦萨·南丁格尔—维基百科

使用 Python 实现数据可视化

原文:https://towardsdatascience.com/data-visualization-using-python-422d7ac4cb65

面向初学者的 Matplotlib

照片由марьянблан| @ marjanblanUnsplash

在与许多有抱负的分析师/数据科学家交谈时,我意识到他们唯一的关注点/兴趣是学习预测模型、机器学习算法等。数据科学生命周期的一个重要方面是数据探索,他们在开始时没有关注这个方面。

答:设定期望

数据可视化是有效探索数据的关键工具之一。本教程旨在向读者介绍 python 中最常用的可视化库之一 Matplotlib。我们假设读者已经基本熟悉 python 语言,比如安装包、Python 数据结构、条件&循环,以及关于包的基本知识,比如 Numpy。

B. Matplotlib 图结构

在我们开始教程之前,理解 Matplotlib 制作任何图形/图表所遵循的结构是很重要的。参考下图,Matplotlib 图形/图表中最外面的容器称为图形。每个图形可以包含一个或多个轴轴,这些轴是真实的图。每个轴还会有进一步的子组件,如轴(x & y)、 标题图例轴标签大刻度&小刻度、等。

图形/图表结构(Matplotlib 官方文档)

Matplotlib 使您能够使用两种不同的接口构建图:

  • 使用" pyplot" 模块
  • 使用面向对象的接口

在本教程中,我们将主要致力于构建" pyplot" 模块接口的知识。我们将进一步限定本教程为创建线图。其他重要的图表,如直方图、散点图、箱线图、2D 直方图等。将在以后的教程中介绍。

c .让我们开始吧

让我们从第一个情节开始。

*#### Importing the necessary libraries.*from matplotlib import pyplot as pltimport numpy as np*#### Defining data to be used for plots.*x_var = np.random.rand(20)y_var = np.random.rand(20)*#### Making the first plot*plt.plot(y_var)plt.title("First Plot")plt.show()

第一个简单的情节(作者图片)

C.1 解释第一个情节

假设读者熟悉 import 语句和 NumPy random 模块的使用,让我们将重点放在用于绘图生成的代码上:

  • plt。plot(y_var) —第一条语句使用 pyplot 模块的 plot 函数来生成图形。默认的绘图功能创建了如上图所示的线图。您会注意到 y 轴上的刻度值介于 0 和 1 之间。这是因为被绘制的变量是 y_var,它包含 0 到 1 之间的随机值
  • plt.title("First Plot") —第二条语句使用带有文本参数(in ")的 title 函数来定义绘图的标题
  • 工厂。 pyplot 模块的 show()show 函数用于绘制图形

正如你们大多数人一定已经注意到的,这个情节有几个问题。

  • 没有轴标签
  • x 轴刻度显示为十进制数。假设我们正在构建一个 20 点的线图,我们将会看到代表每个点数的整数(从 1 到 20)

这些问题将在下一个情节中得到解决

*#### Improving the plot.*plt.plot(y_var)plt.title("Improved Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))plt.show()

改进的情节(图片由作者提供)

C.2 解释改进的情节

可以看到,该图现在有了轴标签。x 轴上的刻度值现在也显示了所需的值。用于改善绘图的附加代码行解释如下:

  • plt.xlabel("数据点编号")& plt.ylabel("数据点值")x-labely-label 功能都用于给 x 轴和 y 轴添加标签。这些函数将文本(in " ")值作为参数来使用它们作为标签
  • pltxticks(range(20),range(1,21))—x 刻度函数有两个参数,f 第一个参数是 x 轴的位置,刻度将放置在此处,而第二个参数用于定义刻度所用的标签。如果缺少第二个参数,则使用第一个参数生成的位置值将用作默认标签

现在我们已经解决了所有的基本问题,让我们来研究一下图表的美学

*#### Beautification*plt.plot(y_var, color = "green", marker = "o",linestyle = '--', label = "y Variable")plt.title("Final Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))plt.legend()plt.show()

线条图后期美化(图片作者提供)

C.3 解释最终剧情

视觉改进后的剧情看起来好多了。它具有标记点、虚线样式和可用的图例。用于生成上述图的附加代码块的详细信息如下:

  • plt.plot(y_var,color = "green ",marker = "o ",linestyle = '-',label = " introductive Plot ")—我们现在向同一个函数传递更多的属性,而不是使用普通的 plot 。这些属性的解释如下:
  1. 颜色 —用于定义线条的颜色(本例中为绿色)
  2. 标记 —用于定义要使用的数据标记(在这种情况下,黑色的高亮点用“o”表示)
  3. 线条样式——用于定义要生成的线条的类型(在“- -”表示的情况下为虚线)
  4. 标签 —如果有人想拥有一个传奇,定义一个标签是必须的。分配给标签属性的值显示为图例
  • plt。图例 —该代码块确保图中出现图例。如前所述,legend 函数使用 plot 函数的 label 参数来定义图例。如果缺少标签参数,图例将会出现,但只有绘图的颜色,没有任何绘图说明

上图中唯一缺少的部分是显示图上数据点的值。让我们包括这一点

#### Showing the data point values using annotateplt.plot(y_var, color = "green", marker = "o",linestyle = '--', label = "y variable")plt.title("Final Plot With Data Labels")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))for i in range(len(y_var)):plt.annotate(round(y_var[i],1), xy = [i-.5,y_var[i]+.01])plt.legend()plt.show()

注释数据标签(图片由作者提供)

C.4 用数据标签解释最终绘图

最后一个代码块和用于生成数据点的代码块之间的唯一区别是注释函数。它是在图上显示任何文本的一站式解决方案。在这种情况下,annotate 函数用于显示数据点值。注释功能的详细信息如下:

  • 第一个参数 —第一个参数( round(y_var[i],1) 在我们的例子中)捕获我们想要在图形上显示的文本。在我们的例子中,我们只是取 y_var 的相应值,并将其四舍五入为一位数
  • 第二个参数(XY) —该参数获取图表(以列表形式)的 x 和 y 坐标,其中第一个参数中的文本需要进行注释

请注意,注释函数一次只能打印 1 个值,因此在我们的例子中,它被用于一个循环中,因为我们必须打印 20 个不同点的数据点值。

D .多个地块

上面的代码块完成了线图的构造。现在让我们看看如何在同一个图形中创建多条线图

*#### Multiple trend lines in the same plot.*plt.plot(y_var, color = "red", marker = "o",linestyle = '--', label = "Plot 1")plt.plot(x_var, color = "black", marker = "o",linestyle = '-', label = "Plot 2")plt.title("Multiple Line Plots")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))plt.legend(loc = [.8,.01])plt.show()

多重趋势(图片由作者提供)

D.1 解释多线图

上图在同一个图上绘制了多条线。单线图和多线图的代码块之间的唯一区别是,为了创建多条线,我们使用了多个 plt。绘图功能。请注意,每个函数负责其样式和格式。

上述代码块中另一个值得注意的区别是 plt。图例功能。图例函数的 loc 属性获取需要放置图例的图表(以列表的形式)的 x 和 y 坐标。第一个值是图例从图左下角向右方向的距离&第二个值是图例从左下角向上方向的距离

图表结构的进一步变化是不同的趋势线有不同的图表。这可以通过以下方式完成:

#### Making 2 plots side by side using axes function#### First plotplt.axes([.05,.05,.425,.8])plt.plot(y_var, color = "red", marker = "o",linestyle = '--', label = "Plot 1")plt.title("First Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.legend()plt.xticks(range(20), range(1,21))#### Second Plotplt.axes([.525,.05,.425,.8])plt.plot(x_var, color = "black", marker = "o",linestyle = '-', label = "Plot 2")plt.title("Second Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))plt.legend()plt.show()

并排图(图片由作者提供)

D.2 解释多图表场景

如你所见,我们通过使用轴功能创建了两个并排的图。这个函数是做什么的?

每个轴函数创建一个新的轴容器供用户构建图形。该函数期望在列表的表单中提供 4 个值来定义容器的位置和大小。这 4 个值是

  • 第一个值-左下角 X 坐标
  • 第二个值-左下角 Y 坐标
  • 第三个值-图的宽度
  • 第四个值-图的高度

有几点需要注意:

  • 轴功能的使用导致新轴容器的生成,因此轴功能后使用的所有图形功能仅应用于新绘图。在我们的代码块中,请注意,在每个轴函数之后,我们都重复了所有的绘图、图例和标签语句
  • 轴功能只创建新的轴容器而不是新的图形容器。这意味着使用轴功能创建的所有图都是同一个图形的一部分
  • 用户可以用不同的坐标不断重复轴功能,并且可以在同一个图形容器中包含多个图形

您一定已经注意到第二个图的轴标签与第一个图重叠,因此像这样绘制多个图变得很乏味,因为这需要对 X & Y 坐标进行强有力的判断。这个问题可以用另一个函数来解决:子情节。让我们看一看

*#### Making 2 plots side by side using Subplot function**#### First Plot*plt.subplot(1,2,1)plt.plot(y_var, color = "red", marker = "o",linestyle = '--', label = "Plot 1")plt.title("First Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))*#### Second Plot*plt.subplot(1,2,2)plt.plot(x_var, color = "black", marker = "o",linestyle = '-', label = "Plot 2")plt.title("Second Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))*#### Avoiding the plot overlaps*plt.tight_layout()plt.show()

紧凑布局(图片由作者提供)

D.3 解释无重叠的并排图

可以看到,现在剧情没有重叠。这可以通过将子图功能和 tight_layout 功能一起使用来实现。上述代码块的详细信息如下:

  • plt。subplot(1,2,2)—sub plot 功能与 axes 功能相同,唯一的区别是根据要创建的图形数量,图形位置会自动调整。subplot 函数有 3 个参数:
  1. 第一个参数定义了我们需要的行数
  2. 第二个参数定义了我们在列中需要的绘图数量
  3. 第三个参数是您正在创建的地块编号。请注意,图的编号从左到右和从上到下移动,例如在 2 X 2 图的情况下,左上角的图将是 1 号,右下角的图将是 4 号
  • PLT . tight _ layout()tight _ layout函数确保多个图具有正确的大小,以便图之间没有重叠。请注意 tight_layout 功能只能在使用子剧情功能时使用

人们一定已经注意到,尽管我们已经克服了情节重叠的问题,但总体数字仍然很小。随着我们将继续使用支线剧情绘制更多的剧情,实际的剧情尺寸将会继续缩小。为了克服这个挑战,我们需要一些灵活性来修改 matplotlib 结构的图形容器。让我们现在就做吧

*#### Changing the figure size to change the size of overall plot**#### Changing the size of the figure*plt.figure(figsize=(14,3))*#### First Plot*plt.subplot(1,2,1)plt.plot(y_var, color = "red", marker = "o",linestyle = '--', label = "Plot 1")plt.title("First Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))*#### Second Plot*plt.subplot(1,2,2)plt.plot(x_var, color = "black", marker = "o",linestyle = '-', label = "Plot 2")plt.title("Second Plot")plt.xlabel("Data Point Number")plt.ylabel("Data Point Value")plt.xticks(range(20), range(1,21))*#### Avoiding the plot overlaps*plt.tight_layout()plt.show()

修改的图形容器(图片由作者提供)

D.4 解说剧情

如你所见,图形功能帮助我们改变了图形的大小。传递给 figure 函数的 figure size 属性接受一组值,其中第一个值是宽度,而第二个值是高度。使用这种方法,图形大小发生了变化,现在绘图看起来更好了

E 结束语

在本教程中,我们使用了 matplotlib.pyplot 模块的多个函数。下面给出了它们的一行摘要以及它们的正式 matplotlib 文档页面的链接,供您参考。

  • plt。图 —用于创建具有自定义需求(如图形大小)的图形容器。将链接到 matplotlib 文档
  • plt 子图 —用于在同一图形容器内创建多个图。链接到 matplotlib 文档
  • plt.tight_layout —与 plt 一起使用。支线剧情功能。确保多轴图自我调整。链接到 matplotlib 文档
  • plt。轴 —用于创建多个轴容器。链接到 matplotlib 文档
  • plt。图 —用于创建线图。链接到 matplotlib 文档
  • 工厂。标题 —用于给任何图添加标题。链接到 matplotlib 文档
  • 工厂。xlabel —用于向任何绘图中的 x 轴添加标签。链接到 matplotlib 文档
  • plt。ylabel —用于向任何绘图中的 y 轴添加标签。将链接到 matplotlib 文档
  • plt。xticks —用于向任何绘图的 x 轴添加刻度。将链接到 matplotlib 文档
  • plt。注释 —用于向绘图的任何部分添加文本。链接到 matplotlib 文档

使用 Matplotlib、Seaborn 和 Pandas 实现数据可视化

原文:https://towardsdatascience.com/data-visualization-with-matplotlib-seaborn-and-pandas-288f63b99751

有点相同,但也不同

Seaborn 基于 Matplotlib,Pandas 可视化是 Matplotlib 对象,但即使它们使用相同的后端,我们用它们绘制图表的方式也可能非常独特。

分组条形图—作者图片

本文将探索不同的条形图来比较 Matplotlib、Pandas 和 Seaborn 在数据可视化方面的可用性和优缺点。

我们将从定义一些虚拟数据和导入所需的库开始。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
import numpy as npdata = {'label':['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X'],
        'percent':[.1, .2, .3, .4, .5, .6, .7, .8, .9, 1],
        'remaining':[.9, .8, .7, .6, .5, .4, .3, .2, .1, 0],
       }df = pd.DataFrame(data)

我们将创建一个带有三个支线剧情的 Matplotlib 图形,然后绘制三个条形图,一个带有 Matplotlib,一个带有 Pandas,一个带有 Seaborn。

f, (ax1, ax2, ax3) = plt.subplots(3, figsize=(12,8))# matplotlib
ax1.bar(df.label, df.percent)# pandas
df.plot(x='label', y='percent', kind='bar', ax=ax2)# seaborn
sb.barplot(x=df.label, y=df.percent, ax=ax3)

plt.show()

简单条形图—图片由作者提供

尽管我们的图表看起来彼此不同,并且我们使用不同的方法来制作它们,但是它们是由相同的 Matplotlib 对象制成的;我们编辑它们的方式是一样的。

定制的

一旦我们绘制出条形图,我们可以通过与轴、图形和其他对象交互来定制它们。

为了定制我们的图表,我们可以使用 Seaborn 的函数或者导航 Matplotlib 对象并进行调整。熊猫有它的窍门,可以方便出图,但是它没有编辑 Matplotlib 对象的方法,只有创建它们的方法。

让我们改变图表中的一些东西。我们将设置一个主题,用 Seaborn 删除脊线,添加标签,并用 Matplotlib 编辑记号、标题和图例。

# seaborn theme
sb.set_theme(style="whitegrid")f, (ax1, ax2, ax3) = plt.subplots(3, figsize=(10,16))# matplotlib + pandas
ax1.bar(df.label, df.percent)
# pandas + matplotlib
df.plot(x='label', y='percent', kind='bar', ax=ax2)
# seaborn + pandas
sb.barplot(x=df.label, y=df.percent, ax=ax3)**# remove espine
sb.despine(f)****for ax in [ax1, ax2, ax3]:
    # add labels
    container = ax.containers[0]
    labels = ["{:g}%".format(val*100) for val in container.datavalues]
    ax.bar_label(container, labels=labels)
    # horizontal grid only
    ax.grid(False)
    ax.grid(axis='y')
    # set y ticks
    ticks = np.arange(0,1.1,0.1)
    tick_labels = ["{:g}%".format(val) for val in ticks]
    ax.set_yticks(ticks, tick_labels)
    # remove legend
    ax.legend([])
    ax.get_legend().remove()
    # define xy labels
    ax.set_ylabel('Y Axis')
    ax.set_xlabel('X Axis')
    # title
    ax.set_title('Hello World', fontsize=16)**plt.show()

条形图—作者提供的图片

水平条

为了在 Matplotlib 中改变我们的横条,我们只需要改变.barh的方法.bar

如果 X 是一个数字字段,Y 是一个字符串,Seaborn 将自动绘制水平条。如果 X 和 Y 都是数字,我们可以用参数:orient=’h’强制水平显示。

熊猫有点令人困惑,但一点也不难。我们需要将参数kind改为“barh”,就这样。XY 参数是反转的,所以我们必须将标签传递给 X,即使它们显示在 y 上。

f, (ax1, ax2, ax3) = plt.subplots(3, figsize=(10,12))**# matplotlib
ax1.barh(df.label, df.percent)

# pandas
df.plot(x='label', y='percent', kind='barh', ax=ax2)

# seaborn
sb.barplot(data=df, x='percent', y='label', orient='h', ax=ax3)**# remove espine
sb.despine(f)for ax in [ax1, ax2, ax3]:
    # add labels
    container = ax.containers[0]
    labels = ["{:g}%".format(val*100) for val in container.datavalues]
    ax.bar_label(container, labels=labels)
    # horizontal grid only
    ax.grid(False)
    ax.grid(axis='x')
    # set x ticks
    ticks = np.arange(0,1.1,0.1)
    tick_labels = ["{:g}%".format(val) for val in ticks]
    ax.set_xticks(ticks, tick_labels)
    # remove legend
    ax.legend([])
    ax.get_legend().remove()
    # define xy labels
    ax.set_ylabel('Y Axis')
    ax.set_xlabel('X Axis')
    # title
    ax.set_title('Hello World', fontsize=16)

plt.tight_layout()
plt.show()

分组/聚类条形图

Seaborn 为 Matplotlib 添加了许多功能,Pandas 也有其简化库的方法。

为了用 Matplotlib 绘制一个簇状条形图,我们的 X 需要是数字来调整条形图的位置。我们还需要为每组条形使用.bar函数。

这一过程在海鸟和熊猫身上变得更加简单。

f, (ax1, ax2, ax3) = plt.subplots(3, figsize=(12,8))**# matplotlib
x = np.arange(0, len(df.label))
ax1.bar(x-0.3, df.percent, width=0.3)
ax1.bar(df.label, df.remaining, width=0.3)****# pandas
df.plot(x='label', y=['percent', 'remaining'], kind='bar', ax=ax2)****# seaborn
df2 = df.melt(id_vars=['label'], value_vars=['percent', 'remaining'])
sb.barplot(data=df2, x='label', y='value', hue='variable', ax=ax3)**# remove espine
sb.despine(f)for ax in [ax1, ax2, ax3]:
    # add labels // loop containers to label all sets of bars
 **for container in ax.containers:
        values = container.datavalues*100
        labels = ["{:g}%".format(val) for val in values]
        ax.bar_label(container, labels=labels)**
    # horizontal grid only
    ax.grid(False)
    ax.grid(axis='y')
    # set y ticks
    ticks = np.arange(0,1.1,0.1)
    tick_labels = ["{:g}%".format(val*100) for val in ticks]
    ax.set_yticks(ticks, tick_labels)
    # remove legend
    ax.legend([])
    ax.get_legend().remove()
    # define xy labels
    ax.set_ylabel('Y Axis')
    ax.set_xlabel('X Axis')
    # title
    ax.set_title('Hello World', fontsize=16)

plt.show()

Pandas 允许我们为 Y 使用一个字段列表,并在传递多个值时自动绘制一个簇状条形图;我们需要每组条形的一列值。

Seaborn 允许我们定义色调,它控制我们的数据如何分组和着色;我们需要一列显示所有的值,一列显示条形的类别/组。在上面的例子中,我们必须调整数据帧的形状以适应 Seaborn。

最后的想法

Seaborn 对我们的图表风格化很有帮助;通过更多可访问的功能和不同的图表类型,它为可视化过程增加了很多价值。

熊猫擅长快速想象。不用导入任何额外的库,只用一行代码就能生成一个不错的图表。

但最终,它们都是 Matplotlib,所以不管我们使用哪个选项,如果我们理解 Matplotlib 的结构和对象,我们就能够根据我们的需要定制图表。

我们还可以在单个可视化上利用所有三个库;我们可以直接从熊猫开始绘制,使用 Seaborn 添加友好的主题,使用 Matplotlib 调整细节。

感谢阅读我的文章!
更多教程 | 推特

万圣节糖果数据集的数据可视化

原文:https://towardsdatascience.com/data-visualizations-with-a-halloween-candy-dataset-b6361d5d29f8

没有诡计,只是款待!了解如何使用万圣节糖果数据集创建酷而有用的数据可视化

作者创作的标题卡

万圣节快乐,朋友们!本着这个季节的精神,我认为利用这个时间来分享我们如何使用万圣节糖果数据集创建一些非常整洁和有用的数据可视化会很有趣。在整篇文章中,我们将利用 Python 和支持库,包括 Pandas、Matplotlib 和 Seaborn ,涵盖各种不同类型的可视化。如果你想在一个无缝的位置看到所有代码,我邀请你去看看 GitHub 上的这个 Jupyter 笔记本。这是我用来制作你将在这篇文章中看到的每一个可视化效果的同一个 Jupyter 笔记本,所以你可以确定你在那里得到了每一点代码!

我们将在这篇文章中使用的数据集是由 FiveThirtyEight 的好人们策划的,存放在 Kaggle 上。如果你想了解更多关于这个数据集的信息,或者下载它供你自己使用,这个 Kaggle 的链接将为你提供你所需要的一切。(此数据集包含在 FiveThirtyEight 的麻省理工学院许可证中,此处链接。非常感谢 FiveThirtyEight 整理了这个数据集;非常感谢!😃

在这篇文章中,我们有很多内容要讲,所以让我们简单介绍一下。在下一个简短的部分,我们将谈一谈我们将如何使用自定义颜色对数据可视化进行主题化,然后我们将进入文章的大部分,涵盖所有类型的数据可视化。我们走吧!🦇

万圣节主题的颜色

除了是一名机器学习工程师和数据科学爱好者之外,我还涉足了一点平面设计。在图形设计社区中,我们真的用一个叫做十六进制值的计算机值来琢磨一种特定的色调。是的,这就像你可能已经熟悉的十六进制系统;然而,你可能不知道这些十六进制值对图形设计师有多重要!图形设计者试图得到一个十六进制值的完整的调色板,它本质上是一些颜色的组合,在一起看起来很有美感。事实上,有许多网站纯粹致力于分享这些策划的调色板。我特别使用这个网站来得到我的万圣节十六进制调色板。(是的,我现在才明白称它为“万圣节妖术颜色”的讽刺意味。😂)这是我创建的一个小图像,显示了我将在数据可视化中使用的颜色及其相关的十六进制值。

作者创作的图形

除了像这样调整颜色,我们还可以使用 Seaborn 来设置每个数据可视化的主题,以突出更暗的背景。默认情况下,我们所有即将到来的可视化将继承这个设计方案。我们在代码中实现所有这些的方法是使用下面的代码片段:

由作者编写并以可视化形式生成的代码,带有

这篇文章是如何组织的

现在我们已经有了万圣节主题,让我们为你在文章的剩余部分所能期待的打下基础。本文的其余部分分为四个主要部分,每个部分代表一组可视化类型:

  • 分类可视化
  • 分布可视化
  • 关系可视化
  • 聚类可视化

现在,非常清楚的是,这是而不是一个包含所有单个数据可视化分组的列表。我只是想给你一些最常见的可视化的例子,因为这个帖子已经够长了!

在每个分组中,我将介绍一些特定的数据可视化,对于每个可视化,我将执行以下操作:

  • 我们将研究什么是可视化以及为什么它在高层次上是有价值的。
  • 然后我们将进入假设一个现实生活中的例子,在这个例子中我们可能使用万圣节糖果数据集的特定可视化。
  • 接下来,我们将使用 Python 和像 Pandas、Matplotlib 和 Seaborn 这样的库,用代码创建可视化。
  • 回到我们假设的场景,我们将使用可视化现在可以帮助我们更好地直觉的东西对数据进行后分析

很简单,对吧?让我们进入第一组数据可视化:分类可视化!

分类可视化

好吧,如果我完全诚实的话…当我选择这个数据集时,我显然没有密切关注数据的性质,特别是考虑到有许多具有二进制值的要素。不管了!这可以给我们一些很好的实践,在下面的可视化中显示这些分类特征。

计数图

顾名思义,计数图是对特定值的数量进行计数的可视化。Seaborn 的countplot功能相对简单,所以我想通过向您展示如何以单一、整体、可视化的方式显示多个计数图来稍微改变一下。

现实生活中的例子:我们有一大堆这样的二元特征,准确地说是九个。这些二元特征用 0 表示“否”,用 1 表示“是”。在一个简单易读的可视化界面中查看所有这些二进制特性的对比,不是很有趣吗?我们首先要做一点数据预处理,然后我们会用这 9 个计数图分析我们的发现。

下面是创建这种可视化效果的代码:

由作者编写的代码,使用 Carbon 生成可视化代码

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后分析:好吧,如果我完全诚实的话,这个可视化并不是非常有用的。😂我想有趣的是,水果糖果和巧克力糖果似乎有某种关联,这是有道理的,因为我想不出任何糖果一定是水果和巧克力的。有趣的是,pluribus 是唯一一个糖果种类比其他种类多的种类。我不得不提醒自己在这种情况下 pluribus 是什么意思。就上下文而言,pluribus 指的是一个盒子里的糖果本质上是多个还是一个整体。也就是说,一颗复数阳性的糖果就像 M & Ms,而一颗复数阴性的糖果就像士力架巧克力棒。因此,即使我们没有从这种特定的可视化中获得很多价值,我希望您可以在可视化这样的多个计数图中找到价值!

饼图

在这篇文章中,我们将涉及的所有可视化,这可能是你最熟悉的一个。这个场景中的饼状图向我们展示了每个二进制特性占整个特性的多少。从视觉角度来看,它告诉我们一个价值是否比另一个更占优势。这也是一个实际上没有对应的seaborn的可视化,所以为了生成这些饼图,我们将不得不使用准系统matplotlib

现实生活中的例子:在带有计数图的例子中,我们看了一下二进制特征,以便从纯计数的角度理解它们是如何分布的。我们将在这里再次进行同样的操作,只是这次我们将使用各自的饼图来可视化这些功能。

下面是实现这种可视化的代码:

由作者编写并以可视化形式生成的代码,带有

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后分析:和上面的计数图分析一样,这些可视化并没有给我们提供太多的价值。有趣的是,我们可以凭直觉知道像bar这样的特征或多或少有 1:3 的比例,但除此之外,我们显然不能从这些特定的可视化中获得太多的价值。尽管如此,我还是希望你能看到我们在其他情况下如何使用饼图和计数图的价值!

分布可视化

虽然该数据集的大部分由二元要素组成,但也有少数要素具有连续值。自然,对于数据科学家来说,想要了解这些类型的特征的分布是非常常见的,因此本节将介绍几种类型的分布可视化。

箱形图/小提琴图

箱线图是一种可视化图,用于在较高层次上理解一个特征的主要分位数。更具体地说,它将突出显示中位数、四分位数范围(IQR)以及外围最小值和最大值。IQR 由中间的方框表示,而方框两侧的“胡须”表示外围的最小值和最大值。 violin 图类似于 box 图,除了它试图提供更多关于数据精确分布的可视化。根据您确切想要了解的数据,您可能会选择其中一个。或者你可以做我们将要做的事情,将两者并排展示!

现实生活中的例子:基于百分比的数据有三个不同的特征:糖的百分比、价格的百分比以及每种糖果在与另一种糖果比赛时获胜的百分比。我们本质上要回答的问题是,在数据中是否有广泛的分布,或者它们在各自的特性上是否都差不多?一探究竟吧!

下面是实现这种可视化的代码:

由作者编写并以可视化形式生成的代码,带有

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后期分析:有趣的是,小提琴情节的绿色让每一个视觉效果看起来都像树叶,但除此之外,小提琴情节似乎没有提供太多价值,因为它们更难解读。另一方面,箱线图更容易解释。在糖和价格百分位数的情况下,看到胡须跨越整个范围而 IQR 位于中间就不足为奇了。有趣的是,赢百分位数的胡须在更紧的 IQR 范围内更紧。在我看来,赢百分位数的方框图是所有图表中信息最丰富的,因为它本质上告诉我们,大多数糖果在受欢迎程度方面处于中间位置。回想一下,这个特征代表了它与另一种糖果直接匹配时获得的获胜百分比,这里的 IQR 基本上是在告诉我们,一种糖果与另一种糖果势均力敌的情况更为常见。当然,胡须代表确实有异常值,但快速浏览一下,这个盒状图告诉我们一个关于数据的快速故事,我们不能通过直接分析数字来快速收集数据。

直方图/ KDE 图

我在上面提到过,小提琴图并没有给我们提供太多的价值,因为它很难解释,盒子图本身也停留在一个相当高的水平。如果我们想在更精细的层次上理解数据的分布?这就是直方图/ KDE 图更有帮助的地方。一个直方图可视化了特定仓中数据的频率,有点类似于计数图。在直方图的顶部,我们可以覆盖一个核密度估计(KDE) ,它在直方图上绘制一条曲线来表示每个相应仓的“连续概率”。(如果你好奇的话,小提琴的情节本身实际上是一条 KDE 曲线,本质上是向一侧翻转,并沿着对称轴对折。)

现实生活中的例子:在箱线图/小提琴图的例子中,我们想从高层次上看看基于百分位数的特征在分布广度方面表现如何。如果我们想更精细地了解数据分布的均匀程度,该怎么办?这就是我们要用直方图和叠加 KDE 曲线评估的!

下面是实现这种可视化的代码:

由作者编写并以可视化形式生成的代码,带有

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后期分析:现在这个其实挺有意思的!与我们之前分析的盒子和小提琴图相比,这些可视化效果讲述了一个截然不同的故事。特别是对于糖和价格百分位数特性,我们在上面注意到 IQR 似乎表明数据是集中聚类的。这些可视化结果表明情况并非如此,特别是价格百分位数特性,我们实际上看到在光谱的两端有更多的糖果。这就是为什么对相同的数据执行多种可视化很重要,因为您可以从每种可视化类型中获得不同的重要信息!

关系可视化

当开始一个新的数据领域的工作时,数据科学家首先要寻找的事情之一是各种数据特征之间的相关性。在本节中,我们将介绍一些相关可视化,以及我们如何建立一种直觉,让这些可视化更好地讲述数据的故事!

回归/散点图

回归图是一种可视化,通常比较 x 轴和 y 轴之间的两个数字特征,每个数据点由一个点表示。我们还可以用一个额外的色调特征来增强回归图,该特征直观地指示了第三个特征如何影响我们正在比较的两个初始特征。在下面的例子中,我们将特别使用 Seaborn 的lmplot功能,它本质上是将标准回归图与添加色调的能力相结合,以便对数据进行更深入的分析。

现实生活中的例子:我们的sugarpercent特性告诉我们一种特定糖果与其他糖果相比含糖量的百分比,而pricepercent特性告诉我们一种糖果与其他糖果相比价格的百分比。我们还有第三个特征,chocolate,告诉我们糖果是否是巧克力。一个lmplot可以帮助我们回答这个问题,巧克力糖是否比非巧克力的 bretherin 糖与价格有更强或更高的相关性?

下面是实现这种可视化的代码:

由作者编写的代码,使用 Carbon 生成可视化代码

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后期分析:这个可视化在一个小小的情节中为我们提供了大量的信息!对于巧克力和非巧克力糖果来说,糖的百分位数和价格的百分位数之间有轻微的正相关,但是这个数据集中的数据太少了,所以我非常谨慎地做出这样一个广泛的概括。另一方面,很明显巧克力糖含糖量更高,价格也更高。这并不令人惊讶,因为根据我的经验,巧克力糖似乎比非巧克力糖价格更高。从这个图中我们可以收集到更多的信息,但是我希望主要的收获是你可以看到你可以从一个单独的图中挤出多少信息!

联合地块

到目前为止,我们已经介绍了直方图、KDE 曲线和散点图。你可能会想,如果有一种方法可以将这些东西结合在一个单一的可视化中呢?我们将通过 Seaborn 的联合情节可视化来实现这一点。一个联合情节试图以一种超越更简单的方式来分析两个特征之间的关系。你实际上可以用 Seaborn 的jointplot做很多事情,所以在下面的可视化中,我将使用 KDE 曲线创建一个特殊类型的可视化,看起来几乎像一个地形高程图。

现实生活中的例子:在前面的例子中,我们研究了巧克力和非巧克力糖果的糖与价格的关系。现在,我想探究一下巧克力和非巧克力糖果的性价比。我们可以生成与上面的lmplot相同的可视化效果,但是这一次,我想向您展示使用基于 KDE 的jointplot的价值。让我们看看结果!

下面是实现这种可视化的代码:

由作者编写并以可视化形式生成的代码,带有

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后分析:这种可视化的有趣之处在于,它有助于真正找到数据最常聚集的地方。例如,我们可以看到,对于巧克力糖果,最大的相关性是糖果在大约 65 个价格百分点和大约 70 个利润百分点处。此外,我们可以看到每个特征的纯 KDE 曲线呈现在每个通道的外侧。例如,我们可以看到,非巧克力糖果往往价格较低,而巧克力糖果往往价格稍高。正如我在之前的分析中指出的,这并不太令人惊讶,因为我的轶事经验是,在目标走道上的那些大袋巧克力糖确实更贵!

聚类可视化

在下一节中,我们将使用一些无监督聚类来将数据聚类到算法认为合适的组中。接下来,我们将创建一些可视化工具来帮助我们直观地了解为什么聚类算法会对数据进行聚类。当我们进入下一部分时,我不确定这是否是尝试这样做的最佳数据集。是的,我们将会看到一些结果,但是数据太小,特征太具体,我不敢说这些结果是可靠的。也就是说,这篇文章的重点是如何在你自己的环境中创建这些可视化效果,所以我不太担心这些结果的可靠性。如果你想看到无监督聚类被合法使用的完整用例,我会建议你看看我的星巴克客户聚类中的帖子

折线图

当确定我们应该尝试将数据组织到多少个集群时,通常在图表上表示聚类算法的误差平方和(SSE) ,并在可视化中寻找作为我们应该保持的理想集群数量。我们将在下面演示如何计算 SSE 分数并将其可视化为折线图

现实生活中的例子:我们的例子非常简单:我们希望对数据集进行聚类,以便对这些聚类进行更深入的分析,看看我们是否可以通过这种方式获得任何见解。我们不确定我们应该瞄准多少个集群,所以这个可视化将允许我们使用肘方法来确定我们应该瞄准多少个集群。让我们测试 1 到 14 个集群,看看我们的结果如何。

下面是实现这种可视化的代码:

由作者编写并以可视化形式生成的代码,带有

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后分析:不出所料,单个聚类总是产生最高的 SSE 得分,然后在几个聚类之后急剧下降。在 5-6 个集群之后,你可以看到上证综指或多或少有收益递减,所以你现在可能明白为什么我们称之为肘方法了。理想的集群数量是在这个可视化的“肘”处,我们可以争论是 2 个还是 3 个集群。

散点图

虽然我们在分析lmplot时已经接触了散点图,但我认为在这个新的背景下再次回顾是值得的。我们将使用 Seaborn 更普通的scatterplot来创建这些可视化效果,而不是使用lmplot。我们还会将每种糖果的标签直接添加到这些最终数据图表的顶部。

现实生活中的例子:当我们通过肘方法确定我们应该创建多少个聚类时,我们注意到我们可以为我们的数据集创建 2 或 3 个聚类。没有正确或错误的答案,但我个人对回答以下问题很感兴趣:当在 2 个或 3 个聚类之间进行聚类时,K-Means 算法如何根据赢价比来区分数据

下面是实现这种可视化的代码:

作者编写的代码,用生成可视化

这是可视化本身的样子:

作者用 Seaborn / Matplotlib 创建的数据可视化

后期分析:我个人认为这些视觉效果是我们迄今为止制作的最有趣的。通过将糖果标签直接添加到可视化的顶部,我们可以更精确地分析为什么聚类是这样产生的。查看只有两个集群的第一个可视化,似乎可以在集群之间划出一条非常清晰的线:第一个集群包含非巧克力糖果,在价格和获胜百分比方面比另一个集群差。另一组似乎由更多的“优质”巧克力糖果组成。当我们观察三个集群的可视化时,事情开始变得更加有趣。最初的“非巧克力”集群基本上保持完整,但是巧克力集群被进一步分成两组。有趣的是,绿色星团中的点似乎都有相似之处。如果阅读这种视觉效果有挑战性,绿色集群中的一些糖果包括 Twix、Kit Kat、Nestle Crunch 和 Snickers Crisper。根据我的经验,所有这些糖果都有点脆脆的味道。确实非常有趣!

包扎

这就是我们这篇文章的结论!我希望你发现所有这些可视化既有趣又好玩。同样,这篇文章并不代表我们可以创造的每一种可视化,但我希望它为你提供了一个很好的想法样本,你可以在自己的环境中产生这些想法。感谢你阅读这篇文章,并希望你们都有一个伟大的,安全的万圣节!🎃

分布式应用的数据仓库

原文:https://towardsdatascience.com/data-warehouses-for-distributed-applications-7e34679f74

什么是云数据仓库,如何大规模实现它们

照片i̇smail·埃尼斯·艾汉在 Unsplash 上拍摄

如今,大多数企业都有自己的内部软件系统,用于消化、分析和管理与企业运营相关的信息。例如,在零售业,内部软件系统用于跟踪付款、客户、工资等。随着时间的推移,这种系统通常会积累大量数据。因此,有效地分析和组织这些数据对企业的发展和成功至关重要。

这就是数据仓库发挥作用的地方。

数据仓库是数据管理系统,使用户能够搜索、分析和查询企业的历史数据。它们通常为用户提供与关键业务运营相关的问题的答案,如在某一时期哪种产品的销量最高?某个部门最大的支出是什么?

这些问题的答案推动着商业决策。因此,数据仓库中的数据和分析模型越好,其答案的质量就越高。

选择正确的数据仓库

在购买高功能仓库时,需要考虑很多方面:

  • 稳定性:数据仓库应该持续运行。
  • 特定领域:由于有许多部门和部门链接到核心商业智能,数据仓库应该能够分离、剖析和聚合其数据,并根据特定部门或主题提供答案。
  • 易于使用:员工使用数据仓库不需要大量的培训。
  • 持续备份和存储:存储在数据仓库中的数据应该永不丢失,并且数据仓库应该能够存储海量的数据。在大多数情况下,我们拥有的数据越多,答案就越准确。最近成功融入数据存储的创新是云。云数据仓库的创建已经彻底改变了数据存储场景,并且现在处于数据存储解决方案的最前沿。

云数据仓库

云数据仓库不仅用于存储和备份。它们还能够接收和存储来自多个数据源的数据,这大大提高了仓库的能力。

它们还相当便宜,因为您不必像构建自己的数据仓库那样,实际购买和设置服务器和存储的物理基础架构。相反,您可以简单地在云上租用它们。云提供商通常还会提供高质量的安全措施来保护您存储数据的可用性和完整性。

来源

数据从 OLTP(在线事务处理)数据库提取到数据仓库。这通常使用定期数据转储或连续更新流来完成。然后,数据被转换成更适合分析的模式,被清理并保存到数据仓库中。这个过程被称为提取-转换-加载( ETL )。

分布式应用

照片由 CreateTravel.tvUnsplash 上拍摄

通常,每个业务部门都有自己的内部系统和自己的数据库。这是因为每个部门基于他们的需求需要不同的功能套件。然而,当涉及到关键数据分析时,需要来自每个部门的每一点数据。因此,数据仓库需要在一个中心位置收集来自不同来源的所有数据。

当然,这个过程带来了许多挑战。例如,在许多企业中,将所有数据存储在一个中央数据库中是没有意义的。因此,数据仓库必须能够动态地从各种来源获取与所提供的查询相关的数据。这就是分布式系统概念发挥作用的地方。

分布式系统是计算机科学中的一个基础领域,在当今的技术进步中,它变得越来越重要。由于大公司要处理大量的数据和计算,所以用一台机器来处理这些数据和计算是不可行的。因此,他们需要将数据或计算过程“分布”到各种不同的机器上。那么,这些机器必须同步运行,以创建一个大数据处理单元。

该系统也称为水平缩放,不同于通常使用的传统垂直缩放方法。垂直扩展是你增加更多计算能力的地方(比如 RAM、CPU 等。)到现有的机器上。相比之下,水平扩展是向现有的机器池中添加更多的机器。

分布式数据仓库应用程序最关键的部分之一是体系结构,它反映了不同的数据库在不同的场合如何相互交互。在这样的高质量架构中,复杂的查询并不总是需要所有数据库的输入。他们将能够分解和“标记化”一个复杂的查询,并从正确的数据库中获取相关数据。

好消息是,随着云数据仓库的兴起,您不一定需要从头构建这样一个系统。在大多数情况下,您将能够使用流行的云提供商提供的现成解决方案。

结论

总而言之,数据仓库是当今运营业务流程的基本组成部分。它们使产品所有者和经理能够获得重要问题的答案,然后他们用这些答案来为未来做出决策。只有你的数据仓库建立在坚实的架构上,使用分布式系统,使用安全的云平台,才能做到这一点。这将使您的数据仓库能够完成收集和分析所有业务数据的复杂任务。

开发团队必须对他们的存储需求进行非常仔细的评估,然后才能做出明智的选择。他们首先必须评估将要存储的数据类型,无论是结构化的、半结构化的、非结构化的还是它们的混合。接下来,他们必须评估自己的读写访问模式。之后,他们需要通过制定预算和寻找最合适的云提供商来考虑财务方面的事情。他们还需要考虑云提供商提供的服务器的位置,因为它会极大地影响延迟。最后,他们还需要大致了解所需的容量。

如果您想定期收到关于人工智能和机器学习的最新论文的论文评论、高质量的 ML 教程等,请添加您的电子邮件此处 &订阅!

数据:工程和科学相遇的地方

原文:https://towardsdatascience.com/data-where-engineering-and-science-meet-296f54131752

数据工程和数据科学之间的分歧仍然存在吗?

在某些地方,当然是这样:负责移动、存储和维护原始数据的工程师和处理、可视化和分析数据的专家呆在各自不同的领域。然而,在许多环境和组织中,事情变得更加模糊。一个人(或一个团队)可能承担一系列介于传统数据工程和数据科学工作之间的职责。

本周,我们从数据工程的角度挑选了一些优秀的帖子。有些处理您可能非常熟悉的工作流,而有些处理您尚未探索的流程。我们认为无论如何你都会发现它们是有用的:增加我们工具包的深度,或者更具体地了解我们同事正在做的工作,这从来都不是一个坏主意。开始了。

  • 建设高性价比管道 。我们都应该注意我们的数据和计算繁重的工作消耗的资源,的高说,“不管你是管理资源的工程师还是收账单的经理。”本文指导我们做出明智的决策,同时控制与云相关的费用。
  • 如何避免最坏的数据迁移陷阱 。在现实世界中,搬到一个新地方很少是有趣的,而且——唉!—在数字领域,这也可能是一种痛苦。 Hanzala Qureshi 为规划和执行平稳的数据迁移提供了有用的资源,明确关注于维护数据质量和“测试、测试和更多测试”
  • 一个精简的数据库工作流程?是的,请 。如果您是一名数据科学家,但还不太精通 SQL,这不应该成为您与数据工程朋友在重要数据库项目上合作的障碍。 Kenneth Leung 带我们领略 PyMySQL 的魔力,它使用 Python 访问和查询 MySQL 数据库成为可能。
  • 有效管道事在毫升,太 。机器学习从业者投入如此多的精力在他们的模型的预测能力上是有道理的;这是他们工作的核心。然而,正如 Zolzaya Luvsandorj 所强调的,确保你的模型能够顺利接收原始数据、对其进行预处理并产生输出也同样重要。

照片由斯维特拉娜 BUnsplash 上拍摄

  • 掌握编排容器化应用的艺术 。Kubernetes 是许多数据基础设施中的一个关键构建块,但是学习如何使用它似乎令人望而生畏。Percy Bolmér 在这里提供帮助,他提供了一个深入代码的完整教程,但保持了事情的可管理性和可访问性。
  • 熟悉数据存储的来龙去脉 。云存储是一项几乎每个人都在使用的技术,但很少有人关心了解它的内部工作原理。如果你想更好地理解它在数据科学工作流环境中的功能,马特·索斯纳的最新贡献非常详细地涵盖了 AWS 的本质。
  • 设计 GitHub 和 Docker 之间的健壮桥梁。Khuyen Tran 最近的教程清楚地表明,一点点数据工程知识可以在非常局部的个人层面上简化过程。它利用开源工具在不同的位置存储和执行您的代码,同时自动化该过程的主要循环块。

等等,还有呢!(不是一直都有吗?)如果你正在寻找其他主题的引人入胜的读物,我们推荐以下几个选项:

感谢您支持我们发表的作品!如果你想产生最大的影响,考虑成为中层成员。

直到下一个变量,

TDS 编辑

Wandb.ai 制作 Excel 文件的数据角力

原文:https://towardsdatascience.com/data-wrangling-of-excel-file-produced-by-wandb-ai-68157d67dbde

最好保持真实。— 作者:连姆·尼森

简而言之:使用 VBA 删除标题中包含特定单词的 Excel 列。

W andb.ai 提供了一个中央仪表盘来跟踪您的超参数、系统指标和预测,以便您可以实时比较模型并分享您的发现。这使得研究人员可以根据记录的数据绘制图表;同时,它还绘制了一段时间内记录的默认值。

Wandb 仪表盘(图片提供:【https://wandb.ai/site】T4)

动机

随着大量信息被记录到 Wandb 中,利用所有这些信息来记录有意义的图表变得更加容易。

当有人希望将这些图表的数据下载到 Excel 文件中进行进一步处理时,问题就出现了。尽管这些图表上的数据看起来很可爱,但在 Excel 中,潜在的支持数据却非常庞大。

昨晚我在做一个漂亮的科幻图表,这很棒,但后来我意识到我需要对这些数据做更多的处理。为此,我下载了 Excel 格式的图表数据,发现了大量的数据字段,大约有 256 个属性;其中一半以上是不需要的。所以我在 wandb 中搜索过滤器功能,但是在我写这篇文章的时候它还没有出现:(希望它会在我的文章之后出现)。

人们可以想到的一个修复方法是执行 Ctrl +F 并找到您感兴趣的列,然后将它们移动到一个新的工作表中,但是我借此机会学习了一个方便的宏,值得与 DataScience 社区共享。

解决

投稿:如果 Excel 中不必要的列的名称包含(' somethingThatIDontKnowYet '),如何删除这些列?

解决问题的步骤:

  1. 在 Excel 文件的标题列中找到模式,这是进一步处理所不需要的。例如,我想删除后缀为 _steps、_MAX、_MIN 的列。

导出的 wandb Excel 文件

2.一旦你确定了你想要删除的列中的‘文本’,下一步就是启动一些 VBA 命令来删除它。

对于 Windows 10 用户,是Ctrl+F11。这将为您打开一个新的宏表。将 wandb 表中的所有数据复制到宏表中。然后右击宏表的任意单元格,选择菜单项。**

**

右边的图像是一个宏表,当你按下(Ctrl+ F11)时就会出现。左图显示了右键单击任何工作表单元格后可以看到的菜单项。

3.给你的宏命名。我称之为 DeleteSpecificCell (应该是一个DeleteColumnsContaining;大脑有自己有趣的做事方式😐).一旦你点击创建,一个 VBA 编辑器将会打开,如下图右侧所示。

在您的 VBA 编辑器中,您最初会看到两行:

***Sub DeleteSpecificCell()** <<We will be writing code in here>>**End Sub***

在代码块中,编写:

***Set MR = Range("A1:EI1")
    For Each cell In MR
        If InStrB(1, cell.Value, "step", vbBinaryCompare) <> 0 Then cell.EntireColumn.Delete
    Next***

代码块解释:

*# Underneath line represents the selection of columns in the workheet which will be considered while running macro 
**Set MR = Range("A1:EI1")** 
    **For Each cell In MR**
  # Underneath line tells macro to delete entire column where header contains "step" replace this with your identified text.
        **If InStrB(1, cell.Value, "step", vbBinaryCompare) <> 0 Then cell.EntireColumn.Delete
    Next***

**

右侧的图像显示了在执行上一步时,点击菜单中的运行后出现的屏幕。左边的图片显示了 VAB 编辑器,一旦你为你的宏提供了一个名字,点击创建按钮,这个编辑器就会打开。

3.瞧啊。!菜谱做好了。填写完数据后,按 F5 键,返回宏表查看神奇之处。我能够将 256 列减少到 65 列,以便进一步处理。

这样,Wandb 客户从此可以愉快地处理他们的数据。

编码快乐!!

参考:

https://www . extend office . com/documents/excel/3086-excel-delete-columns-based-on-header . html #:~:text = Right % 20 click % 20 column % 20 header,columns % 20 被%20deleted%20at%20once

PySpark 的数据争论,适合初学者

原文:https://towardsdatascience.com/data-wrangling-with-pyspark-for-beginners-3f2197c81511

从熊猫用户的角度看 PySpark 初级读本

戈兰·艾沃斯在 Unsplash 上的照片

熊猫图书馆是数据科学家的主要宝库,由于其功能和易用性,许多人开始依赖该模块进行数据处理。

不幸的是,当谈到处理大数据时,Pandas 是不够的,随着大数据变得更加普遍,这提出了一个问题。

PySpark ,一个用于 Apache Spark 的 Python API,是处理大量数据的绝佳选择。

不幸的是,PySpark 并没有获得和熊猫一样的吸引力,尽管它有着巨大的效用。由于 PySpark 不是一个纯 Python 框架,它的学习曲线更长,这可能会阻碍其他人学习使用该工具。

在这里,我们从一个熊猫用户的角度探索 PySpark 作为一个数据争论的工具。

为什么 PySpark 适合大数据?

PySpark 在处理大型数据集方面优于 Pandas,这源于它的两个最明显的特性。

  1. PySpark 采用并行处理

与在单台机器上运行所有操作的 Pandas 不同,PySpark 利用并行处理,这需要在多台机器上并行运行操作,从而以更快的处理速度获得结果。

2。PySpark 实现懒惰评估

PySpark 还通过引入惰性评估来优化其操作。简单来说,它只会在必要的时候导出运算的结果。这种方法有助于最小化任何数据处理过程的计算和运行时间。

这与熊猫图书馆形成对比,熊猫图书馆使用热切的评价。Pandas 中的所有计算都会在操作被调用后立即执行,结果会立即存储在内存中。虽然这对于小数据集是可行的,但当可伸缩性成为一个问题时,这是一个障碍。

PySpark vs 熊猫:语法

幸运的是,PySpark 数据帧的语法与 Pandas 的语法非常相似。

我们可以通过对使用 Mockaroo 生成的模拟数据集执行一些操作来展示 PySpark 语法。

为了获得更好的视角,我们将在适当的时候对熊猫执行相同的操作。

1。创建 Spark 会话

spark 会话充当创建和操作数据帧的入口点。它方便了 PySpark 中的所有后续操作。

默认情况下,创建的会话将使用所有可用的核心。

2。加载数据集

让我们用 Pandas 和 PySpark 加载第一个模拟数据集。

熊猫:

PySpark:

在加载数据时,Pandas 会自动从它读取的数据中做出推断。例如,具有数值的要素将被赋予 int 或 float 数据类型。

另一方面,PySpark 没有这样的推论。默认情况下,PySpark 会将标题视为第一行,将所有列视为字符串变量。为了防止 PySpark 做出任何错误的假设,用户必须给headerinferSchema参数赋值。

3。查看数据集

熊猫:

代码输出预览(由作者创建)

PySpark:

代码输出预览(由作者创建)

因为 PySpark 实现了延迟执行,所以它需要一个触发器来导出任何操作的结果。在这种情况下,show函数充当触发器,让用户查看加载的数据集。

4。选择列

熊猫:

PySpark:

5。描述数据集的特征

熊猫:

代码输出(由作者创建)

PySpark:

代码输出(由作者创建)

虽然 Pandas 和 PySpark 使用相同的describe函数,但是两个包的代码输出略有不同。

首先,PySpark 没有透露列的第 25、50 和 75 百分位值。其次,PySpark 与 Pandas 不同,它包含分类特征的描述性统计数据(在本例中为gender列)。

6。重命名列

使用 Pandas 和 PySpark,我们将id列重命名为“person_id”。

熊猫:

PySpark:

7。添加列

接下来,我们添加一个显示 10 年内受试者年龄的列。

熊猫:

PySpark:

8。移除立柱

之后,我们删除新添加的列。

熊猫:

PySpark:

Pandas 和 PySpark 都用drop功能删除列。唯一的区别是 PySpark 不包含axis参数。

9。删除缺失值

熊猫:

PySpark:

10。过滤

在这个过滤操作中,我们只保留 50 岁以上的人的记录。

熊猫:

PySpark:

PySpark 非常像熊猫,允许使用括号来过滤记录。它还允许用户使用filter功能过滤数值。

11。聚合

让我们通过找出每个性别组的所有特征的平均值来执行聚合。

熊猫:

代码输出(由作者创建)

PySpark:

代码输出(由作者创建)

这两个包之间唯一的区别是 PySpark 在输出的列名中包含了聚合类型(类似于 SQL)。

12。加入

我们现在有了额外的数据,显示了我们希望与当前数据集合并的每个 id 的工作和收入。

熊猫:

代码输出(由作者创建)

PySpark:

代码输出(由作者创建)

13。转换成熊猫数据帧

最后,我们通过使用toPandas函数将合并的 PySpark 数据帧转换为 Pandas 数据帧来结束练习。

代码输出(由作者创建)

PySpark 的缺点(对于熊猫用户)

尽管 Pandas 和 PySpark 数据帧之间的语法相似,但 Pandas 用户可能仍然会发现适应新软件包的困难。

Pandas 用户在使用 Pandas 很长一段时间后,可能会对数据处理有明显的感觉。不幸的是,与熊猫数据框争论的一些原则可能不适用于 PySpark。

PySpark 的一些特性可能会妨碍熊猫用户。

  1. PySpark 不支持行索引

行索引(即每一行被分配一个索引号)在 Pandas 中可能很常见,但在 PySpark 中却没有用武之地。习惯于通过索引访问行或使用 Pandas 遍历行的用户可能会发现很难导航 PySpark。

2。PySpark 不具备熊猫的功能

Pandas 库提供了大量可以增强项目的工具(例如可视化)。不幸的是,可以使用 Pandas 数据框执行的某些任务无法使用 PySpark 数据框执行。

注意:对于这种情况,用户可以使用toPandas函数将 PySpark 数据框转换为 Pandas 数据框。

3。PySpark 错误更难调试

PySpark 错误消息可能很难解释和解决。此外,PySpark 并不像熊猫那样拥有大量的社区支持。因此,在 PySpark 中调试可能是一项艰苦的工作。

结论

照片由 Prateek KatyalUnsplash 上拍摄

尽管数据科学家可以单独使用 Pandas 模块成功完成许多项目,但如果他们缺乏处理更大数据集的适当方法,他们就不应该满足于自己的技能。毕竟,随着行业转向大数据解决方案,像 PySpark 这样的工具将不可避免地成为必需品。

幸运的是,尽管 Pandas 和 PySpark 之间存在差异,但两个包共享相似的语法,因此从一个包转换到另一个包应该是可行的。

我祝你在数据科学的努力中好运!

data2vec 和多模态学习的未来

原文:https://towardsdatascience.com/data2vec-and-the-future-of-multimodal-learning-f33f9c781f48

播客

Alexei Baevski 谈适用于文本、图像、语音、视频等的人工智能架构

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

如果 data2vec 这个名字听起来很熟悉,那可能是因为它在大约两个月前问世时在社交媒体甚至传统媒体上引起了不小的轰动。这是一个重要的条目,现在越来越多的策略专注于创建单独的机器学习架构,处理许多不同的数据类型,如文本、图像和语音。

大多数自我监督学习技术涉及让模型获取一些输入数据(例如,一幅图像或一段文本)并屏蔽掉这些输入的某些成分(例如,通过遮蔽像素或单词),以便让模型预测那些被屏蔽掉的成分。

“填空”任务很难迫使人工智能学习关于其数据的事实,这些事实可以很好地概括,但这也意味着训练模型来执行根据输入数据类型而有很大不同的任务。例如,填充涂黑的像素与填充句子中的空白非常不同。

那么,如果有一种方法可以提出一个我们可以用来在任何类型的数据上训练机器学习模型的任务呢?这就是 data2vec 的用武之地。

在这一集的播客中,我和 data2vec 的创造者之一 Meta AI 的研究员 Alexei Baevski 在一起。除了 data2vec,Alexei 还参与了相当多的文本和语音模型的开创性工作,包括脸书广泛宣传的无监督语音模型 wav2vec 。Alexei 和我一起讨论了 data2vec 的工作原理和该研究方向的下一步,以及多模态学习的未来。

以下是我在对话中最喜欢的一些观点:

  • 自回归模型通常被训练来填充部分涂黑的句子或图像。但这种策略有一个固有的限制:因为填充空白对于文本和图像来说是一个非常不同的任务,所以使用这些任务来训练一个可以同时处理文本和图像的单一架构要困难得多。为了解决这个问题,data2vec 被训练成不是在图像或句子中填空,而是在一个教师网络生成的图像和文本数据的潜在表示中填空。这创建了一个无论输入数据类型如何都可以使用的通用任务。
  • 正如 Alexei 指出的,data2vec 仍然使用专门的预处理技术,根据输入数据类型的不同而不同。因此,它不完全是一个通用的架构,它需要特定目的的输入信息。然而,阿列克谢认为这可能会改变:谷歌人工智能最近发表了他们在一个名为感知者的架构上所做的工作,该架构对所有输入数据类型使用单一的预处理技术。通过将感知者的输入不可知预处理与 data2vec 的输入不可知训练任务相结合,他看到了新一波鲁棒多模态模型的巨大潜力。
  • 越来越多的多模态模型带来的挑战之一是互操作性:当深度网络只处理图像数据时,很难理解深度网络如何处理图像数据,但如果处理视觉的同一网络也处理文本和音频数据会怎么样?我们可能需要新一代的可解释性技术来跟上多模态系统的发展。
  • 阿列克谢和他的团队没有尝试过,但阿列克谢很好奇的一个问题是:data2vec 为单词“dog”生成的潜在表征看起来与它为狗图像提出的潜在表征相似或相关吗?天真地说,这似乎会告诉我们一些关于系统学习概念的健壮性的事情。
  • 人们常说,机器学习——尤其是规模化人工智能——正在成为软件工程。阿列克谢有软件工程背景,虽然他认为这个想法有一些优点,但这并没有转化为软件工程师在人工智能研究中的明显优势。

你可以在 Twitter 上关注阿列克谢,或者我在这里

章节:

  • 0:00 介绍
  • 2 点阿列克谢的背景
  • 10:00 软件工程知识
  • 14:10 data 2 vec 在进展中的作用
  • 30:00 学生和教师之间的时间差
  • 38:30 丧失口译能力
  • 41:45 更强能力的影响
  • 49:15 总结

数据库基础:ACID 事务

原文:https://towardsdatascience.com/database-basics-acid-transactions-bf4d38bd8e26

了解数据库的 ACID 属性

Pawel Czerwinski 在 Unsplash 上的照片

缩写 ACID(原子性、一致性、隔离性、持久性)是一个来自数据库理论的术语,描述了数据库事务的规则和过程。如果遵守 ACID 的规范,系统中的数据就是可靠和一致的。

什么是 A-C-I-D 基本原则?

经典的关系数据库满足四个 ACID 属性。这些说明了对数据库最重要的要求是保持数据的真实性和意义。在许多情况下,数据存储被视为“单点事实”,因此,如果错误的信息被存储和传递,那将是致命的。这四个属性包括以下几点:

  • 原子性(A) :数据事务,例如新数据记录的输入或旧数据记录的删除,要么完全执行,要么根本不执行。对于其他用户,事务只有在完全执行后才可见。
  • Consistency (C) :当每个数据事务将数据库从一致状态转移到一致状态时,满足该属性。
  • 隔离(I) :当多个事务同时发生时,最终状态必须与事务分别发生时相同。也就是说,数据库应该通过压力测试。换句话说,它不应该由于过载而导致不正确的数据库事务。
  • 持久性(D) :数据库内的数据只能因交易而改变,不得因外部影响而改变。例如,软件更新不得无意中导致数据更改或可能被删除。

范例上的基本原则

说明 ACID 组件的最常见的例子是银行转帐,即资金从一个帐户转移到另一个帐户。当然,目标是确保所有转账都是正确的,并且所有客户的账户上都有他们有权获得的金额。

假设发生了从账户 A 到账户 B 的转账。原子性描述了事务要么完全执行,要么完全失败。对于我们的例子来说,这意味着如果账户 A 被记入借方的金额,然后出现系统故障,这笔钱将被简单地贷记回账户 A。如果没有发生这种情况,我们将销毁钱,系统将处于错误状态。

对于一致性,每次事务处理后,必须确定数据库仍然处于一致状态,例如,不包含任何冲突数据。假设我们的示例银行维护一个包含所有账户和当前余额的表。在此表中,帐号是主键,因此每个帐号在数据库中只能出现一次。如果在一次不正确的数据库交易后,一个帐号可能有两个记录,则存在不一致,交易必须被撤销。

隔离声明并行运行的几个事务不能导致不同于事务一个接一个单独发生的结果。因此,如果一家银行必须在高峰期间同时处理 100 笔转账,则必须确保受影响账户的余额与转账一笔接一笔发生时一样高。

最后,对于持久性,银行必须能够保证一致的数据库存不受外部影响。例如,这包括电源故障、系统崩溃或软件更新。

酸有什么好处?

在应用中,遵循 ACID 原则的数据库有很多优点。其中包括:

  • ACID 使得几个人可以毫无顾虑地在一个数据库上工作。
  • 数据库用户和开发人员可以假设数据库没有错误,并且不必处理故障排除。
  • 不再需要手动调试,因为不会出现错误。

NoSQL 数据库满足 ACID 属性吗?

NoSQL 解决方案通常不符合 ACID 属性,尽管也有例外,如图形数据库,它们符合所有概念。NoSQL 数据库在许多情况下分布在多个设备和服务器上。这允许同时处理和存储更大量的数据,这是这些系统的关键要求。然而,这意味着它们不满足一致性的特性。

假设我们在两台物理服务器上实现了一个 NoSQL 数据库,一台位于德国,另一台位于美国。数据库包含德国和美国客户的账户余额和交易。德国帐户存储在德国,美国帐户存储在美国服务器上。

现在可能发生的情况是,一个德国客户向一个美国账户转账。则两个数据存储都被改变,并且在该处理期间不一致。例如,可能会发生这样的情况,我们开始一个数据库查询,而德国的处理已经完成,但是美国的处理还没有完成。在这个时间窗口中,即“不一致窗口”,数据库中的数据不正确且不一致。这在关系数据库中不会发生。

这是你应该带走的东西

  • ACID(原子性、一致性、隔离性、持久性)是数据库理论中的一个术语,描述了数据库事务的规则和过程。
  • 关系数据库满足这些特性,因此在任何时候都是一致的。另一方面,NoSQL 数据库在很大程度上不符合 ACID。
  • 遵循这些原则可以确保数据库在任何时候都没有错误,并且可以毫无顾虑地进行并发访问。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,请不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

data class——Python 中最简单的面向对象编程

原文:https://towardsdatascience.com/dataclass-easiest-ever-object-oriented-programming-in-python-ffd37cd2a5bf

图片由 wal_172619 发自 Pixabay

Python 内置的装饰器降低了代码的复杂性和长度

Python 中的面向对象编程(OOP)一直是热门话题之一。这是因为 Python 之所以出名,是因为它的灵活性和开箱即用的特性可以在很大程度上减少开发工作,这对于 OOP 来说也是如此。

我写过几篇关于在面向对象的场景中使用 Python 的文章。以下是其中的一些,供大家参考。

他们都需要利用第三方库,这仍然是很好的解决方案。

在本文中,我将介绍一个 Python 内置模块——data class。它是在 Python 3.7 中引入的,这使得开发人员能够在没有任何第三方库的情况下以面向对象的方式进行编码。

1.为什么选择数据类

来自 PixabayElasticComputeFarm 的图像

我需要回答的第一个问题可能是为什么我们需要使用 Dataclass?普通的 Python 类有什么问题?让我们考虑这样的场景。

我们将使用 Python 实现一个应用程序。假设我们需要编写一个“Person”类来保存对象中某个人的一些属性。我们可以写一个如下的类。

这已经简化为只有 3 个属性。所以,我们可以如下实例化一个人。

p1 = Person('Christopher', 'Tao', 34)

我们必须实现__repr__()方法,因为我们希望出于调试目的方便地打印对象。

我们还想实现__eq__()方法,因为我们想比较对象以确定它们是否是同一个“人”。

p2 = Person('Christopher', 'Tao', 34)
p1 == p2 # compare the two persons

此外,我们需要在个人类中的一些定制功能,如greeting()方法。

与大多数其他编程语言相比,它已经足够好了。这已经很简洁了。不过我们当时做的一些事情还是比较有规律的,可以在 Python 的“禅”的指引下跳过。

现在,让我们看看 Dataclass 如何改进这一点。我们只需要导入dataclass,它内置在 Python 3.7 及以上版本中。

from dataclasses import dataclass

然后,我们可以在定义类时使用 Dataclass 作为装饰器。

[@dataclass](http://twitter.com/dataclass)
class Person:
    firstname: str
    lastname: str
    age: int def greeting(self):
        print(f'Hello, {self.firstname} {self.lastname}!')

就是这样。完成了。您可以预期,我们刚刚使用装饰器定义的数据类具有我们在前面的普通类中定义的所有特性。是的,除了greeting()方法是一个真正的定制类方法,其他的都是在后台自动生成的。我们可以使用相同的代码进行测试(我不会附加两次测试演示代码),并获得相同的结果。

所以,直接的答案是 Python Dataclass 会自动为我们实现__init__()__repr__()__eq__()方法。

2.现成的实用程序

图片来自来自 www.picjumbo.com 的免费库存照片来自 Pixabay

除了上面提到的基本好处,Dataclass 还提供了一些非常方便的实用程序。我不会一一介绍,但这里会展示一些例子。

一旦我们定义了一个数据类,我们就可以利用dataclasses包中的一些工具。因此,为了方便起见,我们需要导入它,并可能给它一个别名。

import dataclasses as dc

然后,我们可以使用fields()方法检索已定义数据类的字段。不仅是类定义,它还可以处理实例。

dc.fields(Person)
dc.fields(p1)

因为这些是“数据类”,所以将它们序列化为 JSON 对象是很常见的。这通常需要其他编程语言(如 Java)的第三方库。然而,使用 Python Dataclass,就像调用内置方法一样简单。我们可以从数据类对象中获取一个 Python 字典。

dc.asdict(p1)

如果我们只对字段的值感兴趣,我们也可以得到一个包含所有这些值的元组。这也将允许我们轻松地将其转换为列表。

dc.astuple(p1)

有时,我们可能想要定义许多类,并且一些字段或方法可能被参数化。这通常在其他编程语言中用复杂的语法来完成,比如 Java 中的反射。然而,在 Python 中,我们可以使用make_dataclass()方法来生成我们想要的数量。

下面是一个使用方法生成“Student”类的示例。

然后,我们可以像使用其他数据类一样使用这个类。

s = Student('Christopher', 'Tao', '10001')
print(s)
s.greeting()

3.定制的类注释

图片来自 Pixabay

通常,这些类型的特性只满足非常常见的用例。当我们有一些特殊的需求时,它可能会迫使我们回到使用正常的解决方案。然而,在 Python 中并非总是如此,Dataclass 也是如此。

Dataclass 允许我们注释类装饰器来定制行为。

启用比较

Dataclass 自动为我们实现了__eq__()方法,这很棒,但是其他的比较方法呢?换句话说,我们还需要__lt__()__gt__()__le__()__ge__()方法。

我们也可以很容易地自动实现它们,只需简单地给装饰添加一个标志order=True

[@dataclass](http://twitter.com/dataclass)(order=True)
class Person:
    name: str
    age: intp1 = Person('Alice', 30)
p2 = Person('Bob', 56)

该逻辑将使用第一个字段作为比较对象的标准。因此,为了方便地使用order注释来自动生成所有的比较方法,我们可以将“age”字段放在前面。然后,这些人可以按年龄进行比较,如下所示。

不可变字段

有时我们可能希望数据对象的属性不可更改。在这种情况下,我们可以通过在装饰器中添加标志frozen=True来“冻结”字段。

[@dataclass](http://twitter.com/dataclass)(frozen=True)
class Person:
    name: str
    age: intp1 = Person('Chris', 34)
print(p1)

然后,如果我们试图修改属性,将会抛出一个错误。

p1.name = 'Christopher'

4.定制字段注释

图片由 NOST 来自 Pixabay

不仅在类级别,数据类中的字段也可以被注释。因此,我们可以为他们添加一些定制的行为。

默认值和默认工厂

我们可以给一个属性一个默认值。如果在初始化过程中没有给出,该属性将被赋予默认值。

另外,默认的“值”不限于一个值,它也可以是一个函数。

[@dataclass](http://twitter.com/dataclass)
class Employee:
    firstname: str
    lastname: str
    skills: list = dc.field(default_factory=list)
    employee_no: str = dc.field(default='00000')

在上面的 Employee 类中,如果没有给出,雇员号将是“00000”。如果在初始化过程中没有给出技能列表,也将对其进行初始化。

e1 = Employee('Christopher', 'Tao')
print(e1)

如果我们以后想给这个员工添加一些技能,我们可以添加技能列表,而不必检查它是否已经初始化。

e1.skills += ['Python', 'Writing']
print(e1)

排除字段

有时,我们可能不希望所有的字段都使用__init__()方法。在一个普通的类中,我们只是不把它们添加到方法中。在一个数据类中,如果我们不想包含它们,我们需要将其标记为init=False

[@dataclass](http://twitter.com/dataclass)
class Employee:
    firstname: str
    lastname: str
    test_field: str = dc.field(init=False)

然后,我们可以创建一个对象,而不需要提供第三个字段的值,如下所示。

e1 = Employee('Christopher', 'Tao')

然而,会有一个问题。也就是说,test_field属性仍将在__repr__()方法中实现。

因此,我们需要添加另一个标志来将其排除在外。

[@dataclass](http://twitter.com/dataclass)
class Employee:
    firstname: str
    lastname: str
    test_field: str = dc.field(init=False, repr=False)e2 = Employee('Christopher', 'Tao')
print(e2)

在某些情况下,我们可能仍然希望在__init__()方法中有一个字段,但只是想在打印对象时将其排除在外。为了实现这一点,我们只需要repr标志。

[@dataclass](http://twitter.com/dataclass)
class Employee:
    firstname: str
    lastname: str
    test_field: str = dc.field(repr=False)e3 = Employee('Christopher', 'Tao', 'test value')
print(e3)

5.后初始化

图片由来自 Pixabay照片合成

作为我想介绍的最后一个特性,它允许我们在初始化完成后定制数据类的行为。

假设我们想为矩形定义一个类。所以,它需要有高度和宽度。我们也希望有一个矩形的面积,但显然,这可以从其他两个属性派生出来。此外,我们想比较矩形的面积。

为了实现这些,我们可以在数据类中实现一个__post_init__()方法,如下所示。

[@dataclass](http://twitter.com/dataclass)(order=True)
class Rectangle:
    area: float = dc.field(init=False)
    height: float
    width: float def __post_init__(self):
        self.area = self.height * self.width

创建对象后,将执行 post init 方法。我们可以测试它是否有效。

r1 = Rectangle(2,4)
print(r1)

我之所以要把area字段放在第一个位置,是为了让它成为比较的标准。所以,矩形物体可以通过它们的面积来比较。

Rectangle(1,8) > Rectangle(2,3)

摘要

图片来自皮克斯拜

在本文中,我介绍了 Python 中的 Dataclass 模块。它是从 3.7 版本开始内置的,这可以在很大程度上降低我们代码的复杂性,并大大加快我们的开发。

Dataclass 试图概括数据类的常见需求,并提供现成的,但它也提供类级和字段级的注释,允许我们定制行为。除此之外,post init 方法给了我们更多的灵活性。

https://medium.com/@qiuyujx/membership

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

除非另有说明,所有图片均出自作者之手

数据中心实践第一部分

原文:https://towardsdatascience.com/datahub-hands-on-part-i-f0709e7efec9

亚历山大·辛恩在 Unsplash 上的照片

简介

为了准备新的讲座,我正在搜索最新的开源数据编目工具,以便让我的学生能够实践数据治理(DG)理论。其中,我偶然发现了 LinkedIn 的“数据中心”。除了丰富的功能集,如数据发现、数据血统和数据质量,它还使用 docker 进行了非常简单的设置。因为我想记录我的测试用例、配置和发现,所以我决定也分享它们。我计划发布 3 个故事部分,包括:

  1. 数据治理的动机、数据中心的背景、数据中心设置和基本术语表设计
  2. 数据接收、链接数据实体和数据发现
  3. 用户和角色、数据验证、数据沿袭和摘要

那么,让我们从第 1 部分开始吧!

数据治理

我在行业中看到的鸡和蛋的问题是,DG 处理的问题无法用 IT 工具轻松解决,但如果没有强大的 IT 工具,DG 也根本不会有效。那么从哪里开始呢?公司需要理解(或更好地:感受)整个组织中坏数据的痛苦(例如,坏数据会减慢业务流程),否则就没有动力和支持 DG。然后,您需要将这种痛苦转换成量化的度量,更具体地说是数据质量(DQ)度量。一旦你了解了你的敌人,你就可以设定一个目标并设计一个计划来实现这个目标。例如,表中特定列的主数据的完整性很容易衡量,可以作为一个起点。假设您现在有 60%的完整性,并希望在一年内达到 90%,那么您需要确定并修复丢失数据的根本原因(例如,GUI 强制)。当然,您需要跟踪进度,例如,检查完整性是否通过这次修复得到显著提高,一年后是否真的达到了 90%——因为如果没有,必须有人举手启动“b 计划”。

现实的冲击…

听起来很简单…但是…主要的问题是,你需要一个能够首先看到并量化坏数据的人,一个负责推动这种改进的人,你需要专家来填补缺失的数据并维护(或者一个有好算法的人),你需要一个在技术上能够修复 it 系统中的某些东西的人,你需要一个来自管理团队的支持业务流程中必要变化的人,以及一个能够实施 DQ 并定期关注 IT 的人。当然,所有这些人都需要时间,需要优先处理其他任务。这就是 DG 的难点所在,即识别、激励和组织合适的人以结构化的方式改善数据环境,因为在现实中,这不仅仅是一个孤立的小问题,而是一系列相互影响的问题,具体取决于公司规模和业务复杂性。

不幸的是,DG 工具并不是一根魔棒,安装后就能让所有的数据问题和组织缺口消失。但至少它有助于测量和可视化 DQ,加速根本原因(和影响)分析,并建立业务条件和 it 实体之间的联系。这些功能会影响仍然需要人力的组织中 DG 活动的吞吐率——但与没有任何工具支持的 DG 相比,可能只是很小的一部分。

这也是我在本文中想要探讨的:DataHub 如何符合这些期望?

数据中心

数据中心的整体架构如下图所示:

来源:Datahubproject.io

我们可以看到左边有许多不同数据库技术的连接器,涵盖了 SQL 和 NoSQL。有了这些连接器,就可以很容易地设置元数据接收过程,因为所需的配置由向导指导。如果所需的连接器不可用,仍然可以手动配置记录。元数据被转发到所谓的服务层,它由内部数据存储(例如 MySQL)、搜索引擎(Elastic)、图形索引(neo4j)和可以通过 REST 和 GraphQL 查询的 View More 组成。

最终用户与数据中心前端一起工作,例如搜索实体、浏览目录和维护术语表术语。API 和流集成是我不会在测试用例中涉及的其他特性。

数据中心设置

一般来说,您可以选择托管数据中心(SaaS)或自托管数据中心。我选择了第二个选项,简而言之就是在 Docker 环境中部署一个 DataHub 实例。我不会深入讨论每个步骤的所有细节,因为它们在 Datahub 产品页面上都有很好的记录。基本要求是 Docker 桌面、命令行 JSON 处理的 jq、Python 3.7(最低)和足够的硬件(2 个 CPU、8 GB RAM、2 GB 交换空间和 10 GB 磁盘空间)。一旦一切准备就绪(并且所有路径都设置正确:),就可以使用 CMD 命令轻松安装和部署 DataHub 了。

来源:作者

然后可以通过浏览器访问用户前端。名为“DataHub”的默认用户被配置为用于初始访问。当它工作时,你会看到一个类似谷歌的启动屏幕,有一个中央搜索框——然而,没有什么可搜索的,因为数据目录是空的。由于我已经完成了一些数据接收和术语表维护,我的 DataHub 实例已经显示了关于可用域、数据集和平台的附加信息。

来源:作者

词汇表设计

元数据概念

在右上角,我们看到一个带有几个选项的菜单栏。为了将业务知识添加到数据目录中,我们对“治理”选项感兴趣。这里我们看到以下子选项:

来源:作者

适当的目录设计需要正确理解可用的元数据概念:

  • :我们希望组织相关数据资产(例如,属于销售组织的所有数据资产)的更广泛的业务领域
  • 术语组:类似于操作系统的文件夹,可能包含术语甚至其他术语组。
  • 术语:被赋予特定业务定义的单词或短语。
  • 标签:非正式的、松散控制的标签,有助于搜索&发现。

市场上还有其他 DG 框架和数据目录,它们具有稍微不同或附加的元数据概念,例如数据对象、业务流程和策略。根据公司对 DG 工具的灵活性和范围的期望,这可以被认为是 DataHub 的一个弱点。另一方面,各种信息需要从某个地方收集并维护,这意味着相当大的工作量——因此 DataHub 提供的元数据概念集是一个很好的起点,并且仍然允许构建全面的数据模型。

元数据维护

现在的问题是如何恰当地使用这些概念。应该有由中央 DG 办公室制定的指导方针,以便每个业务领域(即领域)以相同的方式使用这些概念。例如,你可以说“术语组”应该像业务流程一样对待。让我们来练习一下这个例子:我们希望有一个业务流程(术语组)“设备采购”,它包含与设备采购相关的所有术语(例如,设备名称、设备制造商、设备本身)。这些术语属于“制造”领域。这可以在 DataHub 中完成,如下所示:

来源:作者

然后我们创建我们的术语组,它的一个术语,我们将这个术语关联到相应的领域。

来源:作者

一旦我们的术语被创建,我们可以设置一个正式的定义,并添加更多的信息作为链接:

来源:作者

语义建模

这很酷——但是我们可以在词汇表中做更多的语义数据建模!假设我们想要指定一些术语,这些术语是设备的详细信息,比如名称或制造商。然后,我们也可以在 DataHub 中使用“contains”关系构建这样的层次结构,这类似于对象/属性关系:

来源:作者

此外,我们可以在术语表中使用继承。例如,我们想要区分像“测量设备”和“后勤设备”这样的设备类型,它们与“设备”没有区别,只是更具体的“设备”类型。在这种情况下,我们可以通过“继承”功能连接这些术语:

来源:作者

结论第一部分

尽管语义建模提供了一些很酷的特性,但我的印象是,在数据中心中,它背后并不总是有技术逻辑。例如,继承术语实际上并不从“父术语”(例如,描述、所有权或链接)继承元数据,而相关的“子术语”(通过“包含”)并不真正了解这种关系,因为它没有出现在它们的数据集中。结构信息只是单向的。我真正缺少的是“包含”术语的继承(就像面向对象编程中的属性),因为作为通用术语一部分的术语也应该是相关“子”术语的一部分,否则您必须冗余地维护这些信息,或者您最终会得到不一致的元数据。另一个设计选项是避免继承术语中的“包含”关系,因为“父术语”可以被链接和点击,这样就可以很容易地在“父”数据集中找到相关的“包含”关系。我认为,还缺少一个双向关系视图,可能也是一个可视化的形式,这样数据管理员就可以一眼看到全局。同样,术语表的好处是可以共享条目,例如通过邮件或直接链接,这支持数据利益相关者之间的交流过程,或者可以集成到其他 IT 工具中。

在下一部分中,我们将深入研究数据摄取,并讨论如何将术语表(业务领域)与数据集(it 领域)联系起来。

数据运营和您:任何数据驱动型企业的 4 大优势

原文:https://towardsdatascience.com/dataops-and-you-4-substantial-benefits-for-any-data-driven-business-124c12d24564

照片由法比奥Unsplash 上拍摄

根据 Statista 的数据,每分钟都有 6900 万条 Messenger 和 WhatsApp 消息、近 70 万个 Instagram 故事和 500 小时的 YouTube 视频在互联网上流传、分享和上传。随着我们生成和消费越来越多的数据,所有行业的领导者都在寻找数据科学领域业务挑战的答案。

尽管前景看好,但数据科学并不总是保证是完美的解决方案。例如, 85%的 AI 项目失败,大部分是由于错误的初步评估,业务和数据需求之间的不协调,以及过度关注数据建模而不是问题本身。

这并不是说你应该抛弃数据科学。相反,它为提高业务绩效和加强决策提供了巨大的潜力。然而,由于从大量信息中提取有价值的见解的过程极易出现管理不善的情况,所以关键是要设定正确的优先级,并在通往成功的道路上穿越等待我们的陷阱。

如何实现这一点?一种有效的方法是应用数据操作,并了解数据管理服务的这一部分如何消除障碍。

数据运营—基本事实

DataOps(数据操作)是一种专注于简化和自动化数据分析的方法。它旨在通过将敏捷原则应用于数据管理来应对不断增长的业务信息量。该理念涉及多个领域:数据科学、DevOps 和数据工程,其主要目标是提高数据分析的质量,同时优化和加快整个数据生命周期中的工作流。

没有合格专业人员的努力,所有这些都不可能实现。由于该领域的多学科特性,数据操作专家必须具备全部的技术和软技能。所涉及的角色分为三类:

数据提供者 —这个角色通常由数据管理员承担,他们处理和管理整个组织的信息和数据访问。

数据准备者 —一组根据消费者需求调整所提供数据的角色。数据工程师(负责创建高效的数据管道)、策展人(根据业务环境调整数据)和管家(负责数据治理政策和合规性)都属于这个群体。

数据消费者 —他们与数据科学家使用的精炼数据资产一起工作,以发明克服业务挑战的新方法。

如果这一切听起来有点复杂,那是因为数据本身正变得越来越复杂。但 DataOps 的最终目标是全面促进数据操作,帮助您的组织以最有效的方式充分利用数据。

以下是采用 DataOps 方法来最大化整个企业的数据价值的四个主要优势。

#1.提高数据质量

数据质量管理是 DataOps 的首要目标,因为它决定了所有后续流程的结果。不幸的是,低质量的数据甚至会将管理最好的项目置于风险之中。

人工智能、人工智能、预测模型——它们都依赖于高质量的数据。当信息被破坏、复制或不完整时,这些技术就变得不可靠,而且基本上毫无用处。如果幸运的话,由低质量数据导致的错误会被及时发现并被修复。然而,在大多数情况下,低劣的数据质量会导致严重的后果,包括违反法规、错失商机以及客户投诉和流失。

DataOps 有助于避免这些问题,同时提高组织中所有基于数据的分析的准确性。数据供应商和编制者主要保证数据资产的令人满意的质量。然而,他们并不是唯一的数据所有者,在 DataOps 框架中,所有利益相关者都对数据质量负责,确保整个管道的高标准。

#2.更好的数据分发

在某些行业,确保单个系统和接触点之间的数据流就足够了。然而,大多数垂直行业依赖于在复杂的软件、应用和程序生态系统中收集和交换的信息。以制造业为例,考虑 MES、SCADA、ERP、维护软件、QA 系统和 IIoT 基础设施。零售业同样依赖于 BI、EDWs、库存管理软件、预测性客户分析和其他类型的软件。

传统上,随着公司的数据环境变得越来越复杂,它也变得更加分散。因此,每个新的应用程序都会使维护和重组变得更加困难,导致集成停机或失败。DataOps 通过将集成从单个系统的代码重新分配到一个中央中枢来解决这一问题。从那里,它们可以很容易地维护、自动化和部署,而没有崩溃或缺少数据访问的风险。

#3.更快、更高效的流程

虽然在法律上有义务遵守严格的数据隐私政策,但受监管行业的组织也是最积极的 DataOps 采纳者之一。原因很简单:受严格的数据保护法和实践约束的实体更有可能已经有专门的数据工程团队来实现合规性。此外,他们更倾向于在数据上投入资源。

让我们以银行为例。应用 DataOps 方法有助于他们简化信誉评估,并立即为员工提供所有必要的详细信息,以便做出有关客户账户的决策。此外,报告流程受益于自动化数据管道和更高的数据透明度,而快速迭代和部署允许快速向银行应用程序和平台添加新功能。所有这些好处都可以延伸到其他行业,无论是否受到监管。

#4.更紧密的合作

DataOps 方法的核心是追求组织中所有成员之间尽可能广泛的互动。目标是打破孤立的数据,使其可广泛访问,使团队能够合作,尽管存在障碍或分布式工作、部门划分以及相互竞争的目标和 KPI。

除了支持协作,数据治理的标准化方法也有助于孕育创新。例如,对自由流动数据的访问使 Spotify 的三名工程师团队能够在短短几周内开发出 Discover Weekly 功能,这在更严格的数据治理政策下是不可能的。

结果

无论在哪个行业,数据都将成为塑造企业未来的主导因素。因此,DataOps 方法对于当今数据驱动型企业的成功至关重要。通过向各方提供互联数据生态系统的鸟瞰图并强制正确使用信息,它可以帮助组织获得信息分析的全部好处。这些包括任务的标准化、提高项目速度、确保合规性、加强安全性等等。

使用 RTrees 和地理数据的数据集识别

原文:https://towardsdatascience.com/dataset-identification-using-rtrees-and-geographical-data-682f8dab78fa

找出哪些数据集包含预定义区域内的面

史蒂文·卡梅纳在 Unsplash拍摄的照片

背景

地理区域数据集通常在每个文件中包含大量多边形。此外,不同的分类意味着有几种类型的区域文件可供选择。

如果我们想要定位特定选择的所有相关数据集,加载每个单独的 GeoJSON 或是低效的。shp 文件并查询它们。相反,我们利用分层树结构(很像使用 k 维树来定位最近的邻居)来减少所需的搜索时间。

什么是树

Rtrees 不是将超平面分割成大小相等的盒子(quad/oct 树)或使用二进制分割方法(k-d 树),而是将数据分割成矩形,使得每组中有相等数量的叶子。

R 树的主要好处是它们是面向地理的。它们的多维搜索关键字结构使它们针对空间数据进行了优化(包含“分组”矩形不需要覆盖整个空间域)。对每个多边形使用边界框是 RTrees 更适合索引质心(点)数据的原因。正是由于这个原因,许多 RTree 实现可以在空间数据库或地理空间包中找到(例如来自 geopandas 的.sindex)。

同一个数据集上的八叉树(左)、三叉树(右)。来源:代码改编自 法蒂陈

注意: 由于多边形从来都不是正方的,所以边界上可能会有一些矩形重叠。尽管这对于多边形定位非常有用,但如果您需要将每个项目一一映射到一个组,这可能会导致问题。在使用 k-d 或八叉树的这种情况下,方法可能是优选的。

数据集

我们使用的数据集是从国家统计局下载的,涵盖了许多区域大小(输出区域→国家),跨越数年。它们位于这个存储库中,以 gzipped geoJSON 文件的形式存在。

读取数据和提取内存中的数据可以按如下方式完成:

import geopandas as gpd
import gzipgdf = gpd.read_file(gzip.open(**<filename.gz>**,'rb'))

获取边界框并构造项目对象

一旦我们加载了我们的文件,我们需要计算每个几何图形的边界框和我们希望传递的任何附加信息。在本例中,我感兴趣的是每个 geoJSON 文件的文件名和年份,使用 regex 搜索很容易提取出来:

def splice(area,name):        bbox = dict(zip(['minX','minY','maxX','maxY'],area.geometry.bounds))        origin = re.search(r'\D+',name).group()    
    year = re.search(r'\d+',name).group()     

   return **dict(id = area.areacd, origin=origin, year=year, **bbox)**

然后这个函数被应用到每个文件的每个项目,并连接成一个列表——准备转换成一个 RTree!

r bush(RTree NPM 包)

虽然我们使用 python 进行处理,但是这个工具的最终版本将在 JavaScript 中执行(并且在 web 上)。出于这个原因,我们选择使用 RTree 库的 JS 实现,而不是 geopandas 中使用的 python‘RTree’库。

在 Python 中使用 RBush

为了在 python 中运行 javascript,我们利用了js2py库。这里,我们首先设置一个具有 require 功能的 JS 评估引擎,然后加载节点 RBush 库。最后,我们创建一个新的 RBush 元素,并将其读入我们的 python 程序。

import js2py context = js2py.EvalJs(enable_require=True)context.eval("const RBush = require('rbush')")tree = context.eval(f"new RBush({size})")

向 RBush 批量添加数据

就计算和处理而言,单个大型操作通常比需要在运行时改变数组大小的单个操作更有效。出于这个原因,我们使用以下命令将之前生成的变量列表读入 RBush 对象:

tree.load(list_of_items)

对于大型数据集,这可能需要几分钟时间。

保存到磁盘

最后,我们希望将生成的树保存到磁盘上,供以后使用,并加载到我们的 web 应用程序中。我们通过将js2py JSON 树结构转换成 python 字典,然后使用 python json库将其写入文件来实现这一点。

import json 
**json.dump**(tree.toJSON()**.to_dict()**,open('docs/tree.json','w'))

构建 Web 应用程序并执行

现在我们已经生成了我们的树,最后一步是在我们的定制 web 应用程序上执行它。我们需要做的第一件事是加载新保存的 JSON 文件:

const data = await fetch(            
     "https://wolfiex.github.io/ONSAreaFinder/tree.json"          
   ).then((d) => d.json()) const tree = new rbush(10).fromJSON(data)

接下来,自定义边界框选择工具与下面的树搜索功能相结合:

tree.search({
    minX: bbox.mnx,
    minY: bbox.mny,
    maxX: bbox.mxx,
    maxY: bbox.mxy
});

这将产生一个描述所有区域类型及其源文件的界面,如下图所示。现场互动版可以在这里 找到

该工具运行时的屏幕截图。来源:Self(此处可用https://wolfiex.github.io/ONSAreaFinder/)

结论

RTrees 已被大量用于快速多边形索引,并在地理数据集查询程序的几个数据库中实现。我们填充了 RBrush npm 库,并从 javascript 和 python 中使用它来构建一个可查询的地图,该地图标识哪些区域类型属于特定的选择。

应用:

https://wolfiex.github.io/ONSAreaFinder/

代码:

该项目的代码可在 (__main__.py)找到:

**https://github.com/wolfiex/ONSAreaFinder

“docs”目录下的 web 应用程序(index.js).

Dan Ellis. (2022). 
wolfiex/ONSAreaFinder: v1.0 (v1.0). Zenodo. https://doi.org/10.5281/zenodo.6408276

机器学习数据集的 50 个公共来源

原文:https://towardsdatascience.com/datasets-for-machine-learning-and-data-science-a27a5d0ba03

马库斯·温克勒在 Unsplash 上的照片

数据科学,机器学习

寻找免费数据集来启动您的机器学习和数据科学项目的最佳地点

精选的公共数据集被广泛用于学习数据科学和机器学习。但是它们在现实世界商业项目中的效用经常被忽视。

您一定听说过,数据科学家花费 80%的时间来收集、清理和准备数据。评估一个想法的可行性需要大量的数据。那么,你愿意投入时间和精力收集数据,却发现你的想法行不通吗?

在与你需要收集的数据类似的数据集上尝试你的想法要便宜得多,也快得多。首先,在类似的数据集上训练一个(公开可用的)模型,看看结果是否合你的意。

公共数据集可以帮助您快速构建原型并明确您需要收集的数据,从而节省您的时间和金钱。

本文给出了机器学习数据集的 50 个公共来源的列表,您可以在其中搜索和下载适合您需求的数据集。

来自学术机构的数据集

机器学习在大学学术研究中的历史要长得多。因此,一些最通用的开放数据集由大学管理也就不足为奇了。

出于两个原因,这些是我寻找相似数据集的首选。首先,对于各种机器学习问题,这些都有非常多样化的数据集。第二,这些通常有不禁止用于商业目的的许可(稍后将详细介绍)。

01. UCI 机器学习库:一组非常多样化的问题和任务的数据集。

02. GroupLens Datasets (由明尼苏达大学提供):用于各种项目类型(电影、书籍、笑话等)的推荐系统的数据集。)

03.哈佛数据世界:超过 10 万个数据集用于研究项目。

04.互联网存档:来自网站的数据集存档。

05. Dataverse 开源研究数据仓库:会议和期刊研究论文中使用的数据集。

06.公共抓取数据:web 抓取数据的开放仓库。

07. DBpedia :从维基百科中提取的开放知识图。

08.英国数据服务:英国最大的经济、社会和人口数据集合,用于研究和教学。

09.OpenML 数据集:大约 4000 个由 ML 社区共享的数据集

10.学术种子数据集:一个由社区维护的分布式数据集存储库。

主要云提供商的数据集

亚马逊、微软、谷歌都有丰富的数据集目录。他们的一些数据集在云数据仓库中可用,如果你在云上进行机器学习,这很方便。

如果你正在 AWS、微软 Azure 或谷歌云上进行机器学习,你应该在这里寻找类似的数据集。这些被设置为易于在云上使用,并拥有许可的许可证。

然而,这些并不像上一节中的那些那样多样化。

11. AWS 开放数据注册中心:作为 AWS 资源的数据集。

12.微软研究院开放数据:来自微软研究院的免费数据集。

13.微软 Azure 开放数据集 ( 目录):Azure 上可用的精选数据集。

14.谷歌公共数据目录:各种来源的公共数据集的库。

15.谷歌数据集搜索:数据集搜索引擎

16.谷歌云数据集:谷歌云上可用的数据集。

17. Google BigQuery 公共数据集:存储在 BigQuery 数据仓库中的数据集。

政府数据集

数据科学成为一个东西之前,它被称为统计学。半个多世纪以来,政府一直在收集各种数据。如果您正在构建社会学、经济发展、教育和医疗保健领域的模型,政府很可能会有一个与您解决问题所需足够相似的数据集。

这些是非常丰富的数据集,您可能需要一些时间来定位正确的数据集(但这些时间只是您收集自己的数据集所需时间的一小部分)。许可证也很宽松,以鼓励使用。

18.印度政府的数据:来自印度联邦政府和邦政府的各种数据集。

19.欧盟数据:欧盟官方数据来源。

20.英国政府数据:由英国中央政府、地方当局和公共机构发布的数据。

21.美国政府的数据:来自美国政府的多样化数据集。

22.美国政府人口普查数据:美国人口普查信息,如教育、就业、健康和住房,细分到邮政编码级别。

23.美国劳工统计局:失业、薪酬&福利、支出和其他就业数据。

24.美国国会预算办公室:预算、经济前景和预测。

25.美国疾病控制和预防中心:酗酒和各种疾病的数据。

26.美国医疗保险数据:医疗保险&医疗补助数据

世界机构的社会经济数据集

就像政府一样,联合国、世卫组织和世界银行等世界组织拥有丰富的社会经济数据集。

如果您正在处理跨国家的模型,标准化和拼接来自多个政府的数据集可能需要相当大的努力。由于目标是在开始昂贵的数据收集、清理和标记之前快速评估 ML 模型的可行性,所以来自世界身体的数据集是更好的选择。

27.联合国数据 ( 目录):各国人口(及移民)、劳动力市场、农业、生产、物价指数、贸易、犯罪、健康、环境、旅游、发展数据。

28.联合国儿童基金会数据:来自联合国国际儿童紧急基金会的分娩、健康、卫生、营养、死亡率、教育和发展数据。

29.世界卫生组织数据:世卫组织的健康、疾病、流行病、免疫、污染和环境卫生数据。

30.世界银行数据 ( 目录):经济、增长、农业、教育、能源、矿业、债务、基础设施、贫困、贸易、农村和城市发展数据。

31.国际货币基金组织(IMF)数据 : GDP、贸易、物价指数、汇率、货币金融数据。

32.亚洲开发银行:类似于世界银行数据,但针对亚洲国家。

33.经济合作与发展组织(经合组织):类似于世界银行数据,但主要针对经合组织成员国。

证券交易所和中央银行的金融数据

几乎所有的股票交易所都提供历史交易数据,每个国家的中央银行都发布各种金融数据。这是您尝试股票价格的任何时间序列模型、分析一段时间内的股票市场趋势以及股票与其他资产类别或行业指标的关系的最佳去处。

34.印度国家证券交易所(NSE)历史数据:在 NSE 交易的 Nifty 指数和股票的交易数据。

35.印度储备银行(RBI)数据:来自印度中央银行的金融数据。

36.[纳斯达克历史数据](https://www.nasdaq.com/market-activity/quotes/historical  https://data.nasdaq.com/):纳斯达克指数和股票交易数据。

37.纽约证券交易所历史数据:在纽约证券交易所交易的股票数据。

38.美国美联储数据:来自美国中央银行的金融数据。

39.雅虎金融 ( 操作步骤):世界各种股票变化的股票数据。

流行的计算机视觉数据集

有许多图像数据集。随着时间的推移,这些已经变得非常大,并且注释丰富。这些数据集足以作为大多数图像相关任务的 ML 解决方案的起点。

40. ImageNet 数据集:著名的图像数据集,根据 WordNet 层次结构组织。

41.上下文中的通用对象(COCO)数据集 : 30 万张图像(带有> 20 万个标签),包含 80 个对象类别的 150 万个对象实例。

42. Google 开放图像数据集:COCO 这样的大规模图像数据集。

43. VisualData :社区策划的计算机视觉数据集。

杂项数据集源

您可能遇到了不属于上述大类的问题。这并不意味着没有数据集可以让你快速评估可行性。

谷歌之前列出的数据集搜索引擎是一个很好的起点。您可以搜索与您的任务相关的企业,例如,yelp 搜索商业评论,Airbnb 搜索租赁物业,YouTube 搜索视频。

这里有一些体育、新闻和其他商业的数据集。

44.来自美国广播公司新闻的体育和选举数据集。

45.BuzzFeed 新闻数据:由 BuzzFeed 新闻策划的新闻、犯罪、民意调查数据。

46. Yelp 开放数据集:来自 Yelp 的商业评论数据集。

47. Airbnb 数据:各城市房源房源及点评。

48. YouTube 视频数据集:带有人工验证的片段注释的 YouTube 数据。

49. Kaggle 数据集:各种 Kaggle 比赛的数据集。

50.维基百科数据库:维基百科上所有可用内容的数据转储。

一定要检查执照

请检查数据集的许可,尤其是在商业项目中使用它之前。一个公开的数据集并不意味着你可以随意使用它。以下是最常见的数据集许可证。

开放数据共享空间(ODC)

社区数据许可协议(CDLA)

  • CDLA 共享:允许使用、共享和增强数据集,但您必须在相同的许可下提供信用和共享您的数据增强。它不对从数据的计算使用中获得的结果强加任何义务或限制。
  • CDLA 许可:与 CDLA 共享相同,除了它让你选择在不同的许可下分发你的作品,只要你也为原始数据集包括这个许可。

知识共享

PDDL 和 CC0 是最宽松的许可证。这些是造物主对所有权利的放弃。其次是 ODC-By 和 CC-BY,因为它们只需要确认数据集使用的属性。然后是 CDLA 许可,然后是 CDLA 共享,因为它们不会对你将创建的 ML 模型(从数据的计算使用中获得的结果)施加任何限制或义务。

对于其他许可,这取决于您是否打算将数据集用于商业目的。类似共享的许可证通常被认为是“病毒式的”,非衍生许可证是最具限制性的。

摘要

公共数据集在学习数据科学和机器学习以及在商业环境中快速构建想法原型时非常有用。只有当一个想法是有希望的,人们才需要开始昂贵的数据收集和标记,

如果您喜欢,请:

最初发表于ML4Devs.com

Power BI 中的 DAX 变量、虚拟关系和迭代器

原文:https://towardsdatascience.com/dax-variables-virtual-relationships-and-iterators-in-power-bi-fbbc2fb5b103

“DAX 简单,但不容易!”让我们学习一些 DAX 的细微差别,可以让你成为一个真正的权力 BI 英雄!

作者图片

“DAX 简单,但不容易!”—当被问及哪种语言最能描述数据分析表达式语言时,阿尔贝托·法拉利(Alberto Ferrari)说了一句名言。这可能是 DAX 指数最精确的定义。乍一看,这可能非常容易,但是理解细微差别和 DAX 的实际工作方式,需要大量的时间和“试错”案例。

显然,这篇文章不是对 DAX 内部的深入探讨,也不会深入这些细微差别,但它将(有希望)帮助您更好地理解几个非常重要的概念,这将使您的 DAX 之旅更加愉快,并帮助您准备 DP-500 (Azure 企业数据分析师)考试。

DAX 中的变量

作为 DAX 新手,很容易陷入认为不需要变量的陷阱。简单地说,当您的 DAX 公式由一两行代码组成时,您为什么要关心变量呢?!

然而,随着时间的推移,你开始写更复杂的计算,你会开始欣赏变量的概念。当我说更复杂的计算时,我指的是使用嵌套函数,并可能重用表达式逻辑。此外,在许多情况下,变量可能会显著提高您的计算性能,因为引擎将只对表达式求值一次,而不是多次!

最后,使用变量使您能够更容易地调试代码,并验证公式某些部分的结果。

下面是一个在 DAX 代码中使用变量的简单示例:

作者图片

正如您可能注意到的,定义变量需要在表达式被求值并赋给特定变量之前使用 var 关键字。

当然,上面的例子相当简单,但是让我们假设我们想以百分比的形式显示同比变化。我们可以这样写一个度量:

作者图片

这里要注意的第一件事是,我们重复使用完全相同的表达式来计算前一年的销售额。我们可以这样写同样的度量:

作者图片

我想我们都同意第二个版本可读性更强,更容易阅读和理解。正如我所说,这是一个相当基本的公式,你可以想象在更复杂的场景中使用嵌套函数的变量的影响。

变量可以在度量和计算列中使用。

处理空白

在创建报告时,我相信您会面临得到(空白)的情况,并且您不希望以这种方式向最终用户显示。

我已经写了一篇文章,展示了三种可能的方法来处理你的 Power BI 报告中的空白。

您可以选择使用 IF 语句、COALESCE()函数,或者通过在数值计算中添加 0 来应用技巧。

然而,在另一篇文章中,我也解释了为什么在用其他值替换空白值之前应该三思。在某些情况下,这可能是真正的性能杀手。

DAX 中的虚拟关系

在我解释什么是虚拟关系以及如何创建虚拟关系之前,我想强调的是,在数据模型中的表之间拥有物理关系的 始终是一种推荐的做法 !但是,在某些情况下,可能会发生表之间没有物理关系的情况,您只需要模拟不存在的物理关系。

创建虚拟关系最方便的方法是使用 DAX 中的 TREATAS() 函数。正如 SQL BI 的文章中所解释的,这是创建与 TREATAS 的虚拟关系的伪代码的样子:

[Filtered Measure] :=
CALCULATE (
    <target_measure>,
    TREATAS (
        SUMMARIZE (
            <lookup_table>
            <lookup_granularity_column_1>
            <lookup_granularity_column_2>
        ),
        <target_granularity_column_1>,
        <target_granularity_column_2>
    )
)

让我们看看现实生活中的例子是什么样的!我将向您展示如何在角色扮演维度场景中利用虚拟关系。在之前的一篇文章中,我解释了如何使用 USERELATIONSHIP()函数处理角色扮演维度来更改表之间的活动关系,现在我将向您展示如何在表之间创建两个与模型中的物理关系无关的虚拟关系:

作者图片

假设我想分析在特定日期(订单日期)下了多少订单,以及在特定日期(发货日期)下了多少订单。第一个度量将在 OrderDate 列上的 FactResellerSales 和 DimDate 表之间建立虚拟关系:

Total Quantity Order Date = 
                CALCULATE(
                            SUM(FactResellerSales[OrderQuantity]),
                            TREATAS(
                                VALUES(DimDate[FullDateAlternateKey]),
                                FactResellerSales[OrderDate]
                            )
                )

本质上,作为虚拟关系的查找表,通过使用 VALUES()函数,我们从 DimDate 表中获取所有不同的(非空)值。在这个虚拟关系的另一边是我们的 OrderDate 列。让我们创建一个类似的度量,但是这次在 ShipDate 列上建立一个虚拟关系:

Total Quantity Ship Date = 
                CALCULATE(
                            SUM(FactResellerSales[OrderQuantity]),
                            TREATAS(
                                VALUES(DimDate[FullDateAlternateKey]),
                                FactResellerSales[ShipDate]
                            )
                )

这是我们在表格上放置两个度量后的外观:

作者图片

因此,即使我们的表与物理关系无关,我们也能够“即时”创建关系,并在 Power BI 报告中显示正确的数字。

DAX 迭代器

不像聚合器聚合特定列的所有值,返回单个值 , 迭代器为它们正在操作的表格的每一行应用表达式

因此,两者之间的第一个区别是迭代器需要(至少)两个参数才能工作——第一个参数始终是迭代器需要迭代的表(物理表或虚拟表),第二个参数是需要应用于该表每一行的表达式。

最常见的迭代器函数实际上是聚合器函数的“亲戚”:

作者图片

如你所见,迭代器函数的末尾有字母 X,这是在 DAX 公式中识别它们的最简单的方法。然而,一个有趣的事实是聚合函数也被引擎内部翻译成迭代器函数!所以,当你写下这样的话:

Sales Amount = SUM('Online Sales'[SalesAmount])

在内部翻译成:

Sales Amount = SUMX('Online Sales',
                     'Online Sales'[SalesAmount]
                 )

请记住,当你想写一个包含不止一列的表达式时,你必须使用迭代器!

迭代器函数需要理解的关键是它们运行的上下文。当它们逐行迭代时,表达式在行上下文中进行计算,类似于计算列 DAX 公式。然而,该表是在筛选器上下文中进行评估的,这意味着,比方说,如果“在线销售”表上有一个活动的筛选器只显示 2019 年的数据,则最终结果将包括 2019 年的行的总和(假设您使用的是 SUMX 迭代器函数)。

Sales Amount Iterator = 
                    SUMX (
                        FactResellerSales,
                        FactResellerSales[OrderQuantity] * FactResellerSales[UnitPrice]
                    )

作者图片

关于迭代器函数的最后一个警告:在大量数据上使用复杂的迭代器函数时要小心,因为它们是逐行计算的,在某些情况下可能会导致性能问题。

结论

不断重复:“DAX 很简单,但不容易!”…正如您可能已经看到的,DAX 为您提供了一个快速进入(im)可能世界的入口,在这里您可以执行各种计算,但您需要了解语言的细微差别,并了解引擎在后台的实际工作方式。

我强烈建议关注 SQL BI 频道和博客,或者阅读“DAX 圣经”:DAX 权威指南,第二版。

网络上还有其他学习 DAX 的极好资源,比如企业 DNA 频道,或者布莱安·葛兰特的令人敬畏的系列视频,叫做“DAX 元素”。

感谢阅读!

DBSCAN 集群:给我分解一下

原文:https://towardsdatascience.com/dbscan-clustering-break-it-down-for-me-859650a723af

强大算法的简单介绍

欢迎回到我不知道为什么我对这个算法感到不安的世界,因为我完全凭直觉得到它,在这里我们采用复杂的机器学习算法,并用有趣的插图将它们分解成简单的步骤。

今天,我们将处理另一种叫做 DBSCAN ( 基于密度的应用程序空间聚类)的聚类算法。为了更好地理解 DBSCAN,首先查看一下 K-Means 和层次聚类文章。

顾名思义,DBSCAN 通过点的密度来识别簇。聚类通常位于高密度区域,异常值往往位于低密度区域。使用它的 3 个主要优势(根据这个算法的先驱)是它需要最少的领域知识,可以发现任意形状的集群,并且对于大型数据库是有效的。

既然我们已经介绍完了,让我们进入有趣的部分——真正理解它是如何工作的。假设我们的原始数据是这样的:

第一件要做的事是数点接近的每一个点。我们通过围绕一个点画一个具有一定半径的圆( eps )来确定这个接近度,并且任何落在这个圆内的其他点都被称为接近第一个点。例如,从这个粉红色的点开始,围绕它画一个圆。

围绕任意一点画一个半径为 eps 的圆

我们看到这一点与其他 7 点完全或部分重叠。所以我们说粉点接近 7 分。

称为 eps 的圆的半径,是我们需要定义的 DSBCAN 算法中的第一个参数。我们需要适当地定义 eps ,因为如果我们选择的值太小,很大一部分数据将不会被聚集。另一方面,如果我们选择一个太大的值,聚类将合并,许多数据点将在同一个聚类中。一般来说,较小的 eps 值是优选的。

现在考虑这个蓝点。我们看到它接近 3 个点,因为它的半径为 eps 的圆与另外 3 个点重叠。

同样,对于所有剩余的点,我们计算接近点的数量。一旦我们这样做了,我们需要决定哪些点是 核心点 ,哪些点是 非核心点

这就是我们算法的第二个参数——min points——出现的地方。我们使用最小点来确定一个点是否是核心点。假设我们将最小点设置为 4,那么我们说如果至少有 4 个点靠近一个点,那么这个点就是核心点。如果少于 4 个点接近一个点,则视为非核心点

一般来说, minPoints 数据集中的维数+ 1。对于有噪声的数据集,较大的值通常更好。 minPoints 的最小值必须是 3,但是我们的数据集越大, minPoints 的值就必须越大。

对于我们的示例,让我们将 minPoints 设置为 4。那么我们可以说粉色点是一个 核心点 因为至少有 4 个点接近它,而蓝色点是一个 非核心点 因为只有 3 个点接近它。

最终,利用以上过程,我们可以确定以下高亮点是 核心点……

…而剩下的就是 非核心点

现在,我们随机选取一个 核心点 并将其分配给第一个集群。这里,我们随机选择一个点,并将其分配给蓝色簇。

接下来,靠近蓝色星团的 核心点 ,意味着它们与半径为 eps 的圆重叠…

…全部添加到蓝色集群中:

然后,将靠近成长中的蓝色星团的 核心点 加入其中。下面,我们看到 2 个 核心点 和 1 个 非核心点 靠近蓝色集群,但是我们只把 2 个 核心点 添加到集群中。

最终,所有靠近增长的蓝色集群的 核心点 都被添加到其中,数据将如下所示:

接下来,我们将所有靠近蓝色星团的非核心点加入其中。例如,这 2 个 非核心点 靠近蓝色集群,因此它们被添加到其中:

然而,由于它们不是**核心点,我们不使用它们来进一步扩展蓝色集群。这意味着靠近 非核心点 1 的另一个 非核心点 将不会被添加到蓝色群集。**

所以,与 核心点不同,非核心点** 只能加入一个簇,不能用来进一步扩展。**

添加完所有的 非核心点 后,我们就完成了蓝色集群的创建,看起来像这样:

现在因为剩余的 核心点 都没有接近第一个集群,我们开始形成新的集群的过程。首先,我们随机挑选一个 核心点 (不在一个集群中)并将其分配给我们的第二个黄色集群。

然后我们添加所有靠近黄色聚类的 核心点 并用它们来进一步扩展聚类。

将剩余的核心点添加到第二个黄色聚类中

然后将靠近黄色簇的 非核心点 加入其中。完成后,我们的 2 个集群的数据如下所示:

在将非核心点添加到黄色聚类之后

我们不断重复这个创建集群的过程,直到我们没有核心点了。在我们的例子中,由于所有的 核心点 都已经被分配给一个集群,我们就完成了新集群的创建。最后,任何剩余的 非核心点 不接近 核心点 且不属于任何聚类的点称为 离群点**

就这样,我们建立了两个集群,发现了异常值,并从另一边毫发无损地出来了。

有人可能会问——为什么 DBSCAN 要优于 K-Means 或层次聚类?

K-Means 和 Hierarchical 适用于紧凑且分离良好的聚类,但也会受到数据中的噪声和异常值的严重影响。另一方面,DBSCAN 捕捉复杂形状的集群,并在识别异常值方面做得很好。DBSCAN 的另一个好处是,与 K-Means 不同,我们不必指定聚类的数量( k ),算法会自动为我们找到聚类。下图举例说明了两者的区别,以及为什么 DBSCAN 在适当使用时会很强大。

**图片来源:【https://github.com/NSHipster/DBSCAN **

今天到此为止。请随时在 LinkedIn 上与我联系,或在shreya.statistics@gmail.com给我发电子邮件,向我发送关于任何其他您想要说明的算法的问题和建议!

如果你想支持我的工作,可以考虑使用我的链接来注册一个媒体订阅!(每月 5 美元,随时取消)

什么是 dbt(数据构建工具)

原文:https://towardsdatascience.com/dbt-55b35c974533

温和地介绍正在接管数据世界的 dbt

罗宾·皮耶尔在 Unsplash 拍摄的照片

dbt,或dATAbuildtool,是一个开源命令行工具,帮助组织构建、测试和维护他们的数据基础设施。该工具旨在通过提供一致和标准化的数据转换和分析方法,使数据分析师和工程师更容易处理数据。

dbt 允许用户使用 SQL 定义他们的数据模型,然后使用这些模型生成优化的 SQL 代码,这些代码可以针对数据仓库或其他数据存储系统运行。这允许用户构建可维护和可伸缩的数据基础设施,该基础设施可以随着时间的推移而容易地更新和扩展。

除了生成 SQL 代码之外,dbt 还提供了许多使数据处理更加容易的特性。这些特性包括管理数据模型之间的依赖关系、运行测试以确保数据完整性,以及跟踪数据的谱系以了解它是如何随着时间的推移而变化的。

dbt 的使用案例

dbt 可以以多种方式使用。该技术的一些常见用例包括:

  • 构建和维护数据管道 : dbt 可用于使用 SQL 定义数据模型,然后生成优化的 SQL 代码,这些代码可针对数据仓库或其他数据存储系统运行。这允许用户构建和维护可伸缩的数据基础设施。
  • 确保数据质量和完整性 : dbt 提供了许多特性,可以更容易地确保数据的质量和完整性。这包括运行测试以验证数据的能力,以及跟踪数据的谱系以了解它是如何随着时间的推移而转变的。
  • 标准化数据转换过程 : dbt 为数据转换和分析提供了一致的标准化方法,使数据分析师和工程师更容易处理数据。这可以帮助组织提高其数据的质量和可靠性,并使其更容易获取见解和推动业务决策。
  • 为数据团队提供协作环境 : dbt 允许数据分析师和工程师在相同的数据模型和转换上一起工作,为数据团队提供协作环境。这有助于改善数据团队内部的沟通和协作,并使处理复杂的数据项目变得更加容易

dbt-核心与 dbt 云

dbt-core 和 dbt Cloud 是由数据构建工具的创建者 Fishtown Analytics 提供的两种不同的产品。

dbt-core 是一个开源命令行工具,它允许用户使用 SQL 定义他们的数据模型,然后使用这些模型来生成优化的 SQL 代码,这些代码可以针对数据仓库或其他数据存储系统运行。

另一方面,dbt Cloud 是一个基于云的平台,它在 dbt-core 的基础上提供了额外的特性和功能。dbt Cloud 提供了一个基于 web 的界面来管理数据模型,以及其他特性,如调度、协作工具和与其他数据工具的集成。

总之,dbt-core 是支持 dbt 的底层开源工具,而 dbt Cloud 是一个基于云的平台,提供额外的特性和功能。dbt-core 可以单独使用,也可以与 dbt Cloud 结合使用,提供更全面的数据基础设施解决方案。

什么 dbt 不是?

dbt不是数据仓库或数据库本身,而是一种工具,它可以与数据仓库结合使用,使处理和管理数据更加容易。另外,dbt不是一种编程语言,但是它确实使用了一种类似编程的语法来指定数据应该如何被转换和加载到数据仓库中。它也不是可视化工具,尽管它可以与可视化工具如 Tableau 或 Looker 结合使用,以帮助用户理解和分析他们的数据

dbt 入门

如果您是 dbt 新手,并且想要启动新的 dbt 项目,您可以参考我最近的一些文章,这些文章将帮助您为您的特定数据仓库或存储安装 dbt以有意义和可伸缩的方式构建您的 dbt 项目

最后的想法

总的来说,dbt 是一个强大的工具,可以帮助组织改善他们的数据基础设施,并使数据分析师和工程师更容易处理数据。通过提供一致和标准化的数据转换和分析方法,dbt 可以帮助组织提高其数据的质量和可靠性,并使提取见解和推动业务决策变得更加容易。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

</2-rules-groupby-sql-6ff20b22fd2c>

如何构建您的 dbt 项目和数据模型

原文:https://towardsdatascience.com/dbt-models-structure-c31c8977b5fc

对 dbt 数据模型实施有意义的结构

阿兰·范在 Unsplash 上的照片

数据模型是表示应用程序域中特定对象的抽象结构。这种模型可以是特定组织的用户、产品和订单。在现代组织中,应该制定数据驱动的决策,因此能够高效和有效地管理数据模型非常重要。

随着公司收集和处理的数据量不断增长,需要维护数百甚至数千个数据模型。这意味着,考虑到一些模型可能是其他数据模型的上游或下游依赖关系,也需要管理它们的相互依赖关系。

在蓬勃发展的数据行业中,大量的工具可供数据专业人员和团队使用。数据构建工具(dbt)是由 Fishtown Analytics 开发的开源工具,无疑是最强大的工具之一,我强烈推荐给每个团队,如果他们对现代组织中可扩展的有效数据管理感兴趣的话。 dbt 可以帮助数据团队创建数据集和模型,作为分析、报告、机器学习建模和一般数据工作流的一部分

dbt 是一个开发框架,它将模块化 SQL 与软件工程最佳实践结合起来,使数据转换变得可靠、快速和有趣。- dbt 文档

除了在工作中使用正确的工具,同样重要的是确保你也以正确的方式使用它们。在我最近的一篇文章中,我们讨论了关于如何安装 dbt 命令行接口以及您需要访问的特定数据平台所需的适配器。

在今天的文章中,我们将讨论如何在 dbt 项目中正确构建数据模型。数据通常是组织中混乱的资产,因此尽可能加强结构总是很重要的。在接下来的几节中,我们将讨论三种类型的数据模型——在 dbt 的上下文中——以及如何以一种有意义且可伸缩的方式构建它们,这将允许数据团队的团队成员保持一致。

分期、中间和集市模型

数据模型各不相同——我的意思是,一些数据模型可能对应于一些特定的数据源,另一些可能将多个数据源甚至其他数据模型组合在一起,等等。因此,创建数据模型的层非常重要。拟分层由三类模型组成,即分期模型、中间模型和集市模型。

阶段模型,是您的 dbt 项目中所有数据模型的构建块。分期模型应该包括基本计算(如字节到千兆字节)重命名类型转换分类(使用CASE WHEN语句)。然而,您应该避免在分段模型上执行任何连接和聚合。作为一般的经验法则,你应该在你的数据源和阶段模型之间有 1-1 的映射。因为阶段模型不应该代表最终的工件,所以建议将它们具体化为视图

中间模型,被认为是将阶段模型甚至其他中间模型集合在一起,它们往往比阶段模型更复杂一些。换句话说,这些模型代表了更有意义的构建模块,将来自多个模型的信息集合在一起。但是请注意,它们不应该向最终用户公开(即由 BI 工具使用,如 Tableau、PowerBI 或 Looker)。同样重要的是要提到,作为一个经验法则,如果一个中间模型在几个地方被引用,那么你可能不得不考虑构建一个宏,或者重新考虑你设计模型的方式。

集市模型,是商业定义的实体,应该由终端用户和商业智能工具消费。每个集市模型都代表一个细粒度的实体——支付、客户、用户、订单只是我们作为集市所代表的一些例子。

构建您的 dbt 模型

既然我们已经对数据构建环境中的三种主要模型类型有了坚实的理解,那么让我们开始讨论如何以一种有意义的方式构建这些模型,以帮助数据团队以一种简单直观的方式维护和扩展它们。

在您的 dbt 项目中,您需要有一个名为models的父目录,由三个目录组成,每个目录代表我们前面讨论的一个模型类型:

models
|---intermediate
|---marts
|---staging

现在让我们从分期模型开始。

  • 对于每个不同的源,您需要在staging目录下创建一个子目录
  • 每一个模型,都必须遵循stg_[source]__[entity]s.sql符号
  • 模型目录下的一个base子目录,以防您需要将登台模型连接在一起

举个例子,假设我们有三个独立的来源——一个是脸书广告(营销活动),另一个来自 Stripe(支付),第三个包含我们的在线商店的商业实体。

models/staging
|---facebook_ads
|   |---_facebook_ads__models.yml
|   |---_facebook_ads__sources.yml
|   |---_facebook_ads__events.yml
|---my_shop
|   |---_my_shop__models.yml
|   |---_my_shop__sources.yml
|   |---base
|   |  |---base_my_shop__deleted_products.sql
|   |  |---base_my_shop__deleted_users.sql
|   |  |---base_my_shop__products.sql
|   |  |---base_my_shop__users.sql
|   |---stg_my_shop__orders.sql
|   |---stg_my_shop__products.sql
|   |---stg_my_shop__users.sql
|---stripe
    |---_stripe_models.yml
    |---_stripe_models.yml
    |---stg_stripe__payments.yml

请注意我们是如何为每个不同的源创建一个单独的子目录的,每个子目录都由两个 yml 文件组成——一个用于定义模型,另一个用于源——以及您为每个源拥有的尽可能多的登台模型。

现在让我们继续讨论中级车型

  • 对于每个不同的业务组,我们创建一个子目录——非常类似于我们前面介绍的分级结构
  • 每一款中级车型,都必须遵循int_[entity]s_[verb]s.sql的命名惯例。请注意,使用动词作为命名的一部分将有助于您构建名称,这有助于读者和维护人员清楚地了解特定模型应该做什么。这样的动词有joinedaggregatedsummed等。

例如,假设我们有两个业务组,即financemarketing:

models/intermediate
|---finance
|   |---_int_finance__models.yml
|   |---int_payments_pivoted_to_orders.sql
|---marketing
|   |---_int_marketing__models.yml
|   |---int_events_aggregated_per_user_platform.sql

最后,让我们看看如何构建我们的最终构件,即对应于业务定义的实体的集市模型

  • 为每个部门、业务单位或实体创建一个子目录
  • 每个 mart 模型都应该简单地以它所代表的实体命名。例如ordersuserspayments
  • 避免跨多个不同业务单元的重复实体(这通常是一种反模式)。
models/marts
|---finance
|   |---_finance__models.yml
|   |---orders.sql
|   |---payments.sql
|   |---payroll.sql
|   |---revenue.sql
|---marketing
|   |---_marketing__models.yml
|   |---campaigns.sql
|   |---users.sql

命名约定:概述

这是一篇相当长的文章,包含了太多的信息——尤其是如果您是 dbt 新手的话——所以让我回顾一下关于命名约定的一些要点。

  • models目录下,为每个数据模型类型创建三个子目录
  • 分级模型需要遵循stg_[source]__[entity]s.sql命名约定
  • 中间型号需要遵循int_[entity]s_[verb]s.sql惯例
  • 集市模型需要以它们所代表的实体命名
  • 暂存模型目录下的一个base子目录,以防您需要将暂存模型连接在一起

这是我们在前面几节中经历的示例的最终结构。

models
|---intermediate
   |---finance
   |   |---_int_finance__models.yml
   |   |---int_payments_pivoted_to_orders.sql
   |---marketing
   |   |---_int_marketing__models.yml
   |   |---int_events_aggregated_per_user_platform.sql
|---marts
    |---finance
    |   |---_finance__models.yml
    |   |---orders.sql
    |   |---payments.sql
    |   |---payroll.sql
    |   |---revenue.sql
    |---marketing
    |   |---_marketing__models.yml
    |   |---campaigns.sql
    |   |---users.sql
|---staging
   |---facebook_ads
   |   |---_facebook_ads__models.yml
   |   |---_facebook_ads__sources.yml
   |   |---_facebook_ads__events.yml
   |---my_shop
   |   |---_my_shop__models.yml
   |   |---_my_shop__sources.yml
   |   |---base
   |   |  |---base_my_shop__deleted_products.sql
   |   |  |---base_my_shop__deleted_users.sql
   |   |  |---base_my_shop__products.sql
   |   |  |---base_my_shop__users.sql
   |   |---stg_my_shop__orders.sql
   |   |---stg_my_shop__products.sql
   |   |---stg_my_shop__users.sql
   |---stripe
       |---_stripe_models.yml
       |---_stripe_models.yml
       |---stg_stripe__payments.yml

显然,我们今天演示的结构可能不是 100%适合您的用例,所以请随意相应地修改它——但是无论什么情况,都要确保清楚地定义这种结构背后的逻辑,并坚持下去。

最后的想法

数据模型管理是现代数据团队必须做好的最重要的支柱之一。对此类模型的薄弱管理可能会导致数据质量下降、数据停机以及难以扩展和丰富您的数据资产。鉴于现代公司需要基于数据做出决策,糟糕的数据模型管理可能会带来灾难性的后果,并导致错误的决策。

在今天的文章中,我们对数据构建工具(dbt)如何帮助现代组织和数据团队更高效地管理数据模型进行了高度概括。但最重要的是,我们讨论了如何在 dbt 本身中构建项目和数据模型。数据构建工具是最强大的工具之一,但是使用正确的工具还不够,以正确的方式使用正确的工具也很重要。

关于 dbt 项目结构的更全面的阅读,你可以参考官方 dbt 文档

成为会员 阅读媒介上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

</2-rules-groupby-sql-6ff20b22fd2c>

DDL 和 DML 是什么?

原文:https://towardsdatascience.com/ddl-dml-e802a25076c6

理解 SQL 中 DDL 和 DML 的区别

张志勇Unsplash 上的照片

结构化查询语言(SQL)是用于设计、访问和操作数据库的标准语言。该语言通常用于在指定的数据库上执行某些命令。

在今天的文章中,我们将讨论各种 SQL 命令是如何根据它们对数据库状态的影响而被划分为子类别的。这些类别包括数据定义语言(DDL)和数据操作语言(DML)。

数据定义语言

数据定义语言由命令组成,这些命令归纳了模式和数据库中对象的构造方式。这些 SQL 命令通常由管理数据库本身的人来执行。

属于 DDL 的 SQL 命令:

  • **ALTER**:改变现有数据库对象的结构
  • **COMMENT**:将注释插入数据字典
  • **CREATE**:创建数据库和数据库对象,如索引
  • **DROP**:删除数据库对象
  • **RENAME**:重命名现有的数据库对象
  • **TRUNCATE**:删除指定表格中的所有记录,释放分配给它们的空间

数据操作语言

数据操作语言由用于插入、修改或删除数据库数据的 SQL 命令组成。DML 下的命令如下

  • **CALL**:调用子程序(如 PL/SQL)
  • **DELETE**:从数据库中删除数据记录
  • **INSERT**:将数据记录插入数据库
  • **LOCK**:锁定数据库上的资源(并发控制)
  • **MERGE**:插入或更新数据记录(又名 UPSERT)
  • **UPDATE**:更新数据库中的现有数据

DQL、大昌洋行和 TCL

除了数据定义和操作语言之外,还有三个子类别来完成这个难题。

数据查询语言(DQL)

数据查询语言是唯一由一个命令组成的子类。但同时我也要说,它是最常用的,因为它可以从数据库中查询数据。

  • **SELECT**:从数据库中检索数据

数据控制语言(DCL)

数据控制语言由命令组成,这些命令用于指定对感兴趣的数据库的权限。属于这个家族的两个命令是

  • **GRANT**:授予用户访问数据库的权限
  • **REVOKE**:撤销用户通过GRANT赋予的权限

交易控制语言(TCL)

最后,事务控制语言由用于与事务交互的 SQL 命令组成。一些例子是

  • **COMMIT**:提交交易
  • **ROLLBACK**:回退交易(如失败)
  • **SAVEPOINT**:标记交易中的一个点
  • **SET** **TRANSACTION**:指定交易的特征(如隔离级别)

最后的想法

下图说明了如何将 SQL 命令分成我们之前讨论过的 5 个不同的子类别。

分为 5 个子组的 SQL 命令—来源:作者

正如您所知,数据定义语言(DDL)和数据操作语言(DML)是将 SQL 命令组合在一起的两个最大的子类别。

一方面,DDL 命令主要用于改变数据库的模式,而属于 DML 的命令用于实际检索、修改或向/从数据库插入数据。

另一个可以用来识别 DML/DDL 命令的经验法则是表达式是否使用了WHERE子句。DML 命令可以利用WHERE子句,而另一方面,DDL 命令则不能。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

航位推算仍然存在!

原文:https://towardsdatascience.com/dead-reckoning-is-still-alive-8d8264f7bdee

介绍航位推算方法,使用具有自主车辆焦点的捷联惯性导航系统。

简介和动机

今天,许多司机都对自动驾驶汽车的梦想非常好奇。这个梦想会实现吗,什么时候实现?AV 需要实现的核心技术之一是惯性导航系统(INS)。这些系统将许多传感器集成在一起,我们称之为“传感器融合”方案。这些传感器包括激光雷达、相机、GPS 接收器、雷达、加速度计、陀螺仪等等。一般的传感器融合方案使用一种非常通用的算法将所有的传感器集成在一起,该算法被命名为“”,以最优地融合所有的传感器(在均方误差意义上)。那么,我们在路上还没有看到一部 AV 的原因是什么呢?在这篇文章中,我们将关注一个主要问题:如何在隧道、桥下、室内停车场、城市峡谷和更加困难的环境中导航?

由 Stocksnap 拍摄的图像

我们希望我们的无人机能够在这些困难的环境中安全航行。看来,在上述所有传感器中,只有加速度计和陀螺仪在任何物理约束下仍能正常工作:白天或黑夜、光线或黑暗、高温或低温等。大多数情况下,这两个高度重要且低成本的传感器组合在一个惯性测量单元** ( IMU )中,几乎随处可见。在现代车辆中,有近 30 个 IMU 传感器支持 ADAS、ABS、巡航控制和许多其他重要系统。 IMU 的一个关键缺点是其附加噪声。例如,当我们对加速度计测量值进行积分以估计 AV 位置时,我们实际上也考虑了噪声,从而导致非常大的累积误差。所以 AV 位置解有很大的漂移。**

什么是捷联惯导算法?

使用 IMU 传感器为汽车导航的常用算法之一是捷联惯性导航系统,**或简称为“INS”。这也被称为“航位推算”。大多数情况下,我们发现 INS 与额外的传感器一起工作,例如 GPS 接收器或照相机。我们称之为 INS/GPS 融合或视觉辅助 INS。融合包括卡尔曼滤波器及其扩展版本。如果我们操作时间很短(几秒钟,没有 GPS 信号或视觉辅助的非视距),只想为我们的 AV 提供连续的位置解决方案,实际上没有必要考虑所有这些融合- IMU **

货车进入隧道(stocksnap)

INS 是如何工作的?超越数学。

首先,我们需要了解这是一种实时算法,它考虑了来自加速度计的加速度和来自车辆陀螺仪的角速度。一旦我们想使用 INS,我们需要提供初始条件(通常,我们提供 GPS 接收机可用时获得的最后值)。现在,我们可以为角速度和加速度定义两个向量。角速度矢量由下式定义:

围绕 x、y 和 z 轴的角速度。

其中, b 表示车身坐标系,而 i 表示惯性坐标系(仅提及测量是在车身坐标系中进行的,并在惯性坐标系中旋转——实际上是地球。加速度矢量(用字母 f 表示特定的力,相当于加速度)由下式给出:

x、y 和 z 轴上的加速度。

这两个测量向量是我们从 IMU 传感器获得的唯一向量,它们允许我们计算 AV 的位置、速度和方向,这非常令人惊讶!

我们这里有 INS 方程,忽略地球自转,以 3D 矢量形式:

捷联惯导算法

其中 p 代表位置 v 代表速度, R 是作为身体方位角的函数的旋转矩阵(下面提供),*只是已知的地球重力矢量,ω是角速度的倾斜矩阵,由下式提供:*

角速度的偏斜矩阵

请注意 nb 脚本: n 代表导航框架,意思是 AV 的位置、速度、方位都表示在导航地图中(例如)。当 AV 沿着导航地图移动时,我们需要在每个时间步旋转 IMU 测量值!这是使用旋转矩阵实现的:

带欧拉角的旋转矩阵。

c 代表 cos, s 代表 sin。AV 车身角度由三个角度提供,如图所示:

作者图片

摘要

所以,就这样了!现在,您可以继续导航几秒钟,直到您的导航解决方案(位置、速度和方向)开始漂移。但是,嘿!也许这足以让 AV 在艰难的环境中保持在路上,并增加它的安全概率——想想看…

我建议你阅读我之前关于惯性导航深度学习的工作。在那里,进行深度学习的集成,以便捕获 IMU 噪声,并由此提高导航精度。

关于作者

Barak 获得了以色列理工学院的航空工程学士学位(2016 年)、硕士学位(2018 年)以及经济和管理学士学位(2016 年,成绩优异)。他曾在高通工作(2019-2020),在那里他主要研究机器学习和信号处理算法。巴拉克目前正在海法大学攻读博士学位。他的研究兴趣包括传感器融合、导航、深度学习和估计理论。

目标编码分类变量

原文:https://towardsdatascience.com/dealing-with-categorical-variables-by-using-target-encoder-a0f1733a4c69

一个很好的替代方法是一键编码你的类别

腾雅特在 Unsplash 上原创

分类变量对机器学习算法来说是一个挑战。因为它们中的大多数(如果不是全部)只接受数值作为输入,所以我们需要将类别转换成数字,以便在模型中使用它们。

通过一次性编码,我们创建了一个真正稀疏的矩阵,并增加了模型需要处理的维数,我们可能会成为可怕的维数灾难的受害者。当特征具有太多类别时,这种情况会被放大,其中大部分类别对于预测是无用的。

处理这个问题的一个聪明的方法是目标编码器。

本文中使用的代码和示例也可以在我的 GitHub 存储库中找到:

https://github.com/vinyluis/Articles/tree/main/Target Encoder

目标编码器

目标编码器背后的主要思想是通过替换类别来对类别进行编码,以测量它们可能对目标产生的影响。

在二元分类器上,最简单的方法是计算概率 p(t = 1 | x = ci) ,其中 t 表示目标, x 是输入, ci 是第 I 个类别。在贝叶斯统计中,假定输入是类别 ci ,这被认为是 t=1 的后验概率。

这意味着我们将用类别 ci 替换目标的后验概率值为 1 的类别。

考虑下面的数据集:

作者图片

对于每一个可能的类别(非虚构、浪漫、戏剧、科幻和幻想),我们需要计算目标 0 和目标 1 出现的次数。然后我们计算:

作者图片

这可以通过下面的代码来完成:

categories = df['genre'].unique()
targets = df['target'].unique()
cat_list = []
for cat in categories:
    aux_dict = {}
    aux_dict['category'] = cat
    aux_df = df[df['genre'] == cat]
    counts = aux_df['target'].value_counts()
    aux_dict['count'] = sum(counts)
    for t in targets:
        aux_dict['target_' + str(t)] = counts[t]
    cat_list.append(aux_dict)cat_list = pd.DataFrame(cat_list)
cat_list['genre_encoded_dumb'] = cat_list['target_1'] / cat_list['count']

所以最后,cat_list数据帧看起来会像这样:

作者图片

既然感兴趣的目标是值“1”,那么这个概率实际上就是目标的 均值 ,给定一个类别。这就是为什么这种目标编码方法也被称为“平均”编码的原因。

我们可以用一个简单集合来计算这个平均值,然后:

stats = df['target'].groupby(df['genre']).agg(['count', 'mean'])

作者图片

这就对了。变量及其各自的编码用一行代码表示。

我们可以用它们的编码值替换类别,如下所示:

作者图片

由于我们使用每个类别的目标平均值,这种方法也很容易适用于回归模型。

这种方法的问题

这种编码方式真的很简单也很强大。然而,在使用它的时候,有一些重要的问题你需要记住。

一个真正重要的影响是目标泄漏。通过使用目标的概率对特征进行编码,我们向它们提供了我们试图建模的变量的信息。这就像“欺骗”,因为模型将从一个自身包含目标的变量中学习。

潘晓珍在 Unsplash 上原创

你可以认为这可能不是一个问题,如果这种编码反映了目标的真实概率,给定一个类别。但是如果是这样的话,我们甚至不需要一个模型。相反,我们可以使用这个变量作为这个目标的一个强大的预测器。

此外,使用平均值作为整个分布的预测值是好的,但并不完美。如果所有不同的数据分布和组合都可以用一个平均值来表示,我们的生活会轻松得多。

即使平均值是一个很好的总结,我们也要在一小部分数据中训练模型。这个分数的平均值可能不是全部人口的平均值(还记得中心极限定理吗?),所以编码可能不正确。如果样本与总体差异足够大,模型甚至可能会过度拟合训练数据。

具有先验平滑的目标编码器

我们可以使用先前的平滑来减少那些不想要的影响。

这个想法很简单。假设我们有一个模型来预测在线商店中一本书的质量。我们可能有一本书有 5 个评价,得到 9.8 分(满分 10 分),但其他书的平均分数是 7 分。这种影响是因为我们使用的是小样本的平均值,与我上面提到的问题类似。

通过考虑所有书籍的平均值,我们可以用更少的评价来“平滑”这本书的分数。

回到我们的例子,我们有 5 个类别要编码:非小说,浪漫,戏剧,科幻和幻想,我们已经知道如何使用每个类别的平均编码。现在我们可以使用目标在所有类别上的平均值来平滑每个类别的编码。

我们称目标的平均值为先验概率 p(t = 1)的 T6,编码可以使用从 0 到 1 的参数 T12、T13、α、T14、T15 来平衡这种平滑。

作者图片

通常,我们从下面的表达式中得到这个α:

作者图片

使用先前平滑来执行编码的代码是:

smoothing_factor = 1.0 # The f of the smoothing factor equation 
min_samples_leaf = 1 # The k of the smoothing factor equationprior = df['target'].mean()
smoove = 1 / (1 + np.exp(-(stats['count'] - min_samples_leaf) / smoothing_factor))
smoothing = prior * (1 - smoove) + stats['mean'] * smoove
encoded = pd.Series(smoothing, name = 'genre_encoded_complete')

这改编自基于 sklearn 的类别编码器库。我们还可以使用该库进行编码,而无需手动操作:

from category_encoders import TargetEncoder
encoder = TargetEncoder()
df['genre_encoded_sklearn'] = encoder.fit_transform(df['genre'], df['target'])

所有方法的结果如下:

作者图片

我们可以看到,虽然有平滑和没有平滑的方法之间存在一些差异,但它们仍然非常接近。

多类方法

到目前为止,我解释了二进制分类器的目标编码器,很容易理解我们如何使它适应回归。但是多类分类呢?

在下面的数据集上,如果我们简单地取平均值,就会认为目标 2 是目标 1 的两倍大。此外,如果目标也是一个类别,我们将如何取平均值?

作者图片

编码的结果很奇怪:非小说类的编码条件概率是 1.00,这显然是错误的,因为它可能有所有三个目标。

为了使目标编码器适用于多类分类,我们需要对每个目标的特征进行独立编码。因此,让我们计算给定每个类别的每个目标的后验概率。

categories = df['genre'].unique()
targets = df['target'].unique()
cat_list = []
for cat in categories:
    aux_dict = {}
    aux_dict['category'] = cat
    aux_df = df[df['genre'] == cat]
    counts = aux_df['target'].value_counts()
    aux_dict['count'] = sum(counts)
    for t in targets:
        aux_dict['target_' + str(t)] = counts[t] if t in counts.keys() else 0
    cat_list.append(aux_dict)cat_list = pd.DataFrame(cat_list)for t in targets:
    cat_list['genre_encoded_target_' + str(t)] = cat_list['target_' + str(t)] / cat_list['count']

结果是:

作者图片

正如所料,所有的编码现在都正确地反映了后验性。甚至“浪漫”对于目标“1”也将被编码为“0”,因为它从未出现在该类别中。

上面的代码也适用于分类目标。如果我们使用以下代码行更改目标:

df['target'] = df['target'].replace({0: 'apple', 1: "banana", 2: 'orange'})

结果不会不同:

作者图片

既然我们已经理解了在多类问题中使用目标编码需要做些什么,那么就很容易创建一个简单的代码来在这个场景中使用category_encoders.TargetEncoder对象:

from category_encoders import TargetEncodertargets = df['target'].unique()
for t in targets:
    target_aux = df['target'].apply(lambda x: 1 if x == t else 0)
    encoder = TargetEncoder()
    df['genre_encoded_sklearn_target_' + str(t)] = encoder.fit_transform(df['genre'], target_aux)

轻松搞定!两种方法的结果都足够好:

作者图片

如果您熟悉 One-Hot 编码,您会知道现在可以删除任何编码列以避免多重共线性。

结论

目标编码分类变量解决了我们通过使用一键编码得到的维数问题,但是这种方法需要小心使用以避免目标泄漏。

您应该在您的模型上使用它,并将其与其他编码进行比较,以选择更适合您的情况的编码。

如果你喜欢这个帖子…

支持我一杯咖啡!

给我买杯咖啡!

看看这个很棒的帖子

参考

https://contrib . sci kit-learn . org/category _ encoders/target encoder . html

https://maxhalford.github.io/blog/target-encoding/

https://dl.acm.org/doi/10.1145/507533.507538

熊猫数据帧中日期和时间的处理

原文:https://towardsdatascience.com/dealing-with-date-and-time-in-pandas-dataframes-7d140f711a47

了解如何操作熊猫数据框中的日期和时间值,让您的生活更轻松

埃斯特·扬森斯Unsplash 上拍摄的照片

您经常需要对 Pandas 数据帧执行的一个常见任务是操作日期和时间。根据日期和时间值最初在数据集中的编码方式,您通常需要花费大量精力来操作它们,以便可以将它们用于数据分析目的。在这篇文章中,我将向你展示一些常见的技术来处理你的熊猫数据帧中的日期和时间。

我将在本文中使用的 CSV 文件是:

将列转换为datetime64数据类型

让我们将 AAPL.csv 文件加载到熊猫数据帧中:

import pandas as pd

df = pd.read_csv('AAPL.csv')
df

您可以检查数据帧中每一列的数据类型:

df.dtypes

从下面的输出可以看出,Date列被表示为一个object:

**Date          object** Open         float64
High         float64
Low          float64
Close        float64
Adj Close    float64
Volume         int64
dtype: object

您可以使用datetime.strptime()函数将Date列转换为datetime64数据类型:

from datetime import datetime

df['Date'] = df['Date'].apply(
    lambda x: datetime.strptime(x,'%Y-%m-%d'))
df

您可以从https://docs . python . org/3/library/datetime . html # strftime-and-strptime-format-codes获取格式代码列表(如%Y%m%d)。

现在您可以检查Date列的数据类型:

df.dtypes

并且Date列现在是类型datetime64:

**Date         datetime64[ns]**
Open                float64
High                float64
Low                 float64
Close               float64
Adj Close           float64
Volume                int64
dtype: object

datetime64[ns]中的[ns]指定了 DateTime 对象的精度,单位为纳秒。

基于特定日期查找行

Date列转换为datetime64数据类型可以让您轻松地执行与日期相关的操作,例如查找 2018 年 12 月的所有行:

df[(df['Date'].dt.month == 12) & 
   (df['Date'].dt.year == 2018)]

根据星期几查找行

您还可以使用dayofweek属性来查找一周中的某一天(比如星期一、星期二等等)。例如,以下语句查找日期在星期一的所有行:

# 0 is Monday, 1 is Tue, etc
df[df['Date'].dt.dayofweek == 0].sample(5)

按星期几排序

在处理日期时,您会遇到的另一个常见场景是根据一周中的某一天对数据进行排序(例如从星期一到星期天,或者从星期天到星期六)。

这里有一个你可以使用的技巧。首先,使用strftime()函数从Date列(一个datetime64[ns]对象)中提取星期几:

df['Day'] = df['Date'].apply(
    lambda x: x.strftime('%a'))
df

提取的星期几保存到名为Day的新列中:

然后,定义要订购的日期顺序:

#---define your category order---
cats = ['Mon','Tue','Wed','Thu','Fri'] # omit Sat and Sun

在上面我省略了周六和周日,因为这两天(周末)没有股票数据。

然后,通过传递您之前定义的日期顺序,使用CategoricalDtype类创建一个新的分类数据类型:

from pandas.api.types import CategoricalDtype

cat_type = CategoricalDtype(categories=cats, ordered=True)

最后,将Day列转换为您刚刚创建的新分类类型:

#---cast the Day column as categorical---
df['Day'] = df['Day'].astype(cat_type)
df.dtypes

您现在可以看到Day属于category数据类型:

Date           object
Open          float64
High          float64
Low           float64
Close         float64
Adj Close     float64
Volume          int64
Day          category
dtype: object

如果你想打印出所有按星期排序的股票数据,你现在可以使用groupby()功能:

for _, gp in df.groupby('Day'):
    display(gp.sample(3))

请注意,分组是按Day排序的,首先是星期一,然后是星期二,依此类推:

如果您想首先显示星期二,然后显示星期三,依此类推,更改cats变量中的顺序:

cats = ['Tue','Wed','Thu','Fri','Mon'] # omit Sat and Sun
cat_type = CategoricalDtype(categories=cats, ordered=True)

#---cast the Day column as categorical---
df['Day'] = df['Day'].astype(cat_type)

for _, gp in df.groupby('Day'):
    display(gp.sample(3))

在加载期间转换日期时间列

在前面的部分中,在将整个 CSV 文件加载到 DataFrame 中之后,您将Date列转换为datetime64数据类型。另一种方法是在使用parse_dates参数加载 CSV 时执行转换:

df = pd.read_csv("AAPL.csv", parse_dates=['Date'])

parse_dates参数指定要解析为datetime64对象的列。

注意,如果将parse_dates参数设置为True,Pandas 将尝试将索引解析为一个datetime64对象

将多个列解析为日期

有时数据集中的日期分别存储在不同的列中,例如一列表示年份,一列表示月份,等等。虽然以这种方式保存数据有利也有弊,但是如果可以将所有不同的列合并成一个列,有时会更容易。这种表示的一个很好的例子是在 flights.csv 文件中。

让我们加载 flights.csv 文件,观察前五列中的五个示例行:

df = pd.read_csv("flights.csv")

# display sample 5 rows and first 5 columns
df.sample(5).iloc[:,:5]

在加载时合并列

flights.csv 文件中,每个航班的日期使用三个不同的列来表示——日。为了执行数据分析,如果可以使用parse_dates参数将三列合并成一个日期列,会更容易:

df = pd.read_csv("flights.csv", 
                 parse_dates=[[0,1,2]])
df.sample(5).iloc[:,:5]

在上面的代码片段中,我使用了列索引来指示要合并到单个列中的列。我还可以指定列的名称:

df = pd.read_csv("flights.csv", 
                 parse_dates=[['YEAR','MONTH','DAY']])

结果是前三列被删除并替换为一个新列,新列的名称是三个列名的串联:

注意,在本文的前面,我用一个列表— parse_dates=[‘Date’]设置了parse_dates参数。将其设置为列表将导致各个列作为datetime对象加载。如果你将它设置为列表的列表,比如parse_dates=[[‘YEAR’,’MONTH’,’DAY’]],那么这些列将被合并成一个单独的datetime对象。

假设您有一个如下所示的 CSV:

DATE,YEAR,MONTH,DAY
2015-09-13,2015,9,13
2015-09-14,2015,9,14

您可以将DATE列转换为datetime对象,同时使用以下parse_dates参数值将YEARMONTHDAY列合并为单个datetime对象:

df = pd.read_csv("test.csv", 
                 parse_dates=['DATE',['YEAR','MONTH','DAY']])

结果看起来像这样:

打印出df中的dtypes确认两列的数据类型:

YEAR_MONTH_DAY    datetime64[ns]
DATE              datetime64[ns]
dtype: object

加载 DataFrame 后合并列

使用to_datetime()功能加载数据帧后,您也可以合并数据帧的列:

df = pd.read_csv("flights.csv")
df['DATETIME'] = pd.to_datetime(df[['YEAR', 'MONTH', 'DAY']])
df.sample(5).iloc[:,-3:]

以下输出显示了数据帧的最后三列,最后一列是三列的组合结果— YEARMONTHDAY:

格式化时间

除了在三个单独的列中存储预定出发日期之外,您还会注意到有一个名为SCHEDULED_DEPARTURE的列:

df[['SCHEDULED_DEPARTURE']]

此列是一个整数列,存储数字,如 5,10,直到 2359。每个值代表的实际上是 HHMM 格式的出发时间。所以 5 实际上代表 00:05,而 2359 实际上代表 23:59。如果您要对这个列执行分析,您肯定需要进一步处理这个列。

这里我要做的是将四列合并成一个datetime列:

  • YEAR
  • MONTH
  • DAY
  • SCHEDULED_DEPARTURE

正如我们在上一节中所看到的,将前三者结合起来是很容易的。第四列需要一些处理:

  • 您需要将出发时间格式化为一个字符串,然后提取前两个数字来表示小时(HH)
  • 然后提取代表分钟(MM)的最后两位数字

上述操作可以按如下方式实施:

import datetime

# function to convert HHMM to datetime.time
def format_time(time):
    # format the time as string
    time = "{0:04d}".format(int(time))
    # extract hh and mm and then convert to time
    hhmm = datetime.time(int(time[0:2]), int(time[2:4]))
    return hhmm

df['SCHEDULED_DEPARTURE'] = \
    df['SCHEDULED_DEPARTURE'].apply(format_time)
df[['SCHEDULED_DEPARTURE']]

合并日期和时间列

既然我们的 dataframe 有两个数据类型为datetime的列— DATETIMESCHEDULED_DEPARTURE,我们现在可以将它们合并成一个列。以下代码片段使用apply()函数和datetime.combine()函数来组合两个指定的列:

from datetime import datetime

df['SCHEDULED_DEPARTURE'] = \
    df.apply(
        lambda r: datetime.combine(r['DATETIME'], r['SCHEDULED_DEPARTURE']), 
        axis=1)
df.sample(5)[['SCHEDULED_DEPARTURE']]

SCHEDULED_DEPARTURE列现在包含出发日期和时间:

现在,您可以轻松找到在特定时间起飞的所有航班:

df[(df['SCHEDULED_DEPARTURE'].dt.month == 12) & 
   (df['SCHEDULED_DEPARTURE'].dt.year == 2015) & 
   (df['SCHEDULED_DEPARTURE'].dt.hour >= 22) & 
   (df['SCHEDULED_DEPARTURE'].dt.minute > 30)]\
   [['FLIGHT_NUMBER','SCHEDULED_DEPARTURE']]

如果你喜欢阅读我的文章,并且认为它对你的职业/学习有所帮助,请考虑注册成为一名灵媒会员。每月 5 美元,你可以无限制地访问 Medium 上的所有文章(包括我的)。如果你使用下面的链接注册,我会赚一小笔佣金(不需要你额外付费)。你的支持意味着我将能够投入更多的时间来写这样的文章。

https://weimenglee.medium.com/membership

摘要

我希望这篇文章在处理您的 Pandas 数据帧中的日期和时间时对您有用。特别是,我介绍了:

  • 如何在加载数据帧后将列转换为datetime64数据类型
  • 如何在加载期间将列作为datetime64对象加载
  • 如何找到特定日期的星期几
  • 如何根据星期几对数据帧进行排序
  • 如何在加载时将不同的列组合成一个datetime64对象
  • 数据框加载后如何将不同的列组合成一个datetime64对象
  • 如何将字符串转换为时间格式
  • 如何组合日期和时间列

用 Python 处理日期

原文:https://towardsdatascience.com/dealing-with-dates-in-python-1b4069a07a0f

使用 Python 实现自动化

这篇文章是关于可以对日期时间变量执行的操作。

Unsplash 上由埃斯特·扬森斯拍摄的照片

你在处理日期时间对象的时候会纠结吗?嗯,不得不承认我经常纠结。我总是需要做大量的搜索来为我的用例找到合适的方法。然后,我决定写这篇文章,作为给我亲爱的读者和我自己的文档。

背景

当我开发自动报告准备或 Excel 文件组合的工具时,我必须能够从文件名或文件夹中识别信息。通常,系统生成的文件或定期报告将根据固定模式命名,并存储在相同的文件夹中。

文件名通常是报告名称、报告日期或期间以及文件扩展名的组合,例如,名为“用电报告 2022 _ Q4 _ ww43 . xlsx”的定期报告和名为“工具申请报告 2022 10 21 . CSV”的每日报告。为了检索正确的文件,我们需要根据报告自动化工具运行的时间来计算文件名中的日期或周期。

因此,本文的结构如下:

  1. 解析并格式化日期时间(str ptimevsstrftime)
  2. 提取年/月/日信息
  3. 从日期开始计算世界周
  4. 从日期开始计算星期几
  5. 将日期时间对象转换为周期
  6. 计算数据时间间隔

我们开始吧!

解析日期时间意味着我们将包含日期的字符串对象转换成日期时间对象。例如,当我们用正则表达式或其他方法从报表“工具申请报告 20221021 . CSV”中获取日期时,日期“20221021”将是一个字符串变量。

在我们解析它之后,它将成为一个 DateTime 对象,并被写成 ISO 格式(YYYY-MM-DD),2022–10–21。然后,我们可以将其格式化为特定的格式,比如 2022 年 10 月 21 日。请注意,DateTime 对象在我们格式化后将变成一个 string 对象。

迷惑?放心吧!

从下面的例子中你会有一个更清晰的画面。

1.解析和格式化日期时间

解析日期时间

DateTime 库中有两种方法可以解析日期:

  • datetime.fromisoformat()
  • datetime.strptime()

让我们看看他们有什么不同!

import datetime as dtmy_date1 = "2022-07-01"
print(my_date1)
print(type(my_date1))my_date2 = "01-07-2022"
print(my_date2)
print(type(my_date2))my_date3 = "01July2022"
print(my_date3)
print(type(my_date3))

好的,我创建了 3 个不同的日期作为变量,现在它们是字符串对象。现在让我们将它们解析成 DateTime 对象。

my_date1a = dt.datetime.fromisoformat(my_date1)
print(type(my_date1a))my_date3a = dt.datetime.fromisoformat(my_date3)
print(type(my_date3a))

图片作者。

对于第一种方法datetime.fromisoformat(),就像方法名一样,它只能解析 ISO 格式的日期 YYYY-MM-DD,就像在名为my_date1的变量中一样。因此,当我们试图在my_date3中对其他日期时间格式使用该方法时,它将返回一个值错误my_date1a变量是我们通过解析my_date1变量得到的 DateTime 对象。

下面是一个用datetime.strptime()方法解析日期的例子。对于这种方法,我们必须根据日期格式指定格式代码来解析日期。您可以在 strftime()和 strptime()行为中了解更多关于格式代码的信息。

# my_date2 = "01-07-2022"
my_date2a = dt.datetime.strptime(my_date2, "%d-%m-%Y")
print(type(my_date2a))
print(my_date2a)**# Output**:
# <class 'datetime.datetime'>
# 2022-07-03 00:00:00

让我们看另一个不同日期格式的例子。

# my_date3 = "01July2022"
my_date3a = dt.datetime.strptime(my_date3, "%d%B%Y")
print(type(my_date3a))
print(my_date3a)**# Output:**
# <class 'datetime.datetime'> 
# 2022-07-01 00:00:00

使用strptime()方法解析日期是通过用各自的格式代码替换日、月和年。如上例所示,%d 表示日,%m 表示月数字,%B 表示月的全名,%Y 表示带有世纪的年。

如果你是新手,现在看起来可能很复杂,但是我向你保证,一旦你熟悉了格式代码,你就会变得很棒。记住,你可以随时参考 strftime()和 strptime()行为。😉

格式化日期时间

在我们将 string 对象解析成 DateTime 对象后,它将以 ISO 格式显示。如果您希望它是其他形式,我们必须使用datetime.strftime()方法来格式化日期。

# my_date3a: 2022-07-01 00:00:00
my_format_date = dt.datetime.strftime(my_date3a, "%B %d, %Y")
print(my_format_date)**# Output:**
# July 01, 2022

请注意,在我们格式化日期之后,它将变成一个字符串。

在我们将字符串解析成 DateTime 对象后,我们可以从中获得信息。

2.提取年/月/日信息

注意:下面的例子使用了第一节例子中的变量。

要获取年、月和日信息,我们只需使用下面 DateTime 对象中的相应属性。

  • datetime_object.year
  • datetime_object.month
  • datetime_object.day
# my_date3a: 2022-07-01 00:00:00**# Get Year Info** my_date3a.year**# Output:** # 2022**# Get Month Info** my_date3a.month**# Output:** # 7**# Get Day Info** my_date3a.day**# Output:** 
# 1

简单吧?😄

但是,我们无法从格式化的日期中提取上述信息。

# my_format_date = "July 01, 2022"
my_format_date.month

图片作者。

它将返回一个属性错误。这是因为当我们将日期格式化为其他格式时,它会再次变成 string 对象。我们只能从日期时间对象返回日期时间属性。

print(type(my_date3a))
print(type(my_format_date))Output:
<class 'datetime.datetime'> 
<class 'str'>

要知道 strftime()的乘积是 string 对象,而 strptime()的乘积是 DateTime 对象。

3.从日期开始计算世界周

注意:在本节中,我们将使用新的变量作为示例。

首先,我用不同的日期创建两个新的 string 对象,然后将它们解析成 DateTime 对象。

import datetime as dtmy_date_str_1 = "2022-07-01"
my_date_1 = dt.datetime.strptime(my_date_str_1, "%Y-%m-%d")
print(my_date_1)my_date_str_2 = "2022-07-03"
my_date_2 = dt.datetime.strptime(my_date_str_2, "%Y-%m-%d")
print(my_date_2)**# Output:**
# 2022-07-01 00:00:00 
# 2022-07-03 00:00:00

我们将使用isocalendar()方法从 DateTime 对象中获取世界周信息。这是因为 DateTime 对象没有世界周属性。

print(my_date_1.isocalendar())
print(my_date_2.isocalendar())**# Output:**
# datetime.IsoCalendarDate(year=2022, week=26, weekday=5) 
# datetime.IsoCalendarDate(year=2022, week=26, weekday=7)

isocalendar()方法将返回一个包含 ISO 年份、周数和工作日的元组。工作日将以数字形式返回。我们可以返回相应索引的值。

print("Date 1: 2022-07-01")
print("Year:", my_date_1.isocalendar()[0])
print("World Week Number: ", my_date_1.isocalendar()[1])
print("Weekday: ", my_date_1.isocalendar()[2])print("Date 2: 2022-07-03")
print("Year:", my_date_2.isocalendar()[0])
print("World Week Number: ", my_date_2.isocalendar()[1])
print("Weekday: ", my_date_2.isocalendar()[2])**# Output:**
# Date 1: 2022-07-01 
# Year: 2022 
# World Week Number:  26 
# Weekday:  5 
# Date 2: 2022-07-03 
# Year: 2022 
# World Week Number:  26 
# Weekday:  7

这就是我们如何得到星期数,以及年份和工作日。

4.从日期开始计算星期几

注意:以下示例使用第 3 节示例中的变量。

有多种方法可以从日期中返回星期几信息。其中一种方法是通过isocalendar(),如前一节所示。另一种方法是使用如下所示的weekday()方法。

print("Date 1: 2022-07-01")
print("Weekday: ", my_date_1.weekday())print("Date 2: 2022-07-03")
print("Weekday: ", my_date_2.weekday())**# Output:**
# Date 1: 2022-07-01 
# Weekday:  4 
# Date 2: 2022-07-03 
# Weekday:  6

嗯,2022 年 7 月 1 日是星期五。isocalendar()方法不遵循 python 索引规则。所以实际上,isocalendar()weekday()方法都在星期一开始计数,但是isocalendar()使用从 1 开始的索引,而 weekday()是从 0 开始的 python 函数。提到的两种方法以数字形式返回星期几。还有一种方法,你能猜出来吗🤔?

strftime()法。

我们可以通过用相应的格式代码格式化日期来获得工作日的名称。

date_weekday_1 = dt.datetime.strftime(my_date_1, "%a")
print(date_weekday_1)date_weekday_2 = dt.datetime.strftime(my_date_2, "%a")
print(date_weekday_2)**# Output:**
# Fri 
# Sun

我们可以通过使用如上例所示的“%a”格式代码来格式化星期几的缩写名称,或者通过使用如下例所示的“%A”格式代码来返回星期几的全名。

date_weekday_1 = dt.datetime.strftime(my_date_1, "%A")
print(date_weekday_1)date_weekday_2 = dt.datetime.strftime(my_date_2, "%A")
print(date_weekday_2)**# Output:** # Friday 
# Sunday

我们也可以用数字的形式返回工作日。

date_weekday_1 = dt.datetime.strftime(my_date_1, "%w")
print(date_weekday_1)date_weekday_2 = dt.datetime.strftime(my_date_2, "%w")
print(date_weekday_2)**# Output:**
# 5 
# 0

有趣的事实是,当你使用strftime()方法时,计数从周日开始,指数从 0 开始,正如文档所示。

为了更好地理解,我合并了下面的比较表,以显示从上面显示的日期开始以数字形式返回工作日的几种方法之间的差异。

将星期几作为起始日期的数字返回的方法之间的差异。图片作者。

5.将日期时间对象转换为周期

注意:在本节中,我们将使用新的变量作为示例。

我们已经学习了返回年、月、日、世界周数和工作日的方法和属性。如果大家还记得我在本文开头给出的报告名称的例子《用电报告 2022 _ Q4 _ ww43 . xlsx》,还有一条信息我们还没有得到,那就是季度。

为了从日期中获取季度,我们必须使用pandas库和 DateTime 库。

import pandas as pd
import datetime as dt
# pandas.Timestamp.to_perioddate_1 = '2022-10-21'
timestamp_1 = pd.Timestamp(date_1)

首先,我们将日期创建为字符串对象,然后将其转换为时间戳。之后,我们可以将时间戳转换成句点。

year_period = timestamp_1.to_period(*freq*='Y')
month_period = timestamp_1.to_period(*freq*='M')
week_period = timestamp_1.to_period(*freq*='W')
quarter_period = timestamp_1.to_period(*freq*='Q')print("Year: ", year_period)
print("Month: ", month_period)
print("Week: ", week_period)
print("Quarter: ", quarter_period)**# Output:**
# Year:  2022 
# Month:  2022-10 
# Week:  2022-10-17/2022-10-23 
# Quarter:  2022Q4

很简单吧?

根据 pandas 的官方文档,对于pandas.Timestamp.to_period()方法只有 4 种输出类型。在前面的部分中,我们分别得到年、月和周。然后,该方法返回日期的特定时间段。例如,2022Q4 指的是 2022 年的第四季度。

只检索“Q4”而不是“2022Q4”还需要一个步骤。quarter_period变量现在是一个周期对象。所以,我们需要将它转换成一个 string 对象,然后返回最后两个字符串以得到“Q4”。

print(str(quarter_period))[-2:]

除此之外,我们可以定义一个 python 函数来表示每个月的季度。当您的组织有自己的财政年度计算方法时,此方法也适用。例如,第一季度可能在 11 月、12 月和 1 月。

**# Output:**
# Q4

上面显示了当您的组织不遵循标准的季度定义时,如何返回季度。您可以根据组织的季度定义修改条件。

6.计算数据时间间隔

注意:在本节中,我们将使用新的变量作为示例。

日期时间间隔有两种计算方式:

  1. 计算两个日期之间的间隔
  2. 给日期加上/减去一个时间间隔

让我们一个一个来看!

在此之前,让我们为示例创建一些日期。

import datetime as dtmy_date1 = dt.datetime.fromisoformat("2022-07-01")
my_date2 = dt.datetime.fromisoformat("2022-07-05")

计算两个日期之间的间隔

计算两次约会的间隔真的很简单。我们只需要减去一个日期。

print(my_date2 - my_date1)**# Output:**
# datetime.timedelta(days=4)

输出将是一个 timedelta ,它指的是两个 DateTime 对象之间的差异。

给日期加上/减去一个时间间隔

另一个例子是给日期加上或减去一个时间间隔。

from datetime import timedeltaprint(my_date2 - timedelta(*days*=10))
print(my_date2 + timedelta(*days*=10))
print(my_date2 - timedelta(*seconds*=10))
print(my_date2 + timedelta(*seconds*=10))**# Output:**
# 2022-06-25 00:00:00 
# 2022-07-15 00:00:00
# 2022-07-04 23:59:50 
# 2022-07-05 00:00:10

我们将使用日期时间库中的 timedelta 类来执行该操作。这个类允许我们在日期上加/减天数、秒数或微秒数。

结论

总之,已经展示了将字符串对象解析为日期时间对象以及将日期时间对象格式化为特定格式的方法。然后,讨论了从日期中获取年、月、日、世界周以及工作日的方法。

给出了将日期时间对象转换成周期的方法,如一年、一年中的一个月以及一年中的一个季度。对于有自己的财政年度计算方法的组织,条件语句可用于返回正确的季度。

最后,解释 DateTime 间隔的两种计算类型,它计算两个日期之间的间隔,并给日期加上/减去一个时间间隔。

用我展示的所有方法和例子,你能用今天的日期(26/10/2022)重新创建下面的文件名吗?😎在评论里落下你的答案吧!

  • 《2022 年用电报告 _ Q4 _ ww43 . xlsx》
  • “工具征用报告 2022 10 21 . CSV”

在文章底部回答。自己先试一下再查答案!😉

我希望您喜欢阅读这篇文章,并希望它能帮助您更多地了解如何在 Python 中处理 DateTime 对象。谢谢!😊

保持联系

在 YouTube上订阅

边注

使用 Python 的报告自动化技巧中,我解释了一些关于报告自动化的技巧。看看吧!

参考

  1. Python 的 DateTime 模块文档
  2. 熊猫。Timestamp.to_period 文档
  3. strftime()和 strptime()行为

谢谢你,祝贺你读到最后😊!

照片由 Alexas_FotosUnsplash 上拍摄

回答:

希望你做对了!😊

用 ImageIO Python 包处理 DICOM

原文:https://towardsdatascience.com/dealing-with-dicom-using-imageio-python-package-117f1212ab82

医学图像== DICOM

乔纳森·博尔巴在 Unsplash 上的照片

在本文中,我们将使用 ImageIO Python 包来读取 DICOM 文件,提取元数据和属性,并使用 Ipywidgets 使用交互式滑块小部件绘制图像切片。

下面是最终代码的输出。[图片由作者提供]

背景信息

DICOM 是医学成像系统的支柱。如果你是生物医学工程师、医疗保健领域的 IT 专家,或者是医疗保健数据科学家/分析师,你可能使用过或者至少听说过 DICOM,因为它与医疗成像系统无处不在。

DICOM,即医学中的数字成像和通信,是一套标准,旨在实现多种形式的通信,如 CT、MRI、超声波等。这样,所有符合 DICOM 标准的医疗设备在通过网络发送信息时都可以使用同一种语言。

DICOM 文件通常包含信息,而不仅仅是像素数据。DICOM 标准提供了一组属性来标准化存储在 DICOM 文件中的数据。每个属性都有特定的数据类型(如字符串、整数、浮点等)。)用固定值表示。这些属性称为元数据。DICOM 元数据的示例:

  • 患者相关属性: 姓名、年龄、性别等。
  • 模态相关属性: 模态(CT、MRI、超声等。)、制造商、收购日期等。
  • 图像相关属性: 形状、采样、长宽比、像素数据等。

注意 我上面提到的图像相关属性对于理解非常重要,因为它们对于在医学图像上实现真实世界的计算非常有用。我们将严重依赖他们。你最好理解每一个代表什么。

有几十种属性可以表征 DICOM 文件。所以,你不能把它们都读完。您只需要关注在 DICOM 相关工作中可能会遇到的属性。为此,我强烈推荐这个由 Innolitics 构建的伟大的 DICOM 标准浏览器,你只需要搜索你想要了解的属性。

现在让我们进入正题…我们将使用 python 轻松浏览 DICOM 文件。我们将着重于使用(ImageIO)包来展示一些关于 DICOM 文件的基础知识。我们将讨论以下内容:

  • 使用 ImageIO 读取 DICOM 文件。
  • DICOM 属性,元数据。
  • 访问特定的 DICOM 属性。
  • 使用 Matplotlib 包的图像表示。
  • 堆叠并读取多个切片。
  • 了解像素间距、形状、切片厚度、纵横比和视野。
  • 使用 Ipywidgets 沿轴向、冠状面和矢状面构建交互式图像表示。

让我们编码

首先,让我们导入所需的包。我们将使用(ImageIO)处理 DICOM 文件,使用(NumPy)将像素数据作为 NumPy 数组读取,使用(matplotlib)将图像可视化。另外一个包(Ipywidgets)也用于构建一个交互式滑块,我们可以用它在多个图像切片之间滚动。

我用Google Colab用 Python 编码

import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import imageio

DICOM 数据集

互联网上有很多 DICOM 样本。你可以使用你自己的 DICOM 文件。在这篇博客中,我选择了一个病人的 99 片胸部 CT 扫描的数据集。你可以在 Kaggle 上找到它。我把数据集保存在我的驱动器上,这样我就可以很容易地通过 GoogleColab 访问。

读取 DICOM 文件

您可以轻松地读取 DICOM 文件,并使用(.imread)。

#Reading a DICOM Image
im = imageio.imread('/content/drive/MyDrive/Datasets/Kaggle/DICOM/dicom_lung/000009.dcm')

DICOM 属性

使用(。meta)输出包含 DICOM 文件的属性和元数据的字典。

#DICOM Metadata
im.meta
Dict([('TransferSyntaxUID', '1.2.840.10008.1.2.1'),
      ('SOPClassUID', '1.2.840.10008.5.1.4.1.1.2'),
      ('SOPInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.397047382026069586801778973091'),
      ('StudyDate', '20100227'),
      ('SeriesDate', '20100227'),
      ('AcquisitionDate', '20100227'),
      ('ContentDate', '20100227'),
      ('StudyTime', '161937.171'),
      ('SeriesTime', '162536.14 '),
      ('AcquisitionTime', '162208.162527 '),
      ('ContentTime', '162208.162527 '),
      ('Modality', 'CT'),
      ('Manufacturer', 'SIEMENS'),
      ('StudyDescription', 'CT CHEST W IV CONTRAST'),
      ('SeriesDescription', 'LUNG 3.0  B70f'),
      ('PatientName', 'C3N-00247'),
      ('PatientID', 'C3N-00247'),
      ('PatientBirthDate', ''),
      ('PatientSex', 'F '),
      ('PatientAge', '077Y'),
      ('StudyInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.258626612405225511766549337110'),
      ('SeriesInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.242193282649561899185427104083'),
      ('SeriesNumber', 2),
      ('AcquisitionNumber', 2),
      ('InstanceNumber', 1),
      ('ImagePositionPatient', (-143.2177734375, -287.2177734375, 6.0)),
      ('ImageOrientationPatient', (1.0, 0.0, 0.0, 0.0, 1.0, 0.0)),
      ('SamplesPerPixel', 1),
      ('Rows', 512),
      ('Columns', 512),
      ('PixelSpacing', (0.564453125, 0.564453125)),
      ('BitsAllocated', 16),
      ('BitsStored', 12),
      ('HighBit', 11),
      ('PixelRepresentation', 0),
      ('RescaleIntercept', -1024.0),
      ('RescaleSlope', 1.0),
      ('PixelData',
       b'Data converted to numpy array, raw data removed to preserve memory'),
      ('shape', (512, 512)),
      ('sampling', (0.564453125, 0.564453125))])

由于元数据存储为字典,您可以看到 DICOM 文件的关键字、属性名称:

# The Attributes of the DICOM File
im.meta.keys()
odict_keys(['TransferSyntaxUID', 'SOPClassUID', 'SOPInstanceUID',
            'StudyDate', 'SeriesDate', 'AcquisitionDate', 'ContentDate',
            'StudyTime', 'SeriesTime', 'AcquisitionTime', 'ContentTime',
            'Modality', 'Manufacturer', 'StudyDescription', 'SeriesDescription',
            'PatientName', 'PatientID', 'PatientBirthDate', 'PatientSex',
            'PatientAge', 'StudyInstanceUID', 'SeriesInstanceUID',
            'SeriesNumber', 'AcquisitionNumber', 'InstanceNumber',
            'ImagePositionPatient', 'ImageOrientationPatient',
            'SamplesPerPixel', 'Rows', 'Columns', 'PixelSpacing',
            'BitsAllocated', 'BitsStored', 'HighBit', 'PixelRepresentation',
            'RescaleIntercept', 'RescaleSlope', 'PixelData', 'shape',
            'sampling'])

注意:我们在这里看到的属性可能不是这个 DICOM 文件中包含的全部属性。这是因为 ImageIO 对 DICOM 图像的处理尽可能的轻。一个可选的阅读是检查这个链接以查看 ImageIO 支持的属性字典。

访问特定的 DICOM 属性

访问特定属性,例如“设备”,可以按如下方式完成:

#The Modality Attribute
im.meta['Modality']
'CT'

使用 Matplotlib 包的图像表示

通常,“像素数据”属性具有像素值。让我们像以前一样访问这个属性:

# Access the Pixel Data attribute
im.meta['PixelData']
b'Data converted to numpy array, raw data removed to preserve memory'

正如它所说,像素值存储为一个 NumPy 数组。这很有用,因为 NumPy 适合快速处理数组及其计算。现在让我们看看像素值:

#print the image Numpy-array
im
Array([[ -985,  -990,  -999, ..., -1017, -1008,  -971],
       [-1016,  -984,  -963, ..., -1000, -1009,  -999],
       [-1024, -1008,  -996, ...,  -979, -1021,  -987],
       ...,
       [ -920,  -942,  -944, ...,  -893,  -917,  -955],
       [ -871,  -879,  -905, ...,  -895,  -869,  -867],
       [ -876,  -855,  -873, ...,  -933,  -982,  -936]], dtype=int16)

我们只看到一串数字,其中每个数字代表 Hounsfield 单位(HU) 形式中的像素值。您可以使用 NumPy 数组索引对图像数组进行切片:

#Slicing the first 5 rows and first 5 columns
im[0:5, 0:5]
Array([[ -985,  -990,  -999,  -982, -1011],
       [-1016,  -984,  -963,  -978, -1005],
       [-1024, -1008,  -996,  -969,  -992],
       [-1006,  -984,  -997,  -963, -1002],
       [ -970,  -988, -1006,  -992,  -999]], dtype=int16)

了解了像素数据代表什么之后,我们来展示一下这些像素的图像。

#Show the image with gray color-map
plt.imshow(im, cmap='gray')
#Don't show tha axes
plt.axis('off')
#Add a title to the plot
plt.title('Axial Slice')
plt.show()

使用 Matplotlib 的图像表示[图片由作者提供]

堆叠并读取多个切片

DICOM 文件可以有多个帧,这些帧可能堆叠在像素数据属性中。你可以看到在 DICOM 文件中有视频或彩色图像,RGB 通道。此外,有时您会在一个文件夹中找到多个 DICOM 文件,其中每个 DICOM 文件包含同一患者的一个帧或切片。在这种情况下,我们必须自己堆叠这些 DICOM 文件。幸运的是,这很容易通过使用(.volread)来自 ImageIO。

#Stacking 99 slices
vol = imageio.volread('/content/drive/MyDrive/Datasets/Kaggle/DICOM/dicom_lung')
Reading DICOM (examining files): 99/99 files (100.0%)
  Found 1 correct series.
Reading DICOM (loading data): 99/99  (100.0%)

您可以使用数组索引来访问特定的切片:

#Access the first slice
vol[0,:,:]
Array([[ -985,  -990,  -999, ..., -1017, -1008,  -971],
       [-1016,  -984,  -963, ..., -1000, -1009,  -999],
       [-1024, -1008,  -996, ...,  -979, -1021,  -987],
       ...,
       [ -920,  -942,  -944, ...,  -893,  -917,  -955],
       [ -871,  -879,  -905, ...,  -895,  -869,  -867],
       [ -876,  -855,  -873, ...,  -933,  -982,  -936]], dtype=int16)

此外,单个特定切片的图像表示可以像前面一样进行:

#Show the first slice.
plt.imshow(vol[0,:,:], cmap='gray')
#Don't show the axis
plt.axis('off')
#Add a title
plt.title('Axial Slice')
plt.show()

这是 99 个堆栈图像的第一个切片。[图片由作者提供]

处理堆叠切片对于提取其他平面(矢状面和冠状面)非常有用。此外,放射科医生在诊断时滚动多个切片也很有帮助。但是为了正确地显示三个平面和滚动切片,我们工程师必须理解特定属性的含义。这些属性是采样、形状和长宽比。

人体解剖平面,横向是轴向平面。[图片来自维基共享资源

#Introduce the metadata of the stacked images.
vol.meta
Dict([('TransferSyntaxUID', '1.2.840.10008.1.2.1'),
      ('SOPClassUID', '1.2.840.10008.5.1.4.1.1.2'),
      ('SOPInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.397047382026069586801778973091'),
      ('StudyDate', '20100227'),
      ('SeriesDate', '20100227'),
      ('AcquisitionDate', '20100227'),
      ('ContentDate', '20100227'),
      ('StudyTime', '161937.171'),
      ('SeriesTime', '162536.14 '),
      ('AcquisitionTime', '162208.162527 '),
      ('ContentTime', '162208.162527 '),
      ('Modality', 'CT'),
      ('Manufacturer', 'SIEMENS'),
      ('StudyDescription', 'CT CHEST W IV CONTRAST'),
      ('SeriesDescription', 'LUNG 3.0  B70f'),
      ('PatientName', 'C3N-00247'),
      ('PatientID', 'C3N-00247'),
      ('PatientBirthDate', ''),
      ('PatientSex', 'F '),
      ('PatientAge', '077Y'),
      ('StudyInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.258626612405225511766549337110'),
      ('SeriesInstanceUID',
       '1.3.6.1.4.1.14519.5.2.1.7085.2626.242193282649561899185427104083'),
      ('SeriesNumber', 2),
      ('AcquisitionNumber', 2),
      ('InstanceNumber', 1),
      ('ImagePositionPatient', (-143.2177734375, -287.2177734375, 6.0)),
      ('ImageOrientationPatient', (1.0, 0.0, 0.0, 0.0, 1.0, 0.0)),
      ('SamplesPerPixel', 1),
      ('Rows', 512),
      ('Columns', 512),
      ('PixelSpacing', (0.564453125, 0.564453125)),
      ('BitsAllocated', 16),
      ('BitsStored', 12),
      ('HighBit', 11),
      ('PixelRepresentation', 0),
      ('RescaleIntercept', -1024.0),
      ('RescaleSlope', 1.0),
      ('PixelData', b'Deferred loading of pixel data'),
      ('shape', (99, 512, 512)),
      ('sampling', (3.0, 0.564453125, 0.564453125))])

请注意堆叠图像的“形状”和“采样”属性的差异。这是在使用(.volread)。

了解“形状”、“采样”和“像素比例”属性

  • 形状: 简单来说就是每个切片中的行数和列数。因为我们处理的是多个切片,所以会有一个第三维度,它等于堆叠在一起的切片的数量。在我们的示例中,堆叠图像的形状是 99 个切片、512 行和 512 列。
# The shape of the stacked images in each plane
# (Axial, Coronal, and Sagittal, respectively)
n0, n1, n2 = vol.shape
# Print the ouput
print("Number of Slices:\n\t", "Axial=", n0, "Slices\n\t",
                               "Coronal=", n1, "Slices\n\t",
                               "Sagittal=", n2, "Slices")
Number of Slices:
  Axial= 99 Slices
  Coronal= 512 Slices
  Sagittal= 512 Slices
  • 采样: 如果在 DICOM 标准下搜索“采样”,不会得到直接的答案。因为它是 ImageIO 内置的特殊变量。这是 SliceThickness 和 PixelSpacing 两个属性的采样组合。切片厚度是以毫米为单位的名义切片厚度。至于像素间距属性,它是患者的物理距离。它由一个数字对指定:第一个值是以 mm 为单位的行间距,即相邻行中心之间的间距,或垂直间距。第二个值是以 mm 为单位的列间距,即相邻列中心之间的间距,或水平间距。

PixelSapcing 属性。[图片由 DICOM 提供]

# The sampling of the stacked images in each plane
# (Axial, Coronal, and Sagittal, respectively)
d0, d1, d2 = vol.meta['sampling'] # in mm
# Print the output
print("Sampling:\n\t", "Axial=", d0, "mm\n\t",
                               "Coronal=", d1, "mm\n\t",
                               "Sagittal=", d2, "mm")
Sampling:
  Axial= 3.0 mm
  Coronal= 0.564453125 mm
  Sagittal= 0.564453125 mm
  • 像素长宽比: 图像中像素沿特定轴的垂直尺寸与水平尺寸的比值。请注意,“PixelAspectRatio”不在堆叠图像的元数据中。但这没关系,因为我们可以通过使用“采样”参数来计算每个轴的纵横比。下图表示 PixelAspectRatio 属性。

PixelAspectRatio 属性。[图片来自 DICOM

# The aspect ratio along the axial plane
axial_asp = d1/d2
# The aspect ratio along the sagittal plane
sagittal_asp = d0/d1
# The aspect ratio along the coronal plane
coronal_asp = d0/d2
# Print the output
print("Pixel Aspect Ratio:\n\t", "Axial=", axial_asp, "\n\t",
                               "Coronal=", coronal_asp, "\n\t",
                               "Sagittal=", sagittal_asp)
Pixel Aspect Ratio:
  Axial= 1.0 
  Coronal= 5.314878892733564 
  Sagittal= 5.314878892733564

视野

通过将“形状”参数乘以“采样”参数,即沿每个轴的物理空间(单位为 mm ),我们得到沿每个轴的视场。

print("Field of View:\n\t", "Axial=", n0*d0, "mm\n\t",
                            "Coronal=", n1*d1, "mm\n\t",
                            "Sagittal=", n2*d2, "mm")
Field of View:
  Axial= 297.0 mm
  Coronal= 289.0 mm
  Sagittal= 289.0 mm

使用 Ipywidgets 构建交互式图像表示

现在,在理解了采样和形状的含义之后,让我们用它们来做一个图像的正确表示,沿着每个轴有正确的纵横比。我们将使用 Ipywidgets 构建一个整数滑块,我们可以在特定平面(轴向、矢状或冠状)的不同切片之间滚动。

# Add a slider that starts with 0 and ends at the number of
# slices along the axial plane, n0=99.
@widgets.interact(axial_slice=(0,n0-1))
# Define the function that shows the images of the specified slice number.
# It starts with the 10th slice. And you can scroll over any slice
# using the slider.
def axial_slicer(axial_slice=50):
  fig, ax = plt.subplots(1, 1, figsize=(8, 8))
  # Show the image of the specified slice number in 'gray' color-map
  # and axial aspect ratio
  ax.imshow(vol[axial_slice,:,:], cmap='gray', aspect=axial_asp)
  # Don't show the axis
  ax.axis('off')

使用交互式小部件在多个轴向切片上滚动。[图片由作者提供]

现在让我们展示沿特定轴的三个切片的图像。我有意显示了轴,以便于理解沿每个轴的切片形状。

# Define a figure with 1 row and 3 columns of plots to show
# the images along the three planes
fig, ax = plt.subplots(1, 3, figsize=(8, 8))
# Axial Plane: show the 10th slice
ax[0].imshow(vol[10,:,:], cmap='gray', aspect= axial_asp)
#ax[0].axis('off')
ax[0].set_title('Axial')

# Coronal Plane: show the slice 100
ax[1].imshow(vol[:,100,:],cmap='gray', aspect= coronal_asp)
#ax[1].axis('off')
ax[1].set_title('Coronal')

# Sagittal Plane: show the slice 100
ax[2].imshow(vol[:,:,100], cmap='gray', aspect= sagittal_asp)
#ax[2].axis('off')
ax[2].set_title('Sagittal')
plt.show()

切片(10 个轴向,100 个冠状,100 个矢状)具有每个平面的正确纵横比。[图片由作者提供]

让我们尝试错误的方式…让我们忽略每个轴的纵横比来显示图像。

# Define a figure with 1 row and 3 columns of plots to show the
# images along the three planes
fig, ax = plt.subplots(1, 3, figsize=(8, 8))
# Axial Plane: show the 10th slice
ax[0].imshow(vol[10,:,:], cmap='gray')
#ax[0].axis('off')
ax[0].set_title('Axial')

# Coronal Plane: show the slice 100
ax[1].imshow(vol[:,100,:],cmap='gray')
#ax[1].axis('off')
ax[1].set_title('Coronal')

# Sagittal Plane: show the slice 100
ax[2].imshow(vol[:,:,100], cmap='gray')
#ax[2].axis('off')
ax[2].set_title('Sagittal')
plt.show()

切片(10 个轴向,100 个冠状,100 个矢状)忽略每个平面的纵横比。[图片由作者提供]

我们可以看到只有轴面不乱。这是因为参数“aspect”默认设置为 1,这是我们例子中轴平面的纵横比(D1/D2 = 0.564453125/0.564453125 = 1)。

我们的最终代码

让我们超越只有一个图像平面的滑块。我们将构建包含三个平面(分别为轴向、冠状和矢状)的三个图。我们可以使用每个平面的特定滑块来指定切片编号。不要忘记添加正确的长宽比,这样就不会像上面那样乱七八糟了。

# Add three sliders that start with 0 and ends at the number of slices
# along each plane.
# Axial:    n0=99   slice
# Corornal: n1=512  slice
# Sagittal: n2=512  slice
@widgets.interact(axial_slice=(0,n0-1), coronal_slice=(0,n1-1),\
                  sagittal_slice=(0,n2-1))
def slicer(axial_slice, coronal_slice, sagittal_slice=100):
  fig, ax = plt.subplots(1, 3, figsize=(12, 12))

  # Show the specfied slice on the axial plane with 'gray' color-map
  # and axial aspect ratio.
  ax[0].imshow(vol[axial_slice,:,:], cmap='gray', aspect= axial_asp)
  ax[0].axis('off')
  ax[0].set_title('Axial')

  # Show the specified slice on the coronal plane with 'gray' color-map
  # and coronal aspect ratio.
  ax[1].imshow(vol[:,coronal_slice,:],cmap='gray', aspect= coronal_asp)
  ax[1].axis('off')
  ax[1].set_title('Coronal')

  # Show the specified slice on the sagittal plane with 'gray' color-map
  # and sagittal aspect ratio.
  ax[2].imshow(vol[:,:,sagittal_slice], cmap='gray', aspect= sagittal_asp)
  ax[2].axis('off')
  ax[2].set_title('Sagittal')

使用交互式小工具在多个轴向、冠状和矢状切片上滚动。[图片由作者提供]

结论

  • 我们已经看到了如何使用 ImageIO 包来读取 DICOM 文件。以及使用的力量(。volread)来叠加图像,更方便的阅读。
  • 我们知道 DICOM 文件有元数据、一组属性以及像素数据。我们还介绍了一些与图像相关的特定属性的基础知识,如像素间距、切片厚度、形状和像素比例。
  • 我们已经指出了当沿着轴绘制图像时纵横比参数的重要性,以及如果没有正确指定纵横比,图像会变得如何混乱。
  • 我们使用 Ipywidgets 包的滑块构建了一个图形,显示了沿轴向、冠状和矢状平面的指定切片数。

感谢阅读……

建议

  • 更多关于 DICOM 的细节:参考之前的博客, 什么是 DICOM?
  • DICOM 元数据—大数据分析的有用资源:
    这篇文章概述了通过结合患者访问和 DICOM 信息来表示数据的新方法,医学成像元数据的高级使用,辐射剂量和图像分割的分析,以及用于丰富数据的特征工程的深度学习。
  • 生物医学图像分析在 Python 课程DataCamp 上。本课程是我第一次使用 Python 研究 DICOM 图像和医学图像处理。本博客中的信息严重依赖于本课程的第一章,这是唯一免费的一章。
  • Ipywidget文档 是学习构建交互式小部件的良好开端。

参考

[1] DataCamp,Python 课程中生物医学图像分析第一章,第一章,探索。

2 C. Rossant, IPython 交互式计算和可视化食谱,(2018),掌握 Jupyter 笔记本中的小部件

[3] ImageIO,GitHub,ImageIO-Plugin-DICOM,(2022),DICOM 属性, MINIDICT

[4] Innolitics,DICOM 标准浏览器,(2022),切片厚度属性

[5] Innolitics,DICOM 标准浏览器,(2022),像素间距属性

[6] Innolitics,DICOM 标准浏览器,(2022),纵横比属性

使用三种稳健线性回归模型处理异常值

原文:https://towardsdatascience.com/dealing-with-outliers-using-three-robust-linear-regression-models-544cfbd00767

照片由 里卡多·戈麦斯天使

通过使用 Huber、RANSAC 和 Theil-Sen 回归算法的实际例子

线性回归是最简单的机器学习模型之一。它通常不仅是学习数据科学的起点,也是构建快速简单的最小可行产品(MVP)的起点,这些产品随后将作为更复杂算法的基准。一般来说,线性回归拟合一条线(在二维中)或一个超平面(在三维和更多维中),它们最好地描述了特征和目标值之间的线性关系。

异常值是位于预期分布之外很远的值。它们导致特性的分布不太好。因此,该模型可能会偏向异常值,正如我们已经确定的,这些异常值远离观察值的中心。自然地,这导致线性回归发现更差和更有偏差的拟合,具有较差的预测性能。

重要的是要记住,异常值可以在要素和目标变量中找到,并且所有场景都可能使模型的性能恶化。

处理异常值有许多可能的方法:从观测值中去除异常值,处理异常值(例如,将极端观测值限制在一个合理的值),或者使用非常适合处理这些值的算法。这篇文章主要关注这些健壮的方法。

设置

我们使用相当标准的库:numpypandasscikit-learn。我们在这里工作的所有模型都是从scikit-learnlinear_model模块导入的。

数据

假设目标是显示不同的稳健算法如何处理异常值,第一步是创建一个定制的数据集,以清楚地显示行为的差异。为此,我们使用了scikit-learn中的可用功能。

我们首先创建一个包含 500 个观察值的数据集,其中有一个信息丰富的特征。只有一个特征和目标,我们绘制数据,以及模型的拟合。此外,我们指定噪声(应用于输出的标准偏差)并创建一个包含基础线性模型系数的列表,也就是说,如果线性回归模型适合生成的数据,系数将是多少。在这个例子中,系数的值是 64.6。我们为所有模型提取这些系数,然后用它们来比较它们与数据的拟合程度。

接下来,我们将前 25 个观察值(5%的观察值)替换为离群值,远远超出生成的大量观察值。请记住,先前存储的系数来自没有异常值的数据。包括他们会有所不同。

图 1

线性回归

我们从好的旧线性回归模型开始,它很可能受到异常值的影响。我们使用以下示例来拟合模型和数据:

lr = LinearRegression().fit(X, y)
coef_list.append(["linear_regression", lr.coef_[0]])

然后,我们准备一个对象用于绘制模型的拟合。plotline_X对象是一个 2D 数组,包含由生成的数据集指定的间隔内均匀分布的值。我们使用这个对象来获取模型的拟合值。它必须是一个 2D 数组,因为它是scikit-learn中模型的预期输入。然后,我们创建一个fit_df数据框架,在其中存储拟合值,通过将模型拟合到均匀分布的值来创建。

准备好数据框架后,我们绘制线性回归模型与异常值数据的拟合图。

图 2 显示了异常值对线性回归模型的重大影响。

图 2

使用线性回归获得基准模型。现在是时候转向稳健的回归算法了。

胡伯回归

休伯回归是稳健回归算法的一个例子,该算法对被识别为异常值的观察值分配较少的权重。为此,它在优化例程中使用了 Huber 损失。Huber 回归最小化以下损失函数:

其中σ表示标准偏差,X_i 表示特征集,y_i 是回归的目标变量,ω 是估计系数的向量,α是正则化参数。该公式还表明,根据 Huber 损失,异常值的处理方式不同于常规观测值:

Huber 损失通过考虑由 z 表示的残差来识别异常值。如果观察值被认为是规则的(因为残差的绝对值小于某个阈值𝜖),则我们应用平方损失函数。否则,观察值被认为是异常值,我们应用绝对损失。话虽如此,Huber 损失基本上是平方损失函数和绝对损失函数的组合。

好奇的读者可能会注意到,第一个方程类似于岭回归,也就是说,包括 L2 正则化。Huber 回归和岭回归的区别在于对异常值的处理。

通过分析两个流行的回归评估指标之间的差异,您可能会认识到这种损失函数方法:均方误差(MSE)和平均绝对误差(MAE)。与 Huber 损失的含义相似,建议在处理异常值时使用 MAE,因为它不会像平方损失那样严重地惩罚那些观察值。

与前一点相关的事实是,优化平方损失会导致均值附近的无偏估计量,而绝对差会导致中位数附近的无偏估计量。对于异常值,中位数比平均数更稳健,所以我们期望这能提供一个更少偏差的估计。

我们对𝜖使用默认值 1.35,这决定了回归对异常值的敏感度。Huber (2004)表明,当误差遵循σ = 1 的正态分布时,相对于 OLS 回归,𝜖 = 1.35 产生 95%的效率。对于您自己的用例,我建议使用网格搜索之类的方法来调优超参数alphaepsilon

然后,我们使用以下示例对数据进行 Huber 回归拟合:

图 3 显示了拟合模型的最佳拟合线。

图 3

RANSAC 回归

随机样本一致性(RANSAC)回归是一种非确定性算法,它试图将训练数据分为内点(可能会受到噪声的影响)和离群点。然后,它只使用内联器来估计最终的模型。

RANSAC 是一种迭代算法,其中迭代由以下步骤组成:

  1. 从初始数据集中选择一个随机子集。
  2. 使模型适合所选的随机子集。默认情况下,该模型是线性回归模型;但是,我们可以将其更改为其他回归模型。
  3. 使用估计模型计算初始数据集中所有数据点的残差。绝对残差小于或等于所选阈值的所有观测值都被视为内点,并创建所谓的共识集。默认情况下,阈值被定义为目标值的中值绝对偏差(MAD)。
  4. 如果足够多的点已经被分类为共识集的一部分,则拟合的模型被保存为最佳模型。如果当前估计的模型与当前最佳模型具有相同数量的内联器,则只有当它具有更好的分数时才被认为是更好的。

这些步骤迭代执行最大次数,或者直到满足特定的停止标准。这些标准可以使用三个专用的超参数来设置。正如我们前面提到的,最终的模型是使用所有的内层样本来估计的。

在下面的代码片段中,我们用 RANSAC 回归模型来拟合数据。

正如您所看到的,恢复系数的过程有点复杂,因为我们首先使用estimator_访问模型的最终估计器(使用所有识别的内联器训练的那个)。由于它是一个LinearRegression对象,我们继续像前面一样恢复系数。然后,我们绘制 RANSAC 回归拟合图(图 4)。

图 4

使用 RANSAC 回归,我们还可以检查模型认为是内部值和异常值的观察值。首先,我们检查模型总共识别出多少异常值,然后检查手动引入的异常值中有多少与模型的决策重叠。训练数据的前 25 个观察值都是已经引入的异常值。

运行该示例将打印以下摘要:

Total outliers: 51
Outliers you added yourself: 25 / 25

大约 10%的数据被确定为异常值,所有引入的观察值都被正确地归类为异常值。这样,我们可以快速地将内标值与外标值进行比较,以查看标记为外标值的其余 26 个观察值。

图 5 显示了距离原始数据的假设最佳拟合线最远的观察值被认为是异常值。

图 5

泰尔-森回归

scikit-learn可用的最后一种稳健回归算法是泰尔森回归。它是一种非参数回归方法,这意味着它对基础数据分布不做任何假设。简而言之,它包括在训练数据的子集上拟合多重回归模型,然后在最后一步聚合系数。

算法是这样工作的。首先,它计算从训练集 x 中的所有观察值创建的大小为 p (超参数n_subsamples)的子集的最小二乘解(斜率和截距),如果我们计算截距(它是可选的),则必须满足以下条件:p >= n_features + 1。线的最终斜率(可能还有截距)被定义为所有最小二乘解的(空间)中值。

该算法的一个可能的缺点是其计算复杂性,因为它可以认为最小二乘解的总数等于n_samples choose n_subsamples,其中n_samplesX 中的观测值的数量。考虑到这个数字会迅速膨胀,我们可以做一些事情:

  • 仅将该算法用于样本和特征数量方面的小问题。然而,由于显而易见的原因,这并不总是可行的。
  • 调整n_subsamples超参数。较低的值以较低的效率为代价导致对异常值的较高鲁棒性,而较高的值导致较低的鲁棒性和较高的效率。
  • 使用max_subpopulation超参数。如果n_samples choose n_subsamples的总值大于max_subpopulation,该算法只考虑给定最大尺寸的随机子群体。自然地,只使用所有可能组合的随机子集会导致算法失去一些数学特性。

此外,我们应该意识到,随着问题的维数增加,估计量的稳健性会迅速下降。为了了解这在实践中是如何实现的,我们使用下面的代码片段来估计 Theil-Sen 回归:

图 6

模型的比较

到目前为止,我们已经对包含异常值的数据拟合了三种稳健的回归算法,并且我们已经确定了各个最佳拟合线。现在是比较的时候了。

我们从图 7 的目视检查开始。为了不显示太多的线条,我们没有打印原始数据的拟合线。然而,给定大多数数据点的方向,很容易想象它看起来像什么。显然,RANSAC 和 Theil-Sen 回归得到了最准确的最佳拟合线。

图 7

更准确地说,我们看估计的系数。下表显示 RANSAC 回归的结果与原始数据最接近。观察 5%的异常值对常规线性回归拟合的影响有多大也很有趣。

你可能会问哪个稳健回归算法最好?通常情况下,答案是:“视情况而定。”以下是一些指导原则,可能有助于您针对具体问题找到合适的模型:

  • 一般来说,在高维设置中的稳健拟合是困难的。
  • 与 Theil-Sen 和 RANSAC 不同,Huber 回归没有试图完全过滤掉异常值。相反,它减少了他们对适合的影响。
  • Huber 回归应该比 RANSAC 和 Theil-Sen 更快,因为后者适合数据的较小子集。
  • Theil-Sen 和 RANSAC 不太可能像使用默认超参数的 Huber 回归那样稳健。
  • RANSAC 比 Theil-Sen 更快,并且随着样本数量的增加,它的伸缩性更好。
  • RANSAC 应该更好地处理 y 方向上的大异常值,这是最常见的情况。

考虑到上述所有信息,您还可以根据经验试验所有三种稳健的回归算法,看看哪一种最适合您的数据。

你可以在我的 GitHub repo 中找到这篇文章中使用的代码。一如既往,我们非常欢迎任何建设性的反馈。你可以在推特或评论中联系我。

喜欢这篇文章?成为一个媒介成员,通过无限制的阅读继续学习。如果你使用 这个链接 成为会员,你就支持我,不需要你额外付费。提前感谢,再见!

您可能还会对以下内容感兴趣:

https://medium.com/geekculture/investigating-the-effects-of-resampling-imbalanced-datasets-with-data-validation-techniques-f4ca3c8b2b94

参考

  • 菲施勒,硕士和博尔斯,R. C. (1981 年)。随机样本一致性:模型拟合范例及其在图像分析和自动制图中的应用。ACM 的通信24 (6),381–395。
  • Peter J. Huber,Elvezio M. Ronchetti,稳健统计伴随规模估计,第 172 页
  • 胡伯,P. J. (1992 年)。位置参数的稳健估计。在统计学的突破(第 492–518 页)。纽约州纽约市斯普林格

所有图片,除非特别注明,均为作者所有。

最初发表于 NVIDIA 的开发者博客2022 年 7 月 20 日

应对欧盟人工智能法案

原文:https://towardsdatascience.com/dealing-with-the-eu-artificial-intelligence-act-40e7059c8210

风险类别、要求、修订和最佳实践

图片提供: unsplash

在撰写本文时,关于欧盟人工智能(AI)法案的讨论很少,除了附件之外,很少有具体的来源简要介绍该法案的主要观点。

《人工智能法案》尚未得到认可,它是一个革命性的里程碑,将人工智能带入了主流技术。受到政府当局的监控必然会使人工智能更加强大、可靠和标准化。尽管建立符合法案标准的基础可能具有挑战性,但下一步除了大规模可伸缩性之外别无选择。

本文摘录了 AI 法案的主要亮点,并向读者简要介绍了 AI 的风险类别、高风险 AI 的禁令和要求,以及避免不遵守拟议法律的后果的最佳实践。这本书非常适合决策者或团队领导,他们可以在各自的组织中塑造和重定向人工智能基础设施和人工智能实践的过程。

什么是欧盟人工智能法案

《欧盟人工智能法案》是由一个主要监管机构提出的首创性提案。《大赦国际法》是在 2021 年 4 月提出的,尚未成熟成为通过的法案。

尽管这是第一个主要命题,但当中国和巴西分别于 2021 年 11 月和 9 月通过其人工智能法规时,它失去了作为第一个具体人工智能法律的地位。

立法者对人工智能的兴趣证明了人工智能不仅在慢慢超越日常技术,也证明了政府开始将其作为主流和可观察的技术使用的兴趣。

人工智能法案通过将人工智能分为三个明确的类别来建立其基础:

AI 法案规定的 AI 风险类别|图片由 censius.ai 提供

不可接受的风险

对最终用户的福祉或隐私构成严重威胁的应用程序,如某些政府使用的社交评分应用程序,将被禁止。

高风险

根据法案中的定义,高风险应用范围很广。我们将在本文后面讨论更多的细节。简而言之,高风险应用程序是那些干预平民活动的应用程序,如简历扫描工具。这些申请受到该法规定的严格法律和禁令的约束。

低风险或最小风险

低风险或最小风险应用程序是那些不会对最终用户的隐私、安全或健康造成任何威胁的应用程序。比如推荐风景滤镜的娱乐类 app。建议当局鼓励和促进行为准则,以促进对低风险人工智能系统自愿适用这些要求。

在本文中,我们将深入探讨构成人工智能法案症结的高风险应用程序。

为什么是现在?

人工智能在学术领域已经存在了几十年,在主流企业或公司应用程序中也存在了大约五年。那为什么政府当局现在想到要管制它呢?

经过多年的概念验证项目和跨行业测试,人工智能已经表明,它有足够的能力和利润成为日常应用中的一项深入吸收的技术。因此,我们现在在主要应用中看到人工智能,有时尽管没有意识到它的存在也在使用它。例如,当我们浏览视频或电子市场时,人工智能就在发挥作用。当我们在谷歌文档或任何流行的文档平台上简单地写一句话时,人工智能甚至会提示我们。它推荐书,推荐要买的东西,甚至推荐吃什么食物。

为什么现在提出 AI 法案?|图片由 censius.ai

随着人工智能逐渐开始帮助平民的决策活动,政府当局感到有必要规范人工智能的影响,以避免建筑商的错误判断或故意恶意造成的任何重大伤害。

近年来发现的人工智能的力量可以显著提高政府的能力,这也不足为奇。这是为什么人工智能需要被密切监控和监管的另一个重要原因,以便政府和执法机构也可以利用它,而不会扰乱基本人权的安宁。因此,人工智能法案成为理解和实现如何在保护最终用户权利的同时构建各种易受风险影响的人工智能应用的桥梁。

AI 法案的直接摘录,列出了为什么现在需要 AI 法案:

  • 确保投放到联盟市场和使用的人工智能系统是安全的,并尊重关于基本权利和联盟价值观的现有法律;
  • 确保法律确定性,以促进人工智能领域的投资和创新;
  • 加强对适用于人工智能系统的基本权利和安全要求的现有法律的治理和有效执行;
  • 促进合法、安全和值得信赖的人工智能应用的单一市场的发展,防止市场分裂。

为什么按照 AI 法案开始建造是必要的?

是的,人工智能法案仍然是一个命题,它肯定需要一段时间才能作为一个独立的法案通过。拖延既有政治原因,也有技术原因。

政治倾向于不同立法者之间的争论。然而,立法者意识到了这一延迟,并希望在 11 月举行最终投票以通过该法律,并弥补失去的时间,即使这意味着损害一项完全证明的法律。

延迟背后也有技术原因,因为人工智能法案相当雄心勃勃,旨在提供一个解决方案来监管各种各样的人工智能应用。目前版本的拟议法律有一些漏洞,一些机构和组织正在发送他们的建议,以便对最终版本进行明智的修改。

“……目标是建立一个有利于创新、经得起未来考验并能抵御干扰的法律框架”。

然而,尽管欧盟人工智能法案似乎还有几个月的时间,但中国和巴西等其他监管机构已经通过了有关人工智能的法律。因此,要参与全球市场,关键是要开始理解和执行管理机构建议的最低义务。

你在构建高风险的 AI 吗?

《人工智能法案》非常明确地阐述了高风险应用程序的定义。然而,即使这个定义也不是完全包罗万象的,因为人工智能是一个非常活跃的领域,每隔一天都会带来新的创新和技术。以下是法案中定义高风险应用的一些直接摘录。

如果满足以下条件,AI 被视为高风险:

(a)人工智能系统旨在用作产品的安全部件,或者本身就是一种产品,由附录 II 中所列的欧盟协调立法涵盖

  • 简单来说,这意味着任何影响系统安全组件的人工智能产品,或者本身就是一个完整的产品,都将被视为高风险
  • 产品或系统的安全组件 是指产品或系统的一个组件,该组件实现该产品或系统的安全功能,或其故障或失灵危及人身或财产的健康和安全
  • 作为快速参考,可以在附件文件的第 2 页找到欧盟协调立法清单

“(b)其安全组件为人工智能系统的产品,或人工智能系统本身作为一种产品,需要进行第三方符合性评估,以便根据附录 II 中所列的欧盟协调立法将该产品投放市场或投入使用。”

  • " 投放市场 是指第一个在欧盟市场上提供的人工智能系统;"
  • 投入使用是指将人工智能系统直接提供给用户首次使用或在欧盟市场上供自己使用以达到其预期目的
  • 符合性评估 是指验证高风险人工智能系统的要求是否得到满足的过程;

除了上面提到的高风险人工智能系统,在附件三(第 4 页)中提到的区域运行的人工智能系统也应被视为高风险。附件三中提到的领域概述如下:

附件三中提到的地区|图片由 censius.ai 提供

  • 自然人的生物识别和分类
  • 关键基础设施的安全组件
  • 教育和职业培训,包括准入、分配或评估
  • 就业、工人管理和自营职业
  • 获得和享受基本的私人服务和公共服务及福利
  • 法律的实施
  • 移民、庇护和边境控制管理
  • 司法和民主进程

如果人工智能系统对健康和安全造成危害的风险,或对基本权利造成不利影响的风险,即就其严重性和发生概率而言,等于或大于高风险人工智能系统造成的危害或不利影响的风险,则可通过增加高风险人工智能系统来更新上述标准。

高风险人工智能系统的要求

对于高风险的人工智能应用,有特定的要求,如果遵循这些要求,可以确保供应商和最终用户的最大安全。通过确保遵循这些要求,开发人员也可以确保规模,因为基础隐含地变得强大和可靠。

下面是对高风险系统的全部要求的一个总结或几个要点,如提议的 AI 法案所述。

高风险 AI 系统需求|图片由 censius.ai

风险管理系统

  • 应建立、实施、记录和维护与高风险人工智能系统相关的风险管理系统(RMS)。
  • RMS 应包含一个连续的迭代过程,要求定期系统更新。
  • 识别&分析已知和可预见的风险,估计&评估常规使用或误用可能出现的风险,基于上市后监控系统收集的数据分析评估其他可能出现的风险,采取适当的风险管理措施
  • 识别风险后,需要消除&控制措施和信息发布措施,并且必须是风险管理系统计划的一部分
  • "测试应针对初步定义的度量和概率阈值进行,这些度量和概率阈值适用于高风险人工智能系统的预期目的。"
  • 阅读直接摘录此处

数据和数据治理

  • 保持高质量的培训、测试和验证数据,使其没有抽样误差、偏差或数据缺口
  • 高风险系统的提供商可以处理特殊类别的个人数据,这些数据对于确保与高风险人工智能系统相关的偏差监控、检测和纠正是绝对必要的
  • 应适用适当的数据治理和管理实践
  • 点击阅读直接摘录

技术资料

  • 在系统投放市场或投入使用之前,必须编制详细的技术文件,并保持更新。
  • 技术文件必须证明高风险人工智能系统符合高风险人工智能系统的要求
  • 阅读直接摘录此处

记录保存

  • 系统必须自动记录事件(‘日志’)
  • 测井能力应符合公认的标准或通用规范。
  • 记录功能应使能够监控高风险人工智能系统的运行,以防止可能导致人工智能系统出现风险的情况发生
  • 阅读直接摘录此处

透明度和向用户提供信息

  • 系统应使用户能够解释系统的输出并正确使用
  • 该系统应附有以适当的数字格式或其他方式使用的说明,包括简明、完整、正确和清晰的信息,这些信息对用户来说是相关的、可访问的和可理解的。
  • 这些信息应该包括系统的预期目的、精确度、健壮性、网络安全以及任何其他可能需要的规格。
  • 阅读直接摘录此处

人为监督

  • 要求在人工智能系统使用期间,该系统能够受到自然人的有效监督
  • 当高风险人工智能系统按照其预期目的使用或在可合理预见的误用条件下使用时,监督者应致力于防止或最大限度地降低健康、安全或基本权利的风险
  • 监督者应充分了解高风险人工智能系统的能力和局限性,并能够适时监控其运行,以便尽早发现和解决异常、功能障碍和意外性能的迹象
  • 系统应该有一种方法或一个按钮,当监管人员认为功能可能有风险时,可以停止功能
  • 对于特定系统,最终决定不应仅取决于该系统,而必须首先由至少两个自然人核实
  • 阅读直接摘录此处

准确性、稳健性和网络安全

  • 系统需要保持准确性或标准度量的一致性,使其永远不会低于合适的阈值
  • 高风险人工智能系统的稳健性可以通过技术冗余解决方案来实现,这可能包括备份或故障安全计划,如关于不一致或故障的根本原因分析
  • 有必要警惕异常情况,例如当未经授权的第三方试图访问系统时
  • 阅读直接摘录此处

高风险人工智能的禁止事项

除了高风险系统必须遵循的要求,法案还列出了高风险系统严禁从事的几点:

  • 在一个人的意识之外使用潜意识技术,从物质上扭曲一种导致或可能导致身体或心理伤害的行为
  • 利用特定人群因其年龄、身体或精神残疾而存在的任何弱点,从实质上扭曲该人群中造成或可能造成身体或心理伤害的人的行为
  • 公共当局或代表公共当局使用人工智能系统对自然人在一定时期内的可信度进行评估或分类
  • 在公共场所使用“实时”远程生物识别系统进行执法,除非此类使用对于一系列列出的目标是绝对必要的

上述例子可能包括伤害少数群体的有偏见的应用程序,推动有影响力的媒体扭曲一个人的行为的营销应用程序,甚至是战略性地放置内容以影响大众行为的社交媒体算法,这些行为会影响重大决策,如下一位政治候选人。

预算造价

数据创新中心的一份报告声称,欧盟人工智能法案将在未来五年内花费€310 亿美元,并减少人工智能投资近 20%。Meeri Haataja 和 Joanna Bryson 等独立研究人员发表了他们自己的工作,表明成本可能会低得多,因为该法案主要覆盖了一小部分被认为是高风险的人工智能应用。

直接从法案中提取的其他估计成本:

开发或使用对公民的安全或基本权利构成高风险的人工智能应用程序的企业或公共机构必须遵守特定的要求和义务。

  • 符合这些要求将意味着到 2025 年,供应一个平均约 170000 欧元€的高风险人工智能系统的成本总计约为 6000 至 7000 欧元€。”
  • “对于人工智能用户来说,根据使用情况,在适当的情况下,还会有每年花费在确保人工监督上的时间成本。据估计,这些费用每年约为 5000 欧元至 8000 欧元。”
  • 对于高风险人工智能的供应商来说,验证成本可能会达到另外 3000 至 7500 欧元的€。”

修正和建议

如前所述,AI 法案是一个命题,各大组织和机构目前都在参与对最早可以通过的最终版本进行完善修正或建议。来自不同实体的一些重要建议分享如下:

  • 欧盟轮值主席国斯洛文尼亚:改善人工智能操纵的禁令
  • 未来生命研究所(Future of Life Institute):该法案应该确保人工智能提供商考虑他们的应用对个人和整个社会的影响
  • 剑桥:增加监管的灵活性,允许对高风险系统清单进行修改
  • 现在就进入欧洲:该提案不足以保护生物识别应用(如情感识别)的基本权利
  • 未来社会:人工智能法案应该确保政府对新的技术趋势保持敏感。

与 AI 法案保持一致的最佳实践

以下是一些最佳实践,以确保尽早始终如一地满足法案规定的要求:

测井

记录元数据、事件发生、设备、网络和每一个微小的细节对于确保可以跟踪数据传承以及可以重现或跟踪任何结果都是至关重要的。这也确保了解决方案的高度清晰,任何操作人员都可以轻松管理系统,而不会有太多的复杂性。

测试

测试不仅仅局限于模型和数据验证。测试涵盖整个 ML 堆栈,其中每个端点都需要进行交叉检查,以确保解决方案基础架构和模型输出的完全安全性以及最大功能和健康。

监控

监控是构建可信的人工智能应用的最重要的实践之一。应持续监控模型和数据的重要指标,如性能、质量、漂移和偏差,以便尽早标记和处理任何潜在问题。

可解释性

如果问题没有得到及时解决,那么监控和检测问题只会带来部分好处。强烈建议人工智能解释,因为它揭示了标记问题背后的根本原因,并使人工智能团队能够缩小雷达范围并修复有针对性的干扰。

资源规划

如果不提前规划资源,遵守该法案规定的所有限制和义务可能会非常昂贵。在提交资源计划之前,评估资源成本、构建与购买成本、流程成本和其他几项成本非常重要。

最后一个音符

尽管人工智能法案尚未通过,但立法者和各种组织正在积极努力建立一套经得起未来考验的法律,以最终规范和监管人工智能应用。同样的过程以前已经应用于各种其他技术,今天我们将它们作为主流技术使用,最不关心后果,因为有法律保护我们的基本权利和安全。也是时候对人工智能进行监管,以确保越来越多的最终用户免受潜在伤害。

开始确保你的人工智能应用程序符合法案中的规定是至关重要的。这样,一旦法律规定成为强制性的,人们就可以避免被切断主流市场的风险。《人工智能法》可能最终会像《GDPR》一样成为一项全球标准。

确保您的应用程序遵守法规的最快方法是遵循 MLOps 实践,这从本质上使整个 ML 堆栈更具弹性、可再现性和可信赖性。请关注此处,了解即将发布的关于 MLOps 实践和工具的详细内容。

顶级参考

取得联系!

如果你想了解更多关于 MLOps 和可靠 AI 框架的知识,请参考我们的资源(博客、电子书、白皮书)。有关可行的入门步骤,请联系我们以获得在您的组织中实施 MLOps 的指导计划。欢迎向我们索取一个关于增强人工智能解决方案可信度的监控和可解释性的演示

使用 Assert 语句在 Python 中调试很容易

原文:https://towardsdatascience.com/debugging-in-python-is-easy-with-with-assert-statements-ff333bfb3388

了解一种能够带来无痛苦故障排除体验的工具

马特·阿特兹在 Unsplash 上拍摄的照片

你是否厌倦了不断与讨厌的虫子打交道?即使使用流行的 ide 提供的调试功能,最小的错误也可能花费宝贵的时间来排除故障。

如果我告诉你,通过在运行程序时主动执行健全性检查,你可以更好地处理错误,那会怎么样?

幸运的是,Python 提供了 assert 语句,使用户能够做到这一点。

在这里,我们提供了 Python 的 assert 语句的概述,并解释了为什么它是 Python 脚本的资产。

断言语句

assert 语句遵循以下逻辑:

如果为假,则抛出 AssertionError

换句话说,assert 语句是一行程序,如果不满足特定的条件,它就会引发错误。

您可以将这些语句视为“安全卫士”,在允许任何后续操作之前对您的程序进行搜身。

句法

assert 语句遵循以下语法:

assert <expression>
assert <expression>, <error message>

举个例子,假设我们有一个变量,它必须是一个正数,这样后续的操作才能成功。我们可以使用 assert 语句在程序中进行健全性检查。

代码输出(由作者创建)

在这种情况下,由于变量为负,将引发 AssertionError。

用户还可以选择添加他们自己的自定义错误信息,这些信息将在引发 AssertionError 时显示。

代码输出(由作者创建)

这是一个有用的特性,尤其是当一个脚本将要包含多个 assert 语句时。该消息将使用户更深入地了解问题的原因。

利益

虽然 assert 语句不是程序中的强制包含项,但它非常有用。

仅仅这个简单的一行程序就可以改善您的编码体验。

用户可以使用 assert 语句主动执行健全性检查,以确保满足必要的条件,而不是采取被动的方法来调试程序。

如果条件不满足,程序将会产生一个错误,这将使用户能够立即发现问题并解决它。

现实生活中的例子

到目前为止,assert 语句的应用可能看起来很抽象,因此有必要提供一些例子来说明如何将它集成到数据科学项目中。

我参加了一门课程,这门课程将机器学习问题作为考试的一部分。要求是将模型的预测作为 CSV 文件提交,然后进行评分。

不幸的是,我的 CSV 文件的尺寸不正确,所以我第一次提交时得到了 0 分(幸运的是我被允许提交 5 次)。

我之所以犯这个错误,是因为我没有主动检查文件的尺寸是否符合要求,但是我能够通过在脚本的末尾添加一个 assert 语句来执行检查,以确保生成的 CSV 文件与提供的示例 CSV 文件具有相同的尺寸,从而纠正这个错误。

通过简单的一行程序,我能够避免意外提交维度错误的文件。这是我在机器学习黑客马拉松中经常用到的一行代码。

从那以后,我养成了使用 assert 语句在程序的某些部分执行健全性检查的习惯,以确保满足特定的条件。总的来说,这个习惯极大地改善了我的调试体验。

以下是可以使用 assert 语句执行的其他一些健全性检查示例:

  • 检查变量是否具有正确的数据类型
  • 检查一个数字是否在某个范围内
  • 检查输入矩阵是否具有神经网络的正确维数
  • 检查列表、集合或字典是否有特定的元素

常见错误

此时,有必要指出在执行健全性检查时会犯的两个常见错误。

1。使用非调试工具进行调试。

人们可能会犯使用 if 语句和 try/except 子句来执行健全性检查的错误。不幸的是,这些工具不是用来调试的。为此目的使用这样的工具是低效的,并且不可避免地会导致更长的代码行,这会妨碍可读性。

希望本文为使用 assert 语句来实现这一目的提供了有力的证据。

2。将断言语句用于非调试目的

断言语句是用来调试的,应该只有使用来调试。对于像错误处理这样的应用程序,最好依靠 try/except 子句。

结论

照片由 Unsplash 上的 Prateek Katyal 拍摄

如果我继续将 assert 语句类比为安全卫士,我会说,在 Python 脚本中包含 assert 语句就像在程序的任何给定点设置安全检查点,以确保所有活动部分都按预期运行。

这是一个一行程序,使用户能够主动执行健全性检查,从而省去他们被动排除错误的麻烦。

我祝你在数据科学的努力中好运!

调试神经网络

原文:https://towardsdatascience.com/debugging-neural-networks-abdc6273a3f1

传统的软件开发已经建立了测试软件的四个阶段:单元测试、集成测试、系统测试和验收测试。第一种猜测是,当建立机器学习模型时,没有什么变化,因为它仍然是一些软件代码。但这是短视的,因为机器学习模型在没有冻结时具有不确定性行为,其参数不再适应数据。

在训练或连续交付机器学习模型的情况下,可能会出现额外的误差源。这些问题可能在长时间的训练过程后被发现,也可能在最早检查结果时被发现——可能在一小时、一天、一周或一个月后,这取决于你预计训练何时结束。你能想象的最简单的错误是你的神经网络的组件(例如神经元)没有连接。这个错误的关键是,你只能在一个无效的验证错误中看到它。

在这篇文章中,我将解释一些调试神经网络的技术。当然,这些技术也可以用于在训练前和训练时测试模型。因此,很难区分设计模型中的错误和改进模型以收敛。

单元测试

在您自己编写层的级别,首先要做的是编写单元测试。这样做的目的是使实现不容易受到代码更改的影响。这里,我们将实际输出与预期输出进行比较。举个简单的例子,考虑 Tensorflow 中的密集层:

import tensorflow as tf 
def dense(inputs, weights):
       return tf.linalg.matvec(weights,inputs)

下面是这个函数的一个简单单元测试。

import numpy as np 
import pytest  
def test_dense():
      inputs = np.random.rand(5)
      weights = np.identity(5)
      assert np.linalg.norm(dense(inputs=inputs, weights=weights) - inputs) == 0

在本例中,选择了一个恒定权重矩阵,因为我们知道会发生什么。这个例子当然是从单元测试开始的一个小练习。我相信 Tensorflow 团队会测试他们的库。

检查一个小数据集

合理的测试方法是在小数据集上训练模型。这也可以写成一个简单的测试,例如可以在任何管道管理器上实现。在这里,少量的数据点就足够了。在训练的第一个时期之后,我们可以分别看到训练如何改进模型或者代码或数据集中是否有 bug。这些第一纪元对于节省大量时间很重要。

如果误差没有减少,学习率可能太高。损耗或梯度的错误符号也可能导致误差增大。如果误差振荡,少数样本中也可能存在错误的标记。另一方面,当模型不是过拟合时,学习率可能太小或者梯度消失。

继续训练有助于观察模型是否能够完美地拟合数据。如果损失不会收敛到零,模型将总是对数据进行欠拟合。请注意,对于这一步,重要的是只考虑描述模型准确性的损失—没有正则化项!当考虑正规化时,损失会增加。不要忘记:仅仅因为你的模型不符合要求,它仍然可能失败。

监控所有内部状态

训练神经网络时,很多事情都可能出错。这就是为什么建议跟踪所有内部状态。特别地,应该考虑任何权重、任何激活以及可能还有任何仿射变换的分布。Tensorboard 是一个很好的工具,可以在训练过程中直观地监控这些量级。

第一个检查是考虑网络是否在学习。一个好的指标是考虑权重更新的硬阈值——这可以是 10^{-3}.比如,香草渐变体面的时候

被认为是,比例

应该在 10^{-3}.附近该阈值仅允许影响很小但足以避免缓慢收敛的更新。调整学习速率是很重要的,因为在固定的学习速率下,这个比率偏离了 10^{-3},权重将不会学习到有用的特征。由于梯度下降法的理论与数据集的大小无关[07],因此可以在少量但有代表性的数据子集上探索学习率的良好策略。

然而,梯度也可能为零。这有几个原因。最明显的是激活饱和或死亡——这意味着仿射变换导致激活函数的一个区域(几乎)恒定。对于重新激活,有人说,当输出为零时,“激活死亡”。虽然几乎恒定的激活可能有希望回到梯度不几乎为零的区域,但是 ReLUs 没有希望,因为梯度为零。这就是所谓的消失渐变

考虑任何仿射变换和输出的直方图有助于解决卡住的问题。仿射变换的数据应该类似于正态分布的数据,并且具有大约 0.5 到 2 的标准偏差。这里,两个方面起着重要的作用:数据不应该接近激活函数的几乎恒定的区域,并且变换数据的幅度应该与权重相同。因此,命名顺序取决于数据、学习过程和数据。当意识到内部激活远离 0 时,批量归一化对于卷积层是一个好的解决方案,而层归一化对于递归层是一个好的解决方案。请注意,即使使用分段线性激活,也建议对数据进行标准化。
还要注意,Tensorflow 中有两个函数进行批量规格化:TF . nn . Batch _ normalization 只是应用规格化,而 TF . keras . layers . Batch normalization 是可训练层。在最后一个中,也有两种模式:用于训练和推理。而训练移动统计量的均值和方差时,训练但不使用,因为它将提供推理时间。

关于权重的分配也很重要。当后面的权重比网络第一层中的权重学习得慢时,我们得到巨大的梯度——所谓的爆炸梯度。在某些点上,梯度在数值上变得不规则,或者我们看到不规则的振荡,这是由于在局部最小值附近振荡。这些可以通过梯度限幅来解决,其中阈值用于限制最大绝对值。On 还可以通过向优化器添加 l2 惩罚项来惩罚权重(所谓的岭正则化)。大多数情况下,权重看起来呈正态分布,但没有证据表明这是良好性能的充分或必要条件。

可视化权重矩阵可以通过辛顿图【10】来完成。这是一个正方形的方桌,每个重量的大小用正方形的大小来表示。当然,也可以通过热图来可视化。

权重更新的不良行为的另一个原因可能是不良的初始化。最糟糕的做法是将所有权重初始化为零,因为一个层中的所有权重行为相同。众所周知,好的初始化器有各种各样的激活函数。然而,训练可能仍然需要很长时间,这就是为什么在[04]中建议使用受限的波尔兹曼机器或自动编码器进行预训练。[16]中的作者还强调,这种预训练给出了更好的泛化误差。

用于梯度近似的样本也可能导致局部最小值。在这种情况下,增加批量是一个合理的选择。

在考虑卷积层时,可视化第一个隐藏卷积层的滤波器也是有帮助的。它可以给网络正在做的事情一些启发。

检查你的梯度计算

当在梯度的实施中有疑问时,可以通过使用数值方法近似梯度并考虑相对误差来实施简单的检查。为了选择计算梯度的方法,我们考虑 f 在 x 点的泰勒级数:

我们使用大 O 符号。所以正向差分公式有一个 O(h)阶的误差项。还要考虑后向差异

不会带来任何更好的近似。然而,将两者结合起来会产生 O(h)的误差近似值:

例如,一个缺陷是 ReLU 作为函数 f,因为它有不连续的梯度。假设|x| < h and x <0 then

Hence, around an discontinue gradient, one should monitor the gradient check carefully. Moreover, for checking whether gradient is working correctly, it is enough to use only a few datapoints. Step size h should be also considered wisely since numerical issues may appear. In [03] a comparision of accuracy and step size is given.

比较初始损失和你计算的预期损失

通过计算您的预期损失,可以轻松检查数据集的不平衡或缺失的归一化。分类网络必须输出随机权重的
-log(1/NumberOfClasses)。否则数据集是不平衡的。当考虑一个不平衡的数据集时,最终层中的权重应该被初始化,使得网络为每个类输出
-log(1/NumberOfClasses)。

使用正则化会导致问题,因为例如 l2 惩罚项可能会造成大量数据丢失。这意味着你告诉网络惩罚权重,而不是逼近数据。因此,特定损失项的比例是不正确的。

观看学习过程

对于所有步骤,当代码是可再现的时是有帮助的。因此,随机种子需要固定。这里需要知道内置的 Python 随机数生成器和 Numpy 有不同的种子,Tensorflow 的种子也需要设置。当使用 GPU 进行训练时,还需要小心,因为训练也可能是随机的,因为 GPU 中的一些快速方法是随机的,但可以设置为较慢的确定性函数。

seed = 1 # Built-in Python 
random.seed(seed) # Derandomize hashes of strings, bytes and datetime objects os.environ['PYTHONHASHSEED']=str(seed) # Numpy 
np.random.seed(seed) # Tensorflow 
tf.random.set_seed(seed) 
os.environ['TF_DETERMINISTIC_OPS'] = '1' # determininistic convolution and max-pool 
tf.config.threading.set_inter_op_parallelism_threads(1) 

考虑分布给出了一个很好的全局概观,但是考虑网络内部沿着流动的几个样本也可以给出一个很好的直觉。几个样本的层的所有输出的可视化显示了一组样本的行为。在检查数据流的同时,还可以检查张量维数的正确性。

大多数培训都围绕优化器的超参数进行,当培训效果不佳时,分析这些超参数非常重要。这里列出了关键的超参数及其影响。

优化器:存在许多用于训练神经网络的选项。自动调整学习率的梯度下降解算器主要设计用于小梯度。由于 RMSProp、Adam 等中的指数平均,大梯度对另一侧的影响消失。
此外,已经观察到[13]用 Adam 进行训练,例如,在初始阶段非常好,但是由于梯度的非均匀缩放,在某些点上停滞不前。[13]中的作者建议转换到 SDG。这里必须采用学习率。

批量:该参数给出近似梯度的样本数量。当不使用整个数据集时,我们称之为批量梯度下降。当存在巨大的泛化差距时,这可能是批量过大的原因。在[01]中,作者观察到大批量导致急剧的最小值,即在小邻域中的点快速增加的网络的损失函数图上的最小值。另一方面,小批量似乎倾向于平坦的最小值。然而,当数据具有高方差时,小批量会降低学习速度。优化器对噪声也非常敏感。在[09]中,作者讨论了在训练期间增加批量大小。[08]中还强调,开始时更多的重量更新(即小批量)有助于探索损失情况。

请注意,对于小批量梯度下降,所有的例子都需要随机洗牌。该过程模拟了数据分布中的随机抽样,并改善了概括差距,尤其是当一组后续样本中存在偏差时。

学习率:该参数给出了梯度步长有多大。小的学习率会导致收敛缓慢和陷入局部极小值的风险。大的学习率有在最小值附近振荡的风险。然而,改变学习率可能有助于加快收敛。已经有一些技术来实现良好的学习率,该学习率在训练过程时改变,例如阶跃衰减、指数衰减和基于时间的衰减。

一般来说,一个持续的或不断增加的训练损失可以由高学习率引起。低学习率导致训练损失减少,但是这里第一个时期中训练损失的斜率是重要的:当训练开始时,网络是一个随机函数,它学习以适应数据:因此大部分学习在第一个时期中完成。如果训练损失的斜率较低,仍然可以提高学习率。

训练和验证准确性:训练时,建议对当前网络进行验证。两种精度之间的巨大差距表明存在严重的过拟合。

更新权重的特征值谱:特征值的广泛分布导致了玉米薄壳形的最小值【08】。

使用掉线错误

辍学是一种强有力的正规化方法,但会导致许多错误:

  • 辍学是培训中最常用的一种方法。评估网络时,必须关闭漏失层,否则它会像[05]中那样考虑模型的不确定性。
  • 使用 Dropout 时,应该非常小心地使用早期停止。由于训练中的随机行为,早期停止可能会过早停止,而不知道退出会导致损失的小幅增加。
  • 必须在批量标准化后使用退出。在[02]中,作者描述了当我们从训练转移到测试时,退出改变了特定单元的方差。批量标准化将保持这种统计方差,因为它是从整个训练过程中积累的。

检查您的数据

关于数据的第一件事也是最重要的一点是,您需要对输入数据进行标准化。在回归问题中,对输出进行归一化也可能是一个不错的选择。

除了查看网络的内部状态,还应该考虑数据本身。不平衡的数据可能会导致分类任务中大多数相关类的过度拟合。当一些地区的数据过多时,回归分析也是如此。下采样数据是一个很好的选择。回归问题也需要解决同样的问题。

手工检查数据也是非常重要的。不正确的标签和相同的样本导致训练过程出现问题。通常,误差最大的样本标记不佳。

不确定性的一个原因也是数据的质量。测量数据时总会有一些噪声,如果在某个邻域中只有一个数据点,网络会在这个点上过度拟合。用不同的噪声多次使用这个数据点将有助于获得更稳定的结果。另一方面,当一个邻域中有许多数据点时,对它们进行批量平均是一种减少过度拟合并提高训练性能的方法。

最后,可以通过不使用数据来检查数据:当训练在文献中多次使用的不同数据集上工作并且已知它是可解的时,您可以确定,您的网络能够工作并且您的数据集有问题。

总结

这篇文章给出了很多如何调试神经网络的想法。当所有的提示和技巧都不起作用时,你可能做了一个错误的超参数选择。选择不同数量的层和神经元,然后重试。如果你有进一步的想法或问题,请告诉我。

文献:
【01】凯斯卡尔等人。艾尔。、论深度学习的大批量训练:泛化差距与尖锐极小值(2017)、
【02】李等。艾尔。、通过方差移位理解脱落与批量归一化的不协调性(2018)、https://arxiv . org/pdf/1801.05134 . pdf
【03】维基、
【04】辛顿等人。艾尔。,用神经网络降低数据的维数,(Science 2006),http://www.cs.toronto.edu/~hinton/science.pdf
【05】Gal 等。艾尔。、作为贝叶斯近似的辍学:深度学习中的表征模型不确定性(2016)、https://arxiv.org/abs/1506.02142
【06】卡帕西,《训练神经网络的诀窍》(2019)、http://karpathy.github.io/2019/04/25/recipe/
【07】村田,《在线学习的统计研究》(1998)、在线学习与神经网络,剑桥大学出版社
【08】神经网络:交易的诀窍,2012、斯普林格
【09】莫勒 人工智能
【11】邵,神经网络调试检查表(2019),https://towards data science . com/check-for-debugging-neural-networks-d 8 B2 a 9434 f 21
【12】Nikishaev,神经网络如何调试。 (2018),https://medium . com/machine-learning-world/how-to-debug-neural-networks-manual-dc2a 200 F10 f 2
【13】Keskar 等人,通过从 Adam 切换到 SGD 来提高泛化性能(2017)
【14】Stanford Course cs 231,
【15】Hui,可视化深度网络模型与度量(第四部分)(2018),al,为什么无监督预训练有助于深度学习?(2010 年),《机器学习杂志》

使用本地模式快速调试 SageMaker 端点

原文:https://towardsdatascience.com/debugging-sagemaker-endpoints-quickly-with-local-mode-2975bd55f6f7

停止等待您的端点创建

图片来自本·怀特Unsplash

对于经常使用 SageMaker 推论的用户来说,一个常见的挫折是能够快速调试端点。通常使用 SageMaker 端点时,您最终会得到一个定制推理脚本,帮助您控制模型的预处理和后处理。

最初,当我第一次使用 SageMaker 时,每当我必须对推理脚本进行更改时,我都会重新部署端点。这很费时间,因为我会等 2-3 分钟,而不知道推理是否会成功。 SageMaker 本地模式幸运地通过将 SageMaker 容器下拉到您的本地环境来帮助我们简化这个过程。在本文中,我们将采用一个简单的 SKLearn 模型,并使用 SageMaker 本地模式部署它,以展示它对于调试您的端点是多么有用。请注意,SageMaker 本地模式也可以用于预处理和训练脚本,本文将只讨论具有该特性的模型服务/推理。

注意:对于刚接触 AWS 的人来说,如果你想继续学习,请确保在下面的 链接 中注册账户。本文假设对 AWS 和 SageMaker 相对满意。如果你想获得更多关于 SageMaker 入门的指导,请务必查看我的附加资源部分。

设置

在我们开始之前,我们有通常的 SageMaker 导入来准备我们的环境。

这里主要导入的是 SageMaker Python SDK ,这是支持本地模式的包。这是一个高级的 SDK,它是对 Boto3 Python SDK 的包装,以简化 SageMaker 中的常见流程和操作。要完全理解这两个 SDK 的区别,请参考下面的文章

接下来,我们可以创建我们的 Python Sklearn 模型,我们将在本地训练一个线性回归模型,并生成一个 model.joblib 文件,其中包含一个样本回归问题的模型工件/数据。

本地模型训练

我们为生成的模型数据提供的另一个脚本是推理脚本。这包含了推理处理器,它将控制端点内模型推理的输入和输出。

推理脚本

SageMaker 希望我们的 model.joblib 和推理脚本中的这两个部分在一个 model.tar.gz 中结合在一起。我们将这些包装在一起,并将其推到一个 S3 桶中,供我们的本地模型拾取。

将数据推送到存储桶

现在,我们已经具备了启用本地模式所需的所有要素。

SageMaker 本地模式服务

首先我们创建一个 SageMaker 本地会话,这实际上是告诉 SageMaker 我们正在本地模式下工作。

本地会话

验证本地会话(作者截图)

接下来,我们配置我们的 SKLearn SageMaker 估计器,使其启用本地模式。

启用 Sklearn 服务的本地模式

在这里,我们传入我们的推理脚本,然后传入带有打包模型数据的模型工件的路径。该路径可以是 S3 路径,也可以是本地目录结构中的文件路径。

让我们来看一个示例负载,在这种情况下,我们的模型需要一个 2D 数组,因此我们使用来自测试数据集的示例数据点创建了一个 numpy 数组。

样本有效载荷

接下来,我们可以用局部模式预测器进行样本推断。注意,第一次运行下面的单元时,需要花几分钟的时间,因为需要下载 SKLearn 的容器映像。请注意,我们还将实例类型指定为 local,而不是通常的 EC2 实例。

局部模型推理

接下来我们看到的是在端点创建期间生成 CloudWatch 日志时,我们通常必须等待才能看到的内容。我们看到我们的推理服务器启动并运行,我们还看到我们的样本预测。

推理服务器正在启动(作者截图)

我们还可以在推理脚本中看到我们的日志记录语句,然后还可以看到示例负载的输出。

推理日志(作者截图)

让我们快速地用一个错别字打破我们的推理脚本,看看本地模式是否反映了错误。

这里我将我的 predict_fn 函数改为“model.pred ”,而不是 SKLearn 模型所期望的“model.predict”。

打字错误/错误

我重新运行正在创建我的 SKLearn 估计器并使用我更新的脚本执行推理的单元,不久之后我看到了正确的错误反映。

错误(作者截图)

其他资源和结论

https://github.com/RamVegiraju/SageMaker-Local-Mode/tree/master

您可以在上面的链接中找到整个示例的代码。SageMaker 本地模式是一个非常有用的特性,可以快速调试您的模型训练和推理,正如本文所反映的那样。更多本地模式示例,请查看 AWS 样本库。关于 SageMaker 入门的更多资源,请点击这里查看我的系列文章。

如果你喜欢这篇文章,请在LinkedIn上与我联系,并订阅我的媒体 简讯 。如果你是新来的中号,用我的 会员推荐 报名吧。

调试 Vaporexpress Profile 浓缩咖啡照片

原文:https://towardsdatascience.com/debugging-vaporexpress-profile-espresso-shots-bfe050dcca0a

咖啡数据科学

重命名蒸汽预注入配置文件

几个月前,我能够更准确地指出为什么杠杆式咖啡机(Kim Express)优于像样的浓缩咖啡机,即蒸汽预浸泡。我开始更多地探索这个变量,蒸汽预注入打破了我们通常与浓缩咖啡联系在一起的一些东西,比如压力。我的大部分镜头压力都低于 2 巴,但提取效果很好。所以我想突破界限,进一步发展自己的形象。

经过 40 次迭代,我重新命名了这个概要文件。Vaporexpress 用几个词来描述这个轮廓。Vapore 在意大利语中是蒸汽的意思。这个侧写是基于蒸汽预注入,这是我用金快车发现的。第一个变体将会跑得像涡轮子弹一样快,伴随着普通子弹的研磨,因此在 express 上有额外的意义。目前的化身已经稍微慢了下来。

我查看了一些变量,以便更好地理解概要文件的走向,因为概要文件是新的。没有关于在哪里或如何改进它的其他信息,也没有关于如何解决它的信息。我使用了以下指标:

  1. 用折射仪测量总溶解固体量(TDS),这个数字结合一杯咖啡的输出重量和输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。 【强度半径(IR) 是将 TDS vs EY 控制图转换为极坐标时的半径。**
  2. 咖啡渣 TDS (gTDS) 的测量方法是将咖啡渣和一些水放在折光仪上,测量咖啡中剩余的可溶物的量。在浓缩咖啡过程中,较高的萃取率导致较低的 gTDS 读数。

数据

这是一个很好的样本拍摄。流动到一边,在另一个讨论中,我把它引到了淋浴屏幕。

所有图片由作者提供

在观察这个冰球时,很难看到任何黑点来理解冰球内部的水流。

****

所以我看了地面 TDS (gTDS)给出更多的答案。

在观察这个镜头时,我从冰球的两边和中心各取了一个样本。我在顶部、中部和底部切割每个样品。

右侧有明显数量的未提取的可溶物,但是圆盘的顶部大部分被提取。我在这里用几种方式绘制了它:

****

我需要更好地了解如何解决这个问题,但我也想更好地了解蒸汽是如何影响咖啡的,因为这对温度有很大的影响。

搬运温度

前两步后,我将温度从 105 摄氏度降至 98 摄氏度,但在混合过程中温度仍在上升。在该图中,所有温度值都比所示值高 8C(由于校准偏差)。我怀疑随着新的、更冷的水的加入,水向下流动,但是蒸汽向上冒泡。我不确定还能怎样解释温度的上升。

冰球内部更奇怪。通常情况下,顶部比中部更突出,中部比底部更突出,但在我的一个镜头中,从中心和侧面测量的 GTD 显示增加。这和上面的镜头不一样。

只有蒸汽

所以我只看了蒸汽预注入。我在最热的水温下用 0.2 毫升/秒的流量做到了这一点。这就变成了蒸汽,但是这种蒸汽状态会持续多久还不知道。我从 10 秒、20 秒和 30 秒开始。水没有完全通过,当水通过冰球大约一半的时候,主要的通道就开始了。

在观察中心和侧核时,有趣的是看到侧核总是在顶部提取,并且大部分在中间。中心似乎有很多麻烦。这部分是由于淋浴设计和篮子形状倾向于侧沟。我甚至尝试以 30 秒的速度旋转冰球,试图抵消左右两侧水流的轻微不均匀,但这很难完成,通道似乎已经完成了。

****

就实际拍摄的 TDS/EY/IR 而言,我没有得到一个明确的指示去哪里与配置文件。

我不小心弄明白了是怎么回事。

视频证据

我决定在我像样的咖啡机下面黑掉我的 Kompresso。我想找到得到答案的方法。

第一个视频中的冰球太深,所以水流不畅:

**https://youtube.com/shorts/_qtOc6U5pSA

然而,我在第二个视频中使用了纸巾,效果好多了。我能够看到蒸汽预注入很快变成低压预注入。

https://youtube.com/shorts/c_Itz7MeahY

由于这个视频和这些调查,我改变了我的个人资料,做蒸汽预输注 2 秒钟,然后缓慢上升。

我喜欢这种形象,但这是一个艰难的过程。我知道我会有所成就,但每次我发现失败并深入挖掘,我都会学到更多。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我的书

我的链接

浓缩咖啡系列文章

工作和学校故事集**

揭穿我对交通数据的假设

原文:https://towardsdatascience.com/debunking-my-assumptions-on-traffic-data-328f6a38a772

从数据探索到模型训练,如何充分利用交通数据集。

美国宇航局在 Unsplash 拍摄的照片

前阵子,作为黑客马拉松的一部分,我对 美国交通 2015 数据 做了一个分析。交通数据集由美国交通部编制,可在谷歌大查询上获得

黑客马拉松组织者没有给出任何问题陈述,除了从这个数据集中找出 5 个最明显的模式。我照做了——事情是这样的。

接近

我处理交通数据集的经验有限,但我确实有大量关于交通量和交通状况的数据假设。从这些假设和我自己的探索性数据分析中,我形成了假设,并尽力去证明!

1.美国节假日和纪念日对日交通量的影响

当浏览美国交通数据时,我首先想到的是许多好莱坞电影,这些电影展示了假期期间家庭在长时间的交通堵塞中穿行的场景。因此,我决定测试一下我的假设,即日流量的激增主要是由假日季节造成的。

我导入了 2015 年美国假日日期,并创建了一个 Tableau 可视化,每个数据点表示当天观察到的交通量。

作者图片

画出平均线后,我观察到,从“Y”(橙色)点的分布来看,假日日期对交通量的影响并不显著。

我决定扩大突出显示点的数量,以包括节假日前后的+/- 1 天(例如,圣诞节—12 月 24 日和 12 月 26 日)。尤其是因为游客们很有可能在假期的前一天或后一天走遍全国。

作者图片

不幸的是,即使突出显示的点扩大了,我仍然没有观察到假日日期和交通流量之间的任何模式。事实上,与假日日期(51.47%)相比,非假日日期高于平均交通量(57.77%)的百分比更高。这进一步证明了我最初的假设是错误的。

2.时刻与日交通量的相关性

对于我的第二个任务,我试图在找出在整个 24 小时周期中哪个时间段对日交通量影响最大。

我是通过一天中的时间流量与每日总流量之间的关联热图来做到这一点的。

plt.figure(figsize=(20,10))
c= df2.corr()
sns.heatmap(c,cmap='BrBG',annot=True)
c

作者图片

相关值

作者图片

在对这些值进行排序后,我观察到下班后的高峰时间与早上上班前的交通相比,对整体每日交通有更大的影响,因为在最高的 5 个相关值中,4/5 是 1500-1900 的时段。

作者图片

然后,该表通常遵循上午高峰时间,随后是下午时段。我还观察到负相关值是从 0000 到 0300 没有流量模式的奇数周期。

3.交通站的几何位置与交通事故和死亡人数的关系

在前两次分析后,我不禁想知道交通量是否代表了整个美国,因为肯定会有农村地区的数据收集可能是一个问题。收集交通数据有多种原因,如经济和环境原因。然而,在我看来,最大的原因必须是限制交通事故和这些事故造成的死亡。

因此,我的假设是,美国政府会在交通事故风险更大的州设立额外的交通站。

为了测试我的假设,我利用了 OpenStreetMap 和来自国家公路交通安全管理局(NHTSA)2015 交通事故数据集 的额外数据来源。

作者图片

在 OpenStreetMap 上绘制了交通站点后,我注意到有几个热点,并希望使用 KMeans clustering 将地图分成几个聚类,以便进一步分析。

作者图片

使用肘方法发现了 7 个簇。此外,我还决定绘制一个热图,以更好地显示交通站的密度。

作者图片

“热区”可以通过黄色和浅绿色色调从密度图中识别出来。有了这些信息,就可以将这些“热点”区域的状态与死亡率进行比较了。

交通事故最多的 10 个州(2015 年)

作者图片

事故死亡率最高的 10 个州(2015 年)

作者图片

在分析了每个州的事故和死亡趋势后,我观察到,事故发生率高的州很可能在道路上安装了交通站。这证明了我的假设是正确的,州政府很可能会在事故多发路段设置更多的加油站。

4 & 5.加州交通量预测

在之前的分析中,我注意到加利福尼亚州安装的交通站数量最多,与第二多的州佐治亚州相比有显著差异。

因此,在我最后的分析中,我选择关注加利福尼亚州,并部署了几个机器学习模型来预测交通量。

模型结果

作者图片

在尝试了不同的 ML 模型之后,线性回归模型仍然是产生最低 RMSE 结果的模型。在最终的测试数据集上部署它之后,我能够将基准 RMSE 降低大约。47875.341 到最终的 RMSE 25285.543,提高了近 50%。

特征重要性分数

作者图片

我观察到最高的三个分数属于与车道数相关的特征。这可能是因为高速公路等车道数较多的道路能够容纳不同等级、重量和容量的大量车辆。因此提高了交通流量。反之亦然,对于车道数少的道路。

作者图片

从下面的分数中,我们可以观察到出行交通的方向流对交通量的影响最小。因此,我相信在这个预测模型中更重要的特征是基于道路的分类而不是交通流量。

未来作品

在数据科学项目中,数据建模很少有尽头。即使模型已经被批准部署,也总是有机会继续完善模型,要么使用更好的机器学习工具,要么使用新的数据源。

在这个项目中,我相信如果黑客马拉松给出的数据包括天气条件、光照条件、道路交叉口类型和道路施工等特征,最终的模型可以进一步改进。

在这个项目的过程中,我偶然发现了 NHTSA的死亡分析报告系统 的一个综合数据集,记录了车辆碰撞事件。我觉得这个数据集将是一个很好的方式来补充我上面所做的分析,我很高兴能够继续揭示我脑海中关于交通数据的许多假设。

这个项目的代码可以在我的 git repo 这里 找到。

快乐编码!

12 月版:2022 亮点

原文:https://towardsdatascience.com/december-edition-2022-highlights-d9d049dcc318

月刊

随着这一年接近尾声,我们庆祝过去一年中最令人难忘的文章

照片由约安-马克·库兹涅佐夫Unsplash 上拍摄

去年 12 月,当我们展望 2022 年时,我们希望我们整个社区在这一年充满学习、发现和平静的时光。世界并没有完全实现最后一项,但我们可以肯定地说,我们享受了大量的学习和发现——这都归功于我们作者的才华、技能和慷慨。

我们不太相信排名和最佳榜单;当到了反思我们过去一年出版的所有不可思议的作品的时候,我们选择了关注那些伴随我们的故事和声音。以下是我们每个人——TDS 团队成员和志愿编辑助理——决定与您分享的精彩文章的个人精选。我们希望你喜欢你的阅读。

这也是一个完美的时刻来感谢我们整个社区阅读、分享和参与我们发布的作品,并感谢通过您的媒体会员身份来支持它。这对我们来说意义重大。

为又一年的学习、好奇和灵感干杯。

TDS 编辑器

凯瑟琳·普雷里,高级编辑

我喜欢抓住我想象力的帖子。这些特殊的帖子——全部由新的 TDS 作者撰写——正是这样做的。

弗雷泽·金的这篇与地球科学相关的文章是我今年最喜欢的。在这里,深度卷积神经网络模型 DeepPrecip 通过近地面雷达估计降雨量,专注于降雪建模。这是对机器学习如何为极地和其他偏远地区的气候变化研究提供急需数据的有趣观察。而他们标题的问题挥之不去:神经网络会梦到落雪吗?

Francesco Bellelli 的一个灵感来自自然的帖子关注的是 T2 的沃罗妮图和它们的应用,这是另一个受欢迎的帖子。我想我再也不会以同样的方式看待图案——或者蜻蜓翅膀了。

我立刻对听图像的想法产生了兴趣,这也是这篇关于 Pavle Marinkovic 的发音的文章的重点。它让我伸手去拿我最喜欢的照片来试一试。

编辑凯特琳·金迪格

我们以五年级学生伊莎贝拉的精彩帖子开始了 2022 年,她(通过她的父亲 Rod Fuentes )写了一份关于她的科学展项目的报告,该项目旨在利用计算机视觉优化垃圾路线,以减少她所在城市的垃圾。最终产品是一份热图,将提交给市议会实施。我特别喜欢 Isabella 将她对我们星球的爱和编码结合起来改善她的空间的方式,我特别喜欢阅读我们年轻作者的帖子。

我一直很欣赏那些触及数据科学更道德方面的帖子,而 Aisulu Omar 在阐明数据本身、工作场所以及两者如何联系的重要性方面做得非常出色。解决数据科学中的系统性偏见非常重要,这份全面的报告在分解人们需要知道的东西方面非常有效。

同样关于数据表示的主题是 Monica P. 关于机器学习的成功和失败的帖子,特别是计算机视觉,以及拥有大型和多样化数据集的必要性以便解决每天使用技术的人的肤色。莫妮卡给读者留下了我至今仍在思考的问题:除了增加训练数据,我们如何才能确保我们生活中使用的算法是合乎道德和负责任的?即使这项技术是可行的,我们还应该利用它吗?使用或不使用它的含义是什么?

卢多维克·贝尼斯坦特,编辑

我喜欢阅读别人的经历,并发现他们一路上学到的东西。我发现这类文章很独特;它们通常是作者后退一步,反思他们的道路和项目的好机会。

在本文中,塞缪尔·弗莱德探讨了为什么理论上看起来不错的模型一旦投入生产就不一定表现良好。致力于为新闻分类建立机器学习模型, Ria Cheruvu 写了关于复合人工智能系统的文章。最后,分享了她在两年时间里撰写的关于 Medium 和 TDS 的技术文章的见解。

Sara A. Metwalli ,志愿编辑助理

教育是建设美好未来的一个重要方面。让教育更上一层楼的方法之一是利用我们今天拥有的技术。这篇文章由 Sanjay AdhikesavenAbyan DasMonish Muralicharan 合著,以一种极好的方式解释了人工智能如何被用来改变我们今天对教育的看法,以及它如何在未来带来更好、更容易获得的学习方法。

志愿者编辑助理卡洛斯·穆根

我真的很喜欢玛格丽特·米歇尔 TDS 播客那一集,由杰瑞米·哈里斯主持,在那里她介绍了一个伦理人工智能的新概念,“分形公平”:人工智能歧视/不平等在人口和亚人口的每一级分辨率中都不断浮出水面。这为我们的研究提供了一个新的视角,关于编码分类保护属性的公平含义(即将出现在 TDS 中!).

本·胡伯尔曼,主编

我们今年涵盖了许多重要的主题和创新,但我想不出有哪一个像生成式人工智能一样吸引了如此多的关注,激发了如此多的兴奋、关注和辩论。

我选择强调的帖子是那些探索这项技术内在极性并揭示它如何挑战我们的创造力和原创性概念的帖子。 Karen Asmar 向我介绍了一个我知之甚少的世界——建筑软件工具——并研究了人工智能如何能够为设计过程注入新的可能性。作为一个终生的歌剧迷,我发现尼科·韦斯特贝克关于合作一部人工智能生成的歌剧的描述绝对令人着迷。

随着新的人工智能图像和文本生成器大张旗鼓地到来,我开始怀疑——并担心——用于训练这些庞大模型的数据。艺术家、作家和其他人的作品被这些数据集囫囵吞下,而且经常是未经许可的,那该怎么办?Anna Rogers 对人工智能生成的文本中的归因问题进行了尖锐而具体的分析,并解释了为什么当人类从其他人的话语中找到灵感时,影响力的概念在这里并不适用。 Javier Ideami 专注于围绕视觉人工制品的问题,并分享了只能被描述为的热情宣言,为人类创造者(以及他们可能偶尔使用的人工智能工具)创造一个道德和可持续的未来。

除了崇高的人工智能问题,我总是对关注我们日常生活中一些怪异或古怪方面的文章情有独钟。作为一个额外的选择,我也衷心推荐 LeAnne Chan 关于博弈论的有趣帖子,以及它可能如何塑造我们选择的超市队列

当我们庆祝 2022 年的伟大文章时,让我们最后看一下过去一个月的亮点。

原始特征

探索我们最新的问答和阅读推荐。

热门帖子

如果你错过了它们,这里有一些 11 月份 TDS 上阅读量最大的帖子。

我们很高兴看到一群新的作者在 11 月成为我们社区的一员。请和我们一起欢迎努里苏珊·黄安娜·阿拉克里扬德米特罗·卡拉巴什亚历克斯·利特维诺夫保罗·尤斯丁金海峰阿龙·科恩沙恩·默里克里斯·加西亚彼得·沃德【T21 罗恩·塞林斯基米恰日·库克罗夫斯基雷夫·布雷纳博士阿尤布·布里基马蒂亚斯·葛雷伯阿伦·泰瓦帕兰法扎德·马赫穆迪诺巴纳曼·阿格拉瓦尔安娜·罗杰斯, 如果你也想和我们一起发表你的作品,我们很乐意收到你的来信。

2023 年见!

体面的浓缩咖啡 DE1Pro vs 金快递:第 1 轮

原文:https://towardsdatascience.com/decent-espresso-de1pro-vs-kim-express-round-1-54dffdfd2892

咖啡数据科学

金是卫冕冠军

我在一月份买了一杯不错的浓缩咖啡,并尽我所能做了最好的简介。我买的是 DE1Pro 型号。我想是时候和我心爱的金快线做一次面对面的比较了。

金氏家族不只是赢了。金被摧毁了。这让我怀疑我对浓缩咖啡了解多少?

我们可以改天再来讨论这个问题,但是今天,让我们把注意力集中在这次表演有多么不同,以及为什么会这样的一些初步想法。

所有图片由作者提供

Kim Express 是 20 世纪 60 年代到 80 年代生产的手动挡,后来公司倒闭了。有几个还在,但没有关于他们的其他信息。Kim 是一种弹簧驱动的杠杆机器,与其他杠杆机器的关键区别在于,Kim 的机头位于封闭的锅炉内部。其他杠杆的头部在锅炉内,但锅炉是敞开的。

这种设计允许在锅炉和顶盖之间进行一些惊人的温度控制。温度仍然不受控制回路的控制,因此通过手动关闭和打开开关来进行一些温度冲浪是必要的。水温可能会高于泵机,因为大多数泵机,如 E61 或 DE,都有一个 105℃的限制器。

我的 Kim 对泄压阀做了一点小改动,所以我可以去 126C。通常,我在 116C 和 120C 之间拉球。

这是与 DE 相比最大的区别之一。我已尝试更改 DE 的温度校准,使其更接近 120C,但我能做到的最好温度是 113C。这不是公司推荐的,我也不推荐。我在这些实验中使用了这个温度,因为我试图缩小这个差距,但我认为其他一些关键的差异被忽略了。

设备/技术

意式浓缩咖啡机 : 体面的意式浓缩咖啡机和金特快

咖啡研磨机 : 小生零位

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:常规镜头

预灌注:长,约 25 秒

输液:压力脉动

过滤篮:7 克 VST 多装至 14 克

其他设备: Atago TDS 计Acaia Pyxis 秤

绩效指标

我使用两组指标来评估技术之间的差异:最终得分和咖啡萃取。

最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和余味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

总溶解固体(TDS)是用折射仪测量的,这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

强度半径(IR)定义为 TDS vs EY 的控制图上原点的半径,所以 IR = sqrt( TDS + EY)。这一指标有助于标准化产量或酿造比的击球性能。

成对拍摄

我在 7 次烘烤中观察了 10 对输出比率相似的镜头。除了一个例外,所有的金镜头味道更好。

金的味道更好,因为它们有更高的萃取物和 TDS。

当查看单个指标时,除了苦味之外,还有很大的差距。

我应用了一个双尾 t 检验,他们显示差异在统计学上是显著的。从数据来看,这似乎是显而易见的,但 t 检验并无大碍。这个数据集很小,但考虑到巨大的差距,还是很有趣的。

我进行这种比较并不是为了抨击 DE。DE 是一台令人惊叹的机器,我在 53 次轮廓迭代和 100 多次更改后制作了轮廓。因此,在我制作的配置文件和我试图模仿的机器之间有着本质上的不同。我将在未来几周内公布我的发现,因为我相信我发现了一个被忽视的变量,这个变量以前没有在 espresso 社区中讨论过。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

从零开始的决策树和随机森林

原文:https://towardsdatascience.com/decision-tree-and-random-forest-from-scratch-4c12b351fe5e

对决策树和随机森林算法的全面外行介绍

图片由叶夫根尼·切尔卡斯基Unsplash 上拍摄

决策树和随机森林可能是机器学习领域中最经典的算法之一。在本教程中,我们将带读者在公园里漫步,欣赏这些雄伟树木背后的算法之美。除了为您提供这些算法结构背后的简单直觉之外,我们将带您从头开始理解这些代码,这样在本教程结束时,您将基本掌握强大的决策树和随机森林算法。

页(page 的缩写)s:这篇文章受到了由约阿希姆·瓦伦蒂撰写的关于决策树和由达里奥·拉德契奇撰写的随机森林的早期文章的启发,但是为了更好的算法和更容易理解,这些想法被进一步微调和提炼。

1.决策树算法

虽然决策树可以用于回归(预测连续的实值目标,例如预测汽车价格,给定特征),但在本教程中,我们将只考虑分类决策树(预测目标的离散类别,例如预测水果类型,给定特征)。

在关于特征-目标关系的模型训练期间,从根(父)节点(包含特征-目标关系的所有数据)生长一棵树,然后以二进制方式递归地分裂成子节点(整个数据的子集)。在父节点中的单个特征上,在该特征的期望阈值处执行每次分割。例如,在父节点的每次分裂期间,如果特征小于阈值,我们转到左节点(具有相应的数据子集),否则转到右节点。但是我们如何决定分割呢?进入基尼杂质。

1.1.基尼杂质

决定父节点分裂的成本函数被称为基尼不纯度,这基本上是一个量化节点的同质性或“纯净”程度的概念,与节点中目标的分布有关。如果节点中的所有训练样本属于同一类,则节点被认为是纯的(G=0 ),而具有来自许多不同类的许多训练样本的节点将具有接近 1 的 Gini 杂质。

作者图片

决策树向基尼杂质递减的方向生长,根节点最不纯。因此,在每个节点,搜索并选择最优特征和相应的最优阈值,使得 2 个子节点的加权平均基尼系数最小。

如果最小可能加权平均 Gini 杂质仍大于父节点的 Gini 杂质,则不会发生二进制分裂,父节点将被视为终端节点

作者图片

1.2.水果分类示例

水果表及其特点。图片作者。

fruit_name为对象、masswidthheightcolor_score为特征的表格数据为例。当我们将这些数据训练到一个这样的模型上时,决策树(最大深度=2)看起来会是什么样子?

页(page 的缩写)为了演示的简单,二进制分割被认为是随机发生的,而不应该被认为是最佳分割。

最大深度=2 的决策树。图片作者。

为了更好地理解,我们鼓励你勾画出上述决策树,并对其进行必要的基尼系数计算。我们也邀请你勾画出决策树的其他排列。

1.3.推理时的预测

给定一个新的水果,具有其指定特征的苹果,预测的水果将从其根节点遍历决策树,遵循每个节点给定的规则,到达终端节点。

通过模型进行推理的测试示例。图片作者。

在上面的测试示例中,因为 mass=160>155,所以决策路径从根节点到右边的子节点。从这一点开始,由于 height=7.4≤7.7,所以它进入左侧子节点,该子节点是没有进一步分割的终端节点。

使用决策树对单个测试示例进行推理。图片作者。

来自决策树的预测类然后由终端节点处的多数类给出,这是在训练过程中确定的。

1.4.从头开始编写决策树

在我们对决策树的工作原理有了基本和直观的了解之后,让我们开始构建一个决策树吧!从头开始构建一个决策树可能看起来令人生畏,但是随着我们一步一步地构建它的组件,情况可能会变得简单得多。在这一节中,我们将按模块介绍代码。

1.4.1。实例化节点类

在导入必要的库之后,我们创建一个带有几个伴随属性的类Node。这通常被认为是父节点。

首先,self.predicted_class将在每个(父)节点中存储预测的类。是的,这是正确的,每个节点都应该有多数类作为预测类,即使是根节点。并且该属性在代码中特别重要,尤其是对于终端节点,当在推断期间应该检索预测的类时。

self.feature_indexself.threshold主要记录父节点处的最优特征和该特征对应的最优阈值,将与父节点发生分裂形成子节点。

self.leftself.right是非常重要的属性,用于递归实例化子节点。因此,这些属性本质上只是另一个类Node,它又可能有self.leftself.right属性作为子节点。

1.4.2。实例化决策树类

决策树模型可以有几个超参数,可以调整这些参数以获得特定数据集的最佳性能模型。在我们的算法中,我们对这种定制进行了编码。虽然self.max_depth确定了树生长的最大深度,但为了在较低深度实现终端节点(通常是为了防止过度拟合),self.max_features允许我们仅考虑有限数量的特征来搜索最佳分割,这允许对特征进行一些正则化效果。

self.random_state控制估计器的随机性,特别是特征置换。每次分割时,特征总是随机排列。当max_features < n_features时,算法将在每次分割时随机选择max_features,然后在其中找到最佳分割。但是最佳发现分割可能在不同的运行中有所不同,即使max_features = n_features.是这种情况,如果标准的改进对于几个分割是相同的,并且必须随机选择一个分割。

最后,self.tree是根Node,包含所有其他的子Node作为属性,当生长树的函数被调用时,这些属性将被递归生长。

1.4.3。拟合方法

DecisionTree类的fit方法接受 Pandas DataFrame/Series 或 Numpy 数组形式的训练数据集,然后确定类的数量、特征的数量和最大特征。最后,调用函数self.grow_tree递归构建决策树。

1.4.4。 种树的方法

在将自身实例化为父节点Node并确定其父节点的预测类之前,grow_tree方法首先接收来自fit方法的数据。当满足self.max_depth条件时,调用函数self.best_split来找到最优特征 ID 和相应的最优阈值,以分裂成子节点。

如果存在最佳特征 ID 和最佳阈值(子节点的 Gini 杂质的加权平均值小于父节点的 Gini 杂质),则将为左子节点和右子节点过滤在特征阈值处分裂的对比数据子集。

然后,各个数据子集被递归调用到同一个grow_tree函数中,并作为父Node的属性存储为node.leftnode.right

然后返回父对象Node

1.4.5。最佳拆分方法

best_split方法从grow_tree 方法中的父节点获取数据,然后检查父节点的 Gini 杂质。如果父节点已经是纯的(G=0),best_feat_idbest_threshold作为None返回。否则,该方法继续基于最大特征数量对特征索引进行加扰和随机化。

接下来,该方法继续搜索最佳特征索引和相应的阈值。在迭代该特征的连续值之间的每个平均值之前,首先过滤和排序每个特征的唯一数据值。

对于每对特征指数和特征阈值,计算平均基尼系数。对应于最低平均 Gini 杂质(如果它低于父节点 Gini 杂质)的特征索引和特征阈值对作为best_feat_idbest_threshold返回。

1.4.6。预测方法

最后,在训练集上训练决策树之后,可以通过predict方法对测试集进行推理。使用predict_example方法,通过合适的决策树运行测试集中的每个测试示例,一次一个。

1.4.7。预测示例方法

每个测试示例都是特征值的向量。在predict_example方法中,测试示例根据每个特性的阈值遍历基于规则的节点,直到到达终端节点,在那里存储并返回预测的类。

决策树算法的完整代码显示在 Github 上。精度性能与 scikit-learn 的实现非常接近,我们邀请您试用。

2.随机森林算法

随机森林,顾名思义,是决策树的集合,也用于回归和分类任务。同样,我们在这里只考虑随机森林进行分类。

随机森林算法建立在“弱”学习者(决策树)投票的思想上,给出了树组成森林的类比。随机性元素有几个方面:

  1. 每棵树都适合整个数据集的子集,因此每棵树的生长方式不同,规则也不同
  2. 如果整个数据集用替换随机引导,对于每棵树,每棵树仍将具有略微不同的数据分布,因此将以不同的规则不同地生长。
  3. 即使整个数据集在没有替换的情况下被引导,每个树仍然可能不同地生长,这是由于在决策树中被考虑用于最佳分裂的特征的随机顺序或子集。

决策树与随机森林。图片由来源

这种随机森林算法被称为自举聚合或 bagging。本质上,它是一个结合多个模型(决策树)的概念,在数据集上引导,通过减少方差和避免单个模型的过度拟合来提高性能。

总而言之,

  • 在训练期间,从整个数据集顺序获得 N 个自举数据集。
  • 每个决策树(总共 N 个决策树)都是从每个自举数据集构建的。
  • 在推理过程中,每个决策树都会做出一个预测,随机树的最终预测将作为多数投票返回。

对随机森林进行推断时的预测。图片由来源提供。

2.1.从头开始编码随机森林

如您所见,随机森林与决策树算法密切相关。因此,从某种意义上来说,它是上述决策树算法代码的结转。同样,我们将按模块介绍代码。

2.1.1。实例化随机森林类

在导入必要的库(包括决策树算法的模块)之后,我们创建一个带有相应属性的类RandomForest:

  • self.num_trees:用于分类的投票决策树分类器的数量。
  • self.subsample_size:用于训练每个决策树的总训练样本的比例。
  • self.max_depth:每个决策树的最大深度。如果没有,那么节点被扩展,直到所有的叶子都是最纯的。
  • self.max_features:对于每个决策树,在从父节点到子节点的每次分裂中,仅考虑“最大特征”来寻找阈值分裂。
  • self.bootstrap:训练样本的 Bootstrap 抽样,有无替换。
  • self.random_state:控制估计器的随机性。在每个决策树的每次分裂中,特征总是随机排列的,并且引导抽样是随机排列的。
  • self.decision_tree:包含构建后的决策树对象的列表。最初,列表是空的。

2.1.2。拟合方法

在训练期间,RandomForest类的fit方法接受 Pandas DataFrame/Series 或 Numpy 数组形式的训练数据集,并在训练数据集的随机引导样本上顺序构建每个决策树(导入的模块),然后将其作为对象附加到RandomForest类的self.decision_tree属性。通过sample方法获取随机引导样本。

2.1.3。取样方法

fit方法中,训练数据集被摄取到被调用的sample方法中,以返回基于整个数据集的随机引导子集。

2.1.4。预测方法

在推理过程中,每个决策树将所有测试实例的结果预测到预测的中。这些预测列在所有决策树中并排附加在 Numpy 数组中。此后,对于每个测试示例,在每个中采用模式或大多数预测。最后一列作为最终预测结果返回。

随机森林算法的完整代码显示在 Github 上。同样,我们的精度性能与 scikit-learn 的实现非常接近。

3.结论

祝贺你成功来到这里!给你一些编码和机器学习的经验,从这本简单的指南中,你将会对决策树和随机森林如何从头开始工作有一个原始的和完整的理解。这种理解对于任何严肃的数据科学家的学习课程都是必不可少的。

在未来,我期待以最浅显易懂的方式从零开始介绍其他机器学习算法,如香草神经网络(多层感知器)、CNN 和 RNNs。敬请关注,关注我在 LinkedinGithub 的报道。

更新(2022 年 7 月 28 日):我已经从头开始完成了关于逻辑回归和神经网络的 3 篇文章系列。查看它们以了解更多:

https://medium.com/mlearning-ai/neural-networks-from-scratch-logistic-regression-part-1-d8cfc4a2fb3b

干杯!_/_

支持我! —如果你没有订阅 Medium,并且喜欢我的内容,请考虑通过我的推荐链接加入 Medium 来支持我。

https://tanpengshi.medium.com/membership

使用 mlr 在 R 中调整决策树超参数

原文:https://towardsdatascience.com/decision-tree-hyperparameter-tuning-in-r-using-mlr-3248bfd2d88c

了解如何使用 mlr 在 R 中执行超参数网格搜索

照片由Alexis bay doun@ unsplash . com 拍摄

许多人通过研究和应用决策树算法进入数据科学之旅。这并不奇怪,因为这种算法可能是最容易解释的一种,它很好地模仿了人类的决策。

理解决策树还有另一个巨大的优势:它们是最著名的 boosting(极端梯度 Boosting)和 bagging(随机森林)算法的基础,这些算法在世界各地发起了 Kaggle 竞赛,解决了无数的商业问题。

在您掌握了决策树如何构建以及它如何选择用于在数据中执行关键拆分的特征的细节后,您会立即明白,当我们开始让决策树适合解决问题时,有许多决策要做,即:

  • 树应该有多深?
  • 应该考虑多少个相关的示例来拆分一个节点?
  • 我们要考虑多少个例子才能做出决定?
  • 考虑新拆分的最小基尼/熵增阈值是多少?

所有这些问题似乎都有点武断。但是,您可能会注意到,它们都与决策树的一个关键特性相关联— 超参数:不是由模型学习而是由用户参数化的一组值。

调整这些超参数对于实现所有机器学习算法的最终目标——泛化能力——至关重要。而且,在决策树中,可以说它们甚至更重要,因为基于树的算法对超参数空间中的微小变化极其敏感。

训练超参数是全世界数据科学家和机器学习工程师的一项基本任务。而且,了解这些参数中的每一个参数的个体影响会让你更信任并更好地解释你的表现。

在这篇文章中,我们将使用 R 和mlr库来优化决策树超参数。我还想向您展示如何可视化和评估每个参数对我们算法性能的影响。对于我们的例子,我们将使用神话般的泰坦尼克号数据集,可在 Kaggle 中获得。

开始吧!

加载数据

在加载数据之前,让我们调用代码的所有依赖项:

library(dplyr)
library(rpart)
library(rpart.plot)
library(Metrics)
library(mlr)
library(ggplot2)
library(plotly)

描述我们的依赖关系:

  • dplyr执行一些数据角力任务。
  • rpart无需调整即可拟合决策树。
  • rpart.plot绘制我们的决策树。
  • Metrics评估我们模型的性能;
  • mlr训练我们模型的超参数
  • ggplot2对于一般的剧情我们都会做。
  • plotly用于三维绘图。

Titanic 数据集是一个 csv 文件,我们可以使用read.csv函数加载它。该数据集包含有关泰坦尼克号乘客的信息,包括以下各列:

  • 幸存 —表示乘客是否在泰坦尼克号失事中幸存的标志。
  • pclass —乘客的机票等级。
  • 性别—乘客的性别。
  • 年龄——以年为单位的年龄。
  • 泰坦尼克号上兄弟姐妹/配偶的数量。
  • 泰坦尼克号上父母/孩子的数量。
  • 车票—车票号码。
  • 票价 —客运票价。
  • 客舱 —乘客的客舱号。
  • 登船 —乘客登船的港口。

我已经用以下方式加载了它:

titanic <- read.csv(‘train.csv’)

为了简化,我将只使用来自titanic数据框架的原始列的子集——让我使用dplyr来选择它们:

titanic <- titanic %>%
  select(Fare, Age, Sex, Pclass, Survived, SibSp, Parch)

让我们也将数据分成训练测试—留下 20%的数据作为维持组:

# Splitting data into Train and Test
titanic['row_id'] = rownames(titanic)set.seed(123)
train_data <- titanic %>%
  sample_frac(0.8)test_data <- titanic %>%
  anti_join(train_data, by='row_id')# Drop row_id from both dataframes
train_data[,'row_id'] <- NULL
test_data[,'row_id'] <- NULL

尽管我们将在超参数调整中使用交叉验证(稍后我会提到这一点),但测试集将用于确保我们不会过度拟合我们的训练集或交叉验证集。对于决策树来说,这是非常重要的,因为它们非常容易出现高方差。

在我们继续之前,让我们先预览一下我们的数据:

我们泰坦尼克号数据的前 9 行——作者图片

很好,我们已经有了数据,让我们来拟合第一个决策树吧!

拟合第一决策树

对于决策树的第一个普通版本,我们将使用带有默认超参数rpart包。

d.tree = rpart(Survived ~ ., 
               data=train_data, 
               method = 'class')

由于我们没有指定超参数,我们使用 rpart 的默认值:

  • 我们的树可以下降到 30 级—maxdepth = 30
  • 一个节点中执行拆分的最小实例数为 20—minsplit = 20
  • 终端节点中的最小实例数是 7—minbucket = 7
  • 分割必须增加树的“性能”(虽然不是那么直接,我们可以认为“性能”是cp的代理)至少 0.01—cp = 0.01

我们如何知道这些是我们数据的最佳超参数?这些都是随机选择,使用默认选项是一个冒险的赌注。

也许我们可以把节点分离得更远一点。或者,我们可能使用低样本来根据低minsplitminbucket做出决策。在继续之前,这是我们树:

普通决策树—作者图片

我们看到这是一个相对较浅的树,有 4 个层次。让我们检查一下测试集的准确性:

# Predict Values
predicted_values <- predict(d.tree, test_data, type = 'class')# Getting Accuracy
accuracy(test_data$Survived, predicted_values)

我们的准确率约为 79.21%。我们还能提高这个值吗?也许吧!调整超参数是我们可以探索的第一个想法!

让我们首先手动设置它们——我们可以使用rpart函数中的rpart.control来覆盖默认的超参数:

d.tree.custom = rpart(Survived~ ., 
               data=train_data, 
               method = 'class',
               control = c(maxdepth = 5, cp=0.001))

在这棵树上,我将maxdepth设置为 5,强制我的树比我们上面看到的更深一点。此外,我还调整了cp——让我们看看结果:

带有调整的超参数的决策树—作者图片

新的树更深一点,包含更多的规则——在性能方面,它的准确率约为 79.78%,比我们的普通版本好一点!

随着我们的准确度提高了几个百分点,我们的指标也在变化。从我们可以调优的超参数的整体情况来看,其中肯定有一些在测试集上产生了最佳性能——对吗?我们必须手动尝试这些参数吗?

幸好没有!虽然rpart不能让我们自动进行搜索,但是我们有一个名为mlr的库可以帮助我们!

使用 MLR 的超参数调整—调整一个参数

一件很酷的事情是,我们将在这里学到的东西可以扩展到其他模型。mlr库使用完全相同的方法,我们将学习调整随机森林、xgboosts、SVM 等的参数。

过去你可能听说过caret,一个著名的 R 数据科学库。虽然caret也有一些内置的超参数搜索,mlr使我们能够更好地查看那些超参数的影响,不那么“黑箱化”——这是我在这篇文章中使用mlr的主要原因。

所以,mlr ,R 库中的机器学习是 R 中一个很酷的人工智能包,它给了我们训练几个不同模型并执行调优的工具。正如我们已经讨论过的,其中一个优点是它让我们可以查看每个超参数对模型性能的影响。

mlr中一个方便的函数是getParamSet,它返回特定模型的所有可调整参数——对于classification rpart,我们可以调用getParamSet("classif.rpart"),得到:

用于分类决策树的可调超参数—作者图片

所有这些参数都可以使用mlr进行调整。让我们重点关注其中的三种——仅从maxdepth开始。

在列constr上,我们可以看到我们可以调整的值的范围——对于maxdepth,我们可以从 1 到 30 进行深度调整。

如果有一种简单的方法来拟合这 30 种不同版本的决策树,并评估这些模型的准确性,岂不是很有趣?这就是mlr的作用!

mlr需要比普通rpart甚至caret多一点的代码。首先,我们需要定义一个任务——在本例中,我用train_datatarget = 'Survived定义一个分类任务:

d.tree.params <- makeClassifTask(
 data=train_data, 
 target=”fraud”
 )

然后,我们需要创建要迭代的参数网格——就像我们讨论的那样,让我们从单个参数开始。我们需要makeParamSet并使用makeDiscreteParam:

param_grid <- makeParamSet( 
 makeDiscreteParam(“maxdepth”, values=1:30))

我在上面的代码中声明的是,我的树将迭代 30 个不同的maxdepth值,这是一个向量(1:30),它包含 1,2,3 …,30 作为输入到超参数的值。

然后,我们需要做三件事——初始化控制网格实验,选择交叉验证方法,并选择一种用于评估我们结果的方法:

# Define Grid
control_grid = makeTuneControlGrid()# Define Cross Validation
resample = makeResampleDesc("CV", iters = 3L)# Define Measure
measure = acc

交叉验证是改善决策树结果的一种方式。在我们的例子中,我们将使用三重交叉验证。对于测量,我们将使用精确度(acc)。

一切就绪!是时候将一切输入到神奇的tuneParams函数中了,这将启动我们的超参数调谐!

set.seed(123)
dt_tuneparam <- tuneParams(learner=’classif.rpart’, 
 task=d.tree.params, 
 resampling = resample,
 measures = measure,
 par.set=param_grid, 
 control=control_grid, 
 show.info = TRUE)

当您运行上面的代码时,我们的超参数搜索将开始执行!show.info = TRUE将输出执行的反馈:

[Tune-x] 1: maxdepth=1
[Tune-y] 1: acc.test.mean=0.7895909; time: 0.0 min
[Tune-x] 2: maxdepth=2
[Tune-y] 2: acc.test.mean=0.7881845; time: 0.0 min
[Tune-x] 3: maxdepth=3
[Tune-y] 3: acc.test.mean=0.8008132; time: 0.0 min
...

每个maxdepth生成一个 acc.test.mean,交叉验证中使用的几个数据集的acc的平均值。mlr也让我们用generateHyperParsEffectData来评估结果:

result_hyperparam <- generateHyperParsEffectData(dt_tuneparam, partial.dep = TRUE)

我们可以使用ggplot绘制精度的变化图:

ggplot(
  data = result_hyperparam$data,
  aes(x = maxdepth, y=acc.test.mean)
) + geom_line(color = 'darkblue')

每最大深度精度的演变——作者提供的图片

查看我们的图,我们知道在深度为 5 之后,对精确度的影响是微不足道的,差异极小。让我们确认一下tuneParams函数选择的最佳模型——我们可以通过直接调用dt_tuneparam对象来检查:

Tune result:
Op. pars: maxdepth=11
f1.test.mean=0.9985403

调整结果选择 19 maxdepth作为最佳参数,只是因为微小的差异——不过,让我们使用对象dt_tuneparam$x来拟合我们的最佳参数,以拾取保存的超参数并使用setHyperPars来存储它们:

best_parameters = setHyperPars(
 makeLearner(“classif.rpart”), 
 par.vals = dt_tuneparam$x
 )best_model = train(best_parameters, dt_task)

train将用保存在best_parameters对象中的超参数来拟合决策树。

运行上面的代码后,我们得到了一个拟合树,其中包含从best_model上的网格搜索中返回的最佳超参数。为了在我们的测试集上评估这个模型,我们需要创建一个指向测试数据的新的makeClassifTask:

d.tree.mlr.test <- makeClassifTask(
 data=test_data, 
 target=”Survived”
)

test_data上预测和检查准确性:

results <- predict(best_model, task = d.tree.mlr.test)$data
accuracy(results$truth, results$response)

我们的准确率约为 79.21%,与我们的普通版本相同。所以…很可能,我们对cp参数的调整是为了提高我们的模型性能。

问题是..在本例中,我们保持其他参数不变,这是否意味着我们只能逐个调整超参数**?不对!

有了mlr,我们可以同时调整整个参数范围,只需在代码中做一点小小的调整!就这么办吧。

调整多个参数

调整多个超参数很容易!还记得我们为网格搜索创建的param_grid对象吗?让我们回忆一下:

param_grid <- makeParamSet( 
 makeDiscreteParam(“maxdepth”, values=1:30))

如果我在makeParamSet函数中添加新的参数,我将会添加新的参数,这些参数将会在搜索中进行组合——例如,让我们将cpminsplit添加到我们的场景中:

param_grid_multi <- makeParamSet( 
 makeDiscreteParam(“maxdepth”, values=1:30),
 makeNumericParam(“cp”, lower = 0.001, upper = 0.01),
 makeDiscreteParam(“minsplit”, values=1:30)
 )

makeNumericParam创建numeric参数(例如包含小数位的cp)—我们可以在getParamSet函数中检查哪些超参数是离散的还是数字的(记住integers可以用makeDiscreteParam调用)。

又该如何训练这种多参数搜索呢?通过将我们的param_grid_multi输入到tuneParams函数中!

dt_tuneparam_multi <- tuneParams(learner=’classif.rpart’, 
 task=d.tree.mlr, 
 resampling = resample,
 measures = measure,
 par.set=param_grid_multi, 
 control=control_grid, 
 show.info = TRUE)

当我们训练更多数量的超参数时,存在计算成本。你会注意到dt_tuneparam_multi将比dt_tuneparam搜索花费更多的时间,因为我们将在 3000(!)树到我们的数据。

在搜索结束时,您可能会得到如下输出:

[Tune]输出中,我们有搜索的最佳参数:

  • 15 的一个maxdepth
  • 0.003 的一个cp
  • 5 的一个minsplit

这个超参数的组合在交叉验证中产生了大约 82%的准确率,不错!

让我们提取最佳参数,用它们训练一个新的树,并在我们的测试集上查看结果:

# Extracting best Parameters from Multi Search
best_parameters_multi = setHyperPars(
 makeLearner(“classif.rpart”, predict.type = “prob”), 
 par.vals = dt_tuneparam_multi$x
)best_model_multi = train(best_parameters_multi, d.tree.mlr)# Predicting the best Model
results <- predict(best_model_multi, task = d.tree.mlr.test)$dataaccuracy(results$truth, results$response)

我们在 test_set 的准确率是 81.46%!仅仅通过调整这些参数,我们就能够将基线精度提高 2 个百分点,这是一个非常好的结果!

最后,为了帮助您直观地了解我们所做的工作,让我们绘制一个网格搜索结果样本的准确性结果:

# Extracting results from multigrid
result_hyperparam.multi <- generateHyperParsEffectData(dt_tuneparam_multi, partial.dep = TRUE)# Sampling just for visualization
result_sample <- result_hyperparam.multi$data %>%
 sample_n(300)hyperparam.plot <- plot_ly(result_sample, 
 x = ~cp, 
 y = ~maxdepth, 
 z = ~minsplit,
 marker = list(color = ~acc.test.mean, colorscale = list(c(0, 1), c(“darkred”, “darkgreen”)), showscale = TRUE))
hyperparam.plot <- hyperparam.plot %>% add_markers()
hyperparam.plot

超参数搜索景观-作者图片

在 y 轴上我们有minsplit。x 轴上有maxdepth,z 轴上有cp

每个点都是一个实验(由超参数组合而成),颜色与该实验的精度结果相关。红点表示准确度较低。绿点意味着更好的性能。

在三维图中有一个明显的红色区域,我们看到来自cp的结果不是很好——让我旋转它以获得更好的视图:

超参数搜索景观-作者图片

请注意,真正低的 cp 会产生更差的性能,尤其是与低的 minsplit 结合使用时!

可视化我们的超参数搜索结果‘让我们可以鸟瞰我们的训练过程是如何进行的。

如果你想查看以上剧情的互动版本,请点击链接

感谢你花时间阅读这篇文章!我希望你已经欣赏了它,现在你可以理解如何使用 r 训练超参数

超参数可以建立或破坏一个模型,作为数据科学家,我们需要知道如何用几行代码有效地调整它们。如果使用 R,mlr可能是做这个常见机器学习任务的绝佳选择!

我在 Udemy 上建立了一个R 简介 和一个 学习数据科学的训练营 。这两个课程都是为初学者量身定做的,我希望你能在我身边!

数据科学训练营:你成为数据科学家的第一步 —图片由作者提供

https://ivopbernardo.medium.com/membership

下面是这篇文章中代码的一个小要点:

数据集许可:本文中使用的数据集在https://www.openml.org/d/40945公开提供使用

决策树,解释

原文:https://towardsdatascience.com/decision-trees-explained-d7678c43a59e

如何训练他们,他们如何工作,与工作代码的例子

津巴布韦的一棵大决策树。图片作者。

在这篇文章中,我们将讨论一种常用的机器学习模型,称为 决策树 。决策树是许多应用程序的首选,主要是因为它们的高度可解释性,但也因为它们的设置和训练相对简单,以及用决策树进行预测所需的时间较短。决策树对于表格数据来说是很自然的,事实上,在这种类型的数据上(相对于图像),它们目前似乎优于神经网络。与神经网络不同,树不需要输入标准化,因为它们的训练不是基于梯度下降,并且它们只有很少的参数可以优化。他们甚至可以对有缺失值的数据进行训练,但现在这种做法不太推荐,缺失值通常是估算的。

决策树的众所周知的用例是推荐系统(根据你过去的选择和其他特征,如年龄、性别等,你预测的电影偏好是什么)。)和搜索引擎。

树中的预测过程由样本属性(特征)与预先学习的阈值的一系列比较组成。从顶部(树根)开始向下(朝向树叶,是的,与真实的树相反),在每一步中,比较的结果确定样本在树中是向左还是向右,并由此确定下一步的比较。当我们的样本到达一个叶子(一个末端节点)时,基于叶子中的多数类做出决策或预测。

图 1 显示了根据花瓣和萼片的长度和宽度将鸢尾属样品分成 3 个不同种类(类)的问题。

我们的示例将基于著名的 Iris 数据集(Fisher,R.A .“分类问题中多重测量的使用”Annual Eugenics,7,Part II,179–188(1936))。我是用 sklearn 包下载的,这是一个 BSD (Berkley 源码发布)授权软件。我修改了其中一个类的特性,减小了训练集的大小,将这些类混合在一起,让它变得更有趣。

图 1 在 Iris 数据集的修正训练集上训练的决策树。图片作者。

我们稍后会解决这棵树的细节问题。现在,我们将检查根节点,并注意到我们的训练群体有 45 个样本,分为 3 类,如下所示:[13,19,13]。“class”属性告诉我们,如果这个样本是一个叶子,树将为它预测的标签——基于节点中的多数类。例如,如果不允许我们运行任何比较,我们将处于根节点,我们的最佳预测将是类 Veriscolor,因为它在训练集中有 19 个样本,而其他两个类只有 13 个样本。如果我们的比较序列将我们引向左起第二片叶子,模型的预测将再次是 Veriscolor,因为在训练集中有 4 个该类的样本到达这片叶子,而只有 1 个 Virginica 类的样本和 0 个 Setosa 类的样本。

决策树可用于分类或回归问题。让我们从讨论分类问题开始,解释树训练算法是如何工作的。

做法:

让我们看看如何使用 sklearn 训练一棵树,然后讨论其机制。

下载数据集:

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoderiris = load_iris()
X = iris['data']
y = iris['target']
names = iris['target_names']
feature_names = iris['feature_names']# One hot encoding
enc = OneHotEncoder()
Y = enc.fit_transform(y[:, np.newaxis]).toarray()# Modifying the dataset
X[y==1,2] = X[y==1,2] + 0.3# Split the data set into training and testing
X_train, X_test, Y_train, Y_test = train_test_split(
    X, Y, test_size=0.5, random_state=2)# Decreasing the train set to make things more interesting
X_train = X_train[30:,:]
Y_train = Y_train[30:,:]

让我们将数据集可视化。

# Visualize the data sets
import matplotlib
import matplotlib.pyplot as pltplt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
for target, target_name in enumerate(names):
    X_plot = X[y == target]
    plt.plot(X_plot[:, 0], X_plot[:, 1], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.axis('equal')
plt.legend();plt.subplot(1, 2, 2)
for target, target_name in enumerate(names):
    X_plot = X[y == target]
    plt.plot(X_plot[:, 2], X_plot[:, 3], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[2])
plt.ylabel(feature_names[3])
plt.axis('equal')
plt.legend();

图 2 改进的 Iris 数据集的可视化。图片作者。

仅仅是火车组:

plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
for target, target_name in enumerate(names):
    X_plot = X_train[Y_train[:,target] == 1]
    plt.plot(X_plot[:, 0], X_plot[:, 1], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.axis('equal')
plt.legend();plt.subplot(1, 2, 2)
for target, target_name in enumerate(names):
    X_plot = X_train[Y_train[:,target] == 1]
    plt.plot(X_plot[:, 2], X_plot[:, 3], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[2])
plt.ylabel(feature_names[3])
plt.axis('equal')
plt.legend();

图 3——只有(缩小的)列车组。图片作者。

现在,我们准备训练一棵树,并将其可视化。结果就是我们在图 1 中看到的模型

from sklearn import tree
import graphviziristree = tree.DecisionTreeClassifier(max_depth=3, criterion='gini', random_state=0)
iristree.fit(X_train, enc.inverse_transform(Y_train))feature_names = ['sepal length', 'sepal width', 'petal length', 'petal width']dot_data = tree.export_graphviz(iristree, out_file=None, 
                      feature_names=feature_names,  
                      class_names=names,
                      filled=True, rounded=True,  
                      special_characters=True)  
graph = graphviz.Source(dot_data)display(graph)

我们可以看到,该树使用花瓣宽度进行第一次和第二次分割——第一次分割清楚地将类 Setosa 与其他两个类分开。请注意,对于第一次分裂,花瓣长度可以同样好地工作。

让我们看看这个模型在训练集上的分类精度,然后是测试集:

from sklearn.metrics import precision_score
from sklearn.metrics import recall_scoreiristrainpred = iristree.predict(X_train)
iristestpred = iristree.predict(X_test)# train precision:
display(precision_score(enc.inverse_transform(Y_train), iristrainpred.reshape(-1,1), average='micro', labels=[0]))
display(precision_score(enc.inverse_transform(Y_train), iristrainpred.reshape(-1,1), average='micro', labels=[1]))
display(precision_score(enc.inverse_transform(Y_train), iristrainpred.reshape(-1,1), average='micro', labels=[2]))>>> 1.0
>>> 0.9047619047619048
>>> 1.0# test precision:
display(precision_score(enc.inverse_transform(Y_test), iristestpred.reshape(-1,1), average='micro', labels=[0]))
display(precision_score(enc.inverse_transform(Y_test), iristestpred.reshape(-1,1), average='micro', labels=[1]))
display(precision_score(enc.inverse_transform(Y_test), iristestpred.reshape(-1,1), average='micro', labels=[2]))>>> 1.0
>>> 0.7586206896551724
>>> 0.9473684210526315

正如我们所看到的,训练集中数据的稀缺以及类 1 和类 2 混合的事实(因为我们修改了数据集)导致测试集中这些类的精度较低。0 类保持完美的精度,因为它与其他两类高度分离。

现在谈谈理论——树是如何训练的?

换句话说,它如何选择最佳的特征和阈值放入每个节点?

基尼杂质

如同在其他机器学习模型中一样,决策树训练机制试图最小化由训练集上的预测误差引起的一些损失。基尼杂质指数(源自意大利统计学家 科拉多基尼 )是分类准确度的自然衡量标准。

图 4-树木的两个常见训练目标:基尼系数和熵。P(ci)是从总体中随机选取一个 ci 类样本的概率。n 是类的数量。

高基尼系数对应的是异质人口(每个阶层的样本量相似),而低基尼系数表示同质人口(即主要由单一阶层组成)

最大可能的基尼值取决于类别的数量:在 C 个类别的分类问题中,最大可能的基尼值是 1–1/C(当类别均匀分布时)。最低基尼系数为 0,当整个人口由一个阶层组成时,基尼系数达到 0。

基尼系数是随机分类时错误分类的期望值。

为什么?

因为从 ci 类中随机选取一个样本的概率是 p(ci) 。挑了那个,预测错类的概率是 (1-p(ci)) 。如果我们对所有类别的p(ci)(1-p(ci))*求和,我们得到图 4 中的基尼系数公式。

以基尼指数为目标,该树在每一步中选择特征和阈值,以最大限度地降低两个结果人口的加权平均基尼系数(或最大限度地增加其加权平均同质性)的方式分割人口。换句话说,训练逻辑是最小化两个结果群体中随机分类错误的概率,将更多的权重放在较大的子群体上。

是的——该机制会检查所有样本值,并根据标准将其拆分,以检查所得的基尼系数。

根据这个定义,我们还可以理解为什么阈值总是在至少一个训练样本上找到的实际值——使用样本之间的间隙值没有好处,因为得到的分割是相同的。

另一个常用于树训练的度量是 (见图 4 中的公式)。

基尼策略的目标是最小化下一步的随机分类误差,而熵最小化策略的目标是最大化 信息增益

信息增益

在缺乏关于一个群体如何被分成 10 类的先验知识的情况下,我们假设在它们之间平均分配。在这种情况下,我们需要平均 3.3 个是/否问题来确定样本的分类(您是 1-5 级吗?如果不是,你是 6-8 班的吗?等等。).这意味着总体的熵是 3.3 比特。

但是现在让我们假设我们做了一些事情(比如一个聪明的分裂)给了我们关于人口分布的信息,现在我们知道 50%的样本在 1 类,25%在 2 类,25%在 3 类。在这种情况下,总体的熵将是 1.5——我们只需要 1.5 比特来描述随机样本。(第一个问题——你是一班的吗?这个序列有 50%的时间会在这里结束。第二个问题——你是二班的吗?并且不需要更多的问题—所以平均 50%1 + 50%2 = 1.5 个问题)。我们得到的信息值 1.8 比特。

像基尼一样,最小化熵也与创建更同质的人口相关,因为同质人口具有更低的熵(单个阶层人口的极端熵为 0——无需问任何是/否问题)。

基尼还是熵?

大多数消息来源声称,这两种策略之间的差异并不显著(事实上,如果你试图在我们刚刚解决的问题上训练一棵熵树,你会得到完全相同的分裂)。很容易看出为什么:当基尼系数最大化一类概率的期望值时,熵最大化对数类概率的期望值。但是对数概率是概率的单调递增函数,所以它们的操作通常非常相似。然而,当人口高度不平衡时,熵最小化可能选择不同于基尼系数的配置。例如,我们可以考虑一个包含 1000 个训练样本的数据集,其中 980 个属于 0 类,20 个属于 1 类。假设该树可以根据图 5 中的示例 a 或示例 b 选择一个阈值对其进行分割。我们注意到,这两个示例都创建了一个主要由多数类组成的人口众多的节点,以及一个主要由少数类组成的人口较少的第二节点。

在这种情况下,对数函数在小值处的陡度将比基尼准则更强烈地激励熵准则来净化具有大群体的节点。如果我们算一下,我们会发现基尼系数会选择分裂 a ,熵系数会选择分裂 b

这可能导致特征/阈值的不同选择。不一定更好或更坏——这取决于我们的目标。

图 5 高度不平衡数据的两种可能分裂。图片作者。

请注意,即使在初始群体平衡的问题中,分类树的较低节点通常也会具有高度不平衡的群体。

培训结束

当树中的路径达到指定的深度值时,或者当它包含零基尼/熵群体时,它停止训练。当所有路径停止训练时,树就准备好了。

通常的做法是限制树的深度。另一个是限制一个叶子中的样本数量(不允许样本少于阈值)。这两种做法都是为了防止列车组过度装配。

回归树

既然我们已经研究出了训练分类树的细节,那么理解回归树就非常简单了:回归问题中的标签是连续的而不是离散的(例如,给定药物剂量的有效性,以%的情况来衡量)。在这类问题上训练,回归树也分类,但是标签是作为每个节点中样本的平均值动态计算的。这里,通常使用 均方误差卡方度量 作为最小化的目标,而不是基尼和熵。

结论

在这篇文章中,我们了解到决策树基本上是比较序列,可以训练来执行分类和回归任务。我们运行 python 脚本来训练决策树分类器,使用我们的分类器来预测几个数据样本的类别,并计算训练集和测试集上预测的精度和召回指标。我们还学习了决策树训练背后的数学机制,其目的是在每次比较后最小化一些预测误差度量(基尼、熵、mse)。我希望你喜欢读这篇文章,在我的其他文章中再见!

决策树解释—熵、信息增益、基尼系数、CCP 剪枝

原文:https://towardsdatascience.com/decision-trees-explained-entropy-information-gain-gini-index-ccp-pruning-4d78070db36c

尽管决策树看起来简单而直观,但算法如何决定分裂以及如何修剪树却一点也不简单。在这篇文章中,我将带你通过一个简单的例子来理解决策树的内部工作原理。

来自 Scikit Learn 的 Iris 决策树(图片来源: sklearn )

决策树是一种流行的和令人惊讶的有效技术,特别是对于分类问题。但是,看似直观的界面隐藏着复杂性。选择变量和层次的标准可能很难得到,更不用说基尼指数,熵(等等,那不是物理学吗?)和信息增益(那不是信息论吗?).如你所见,有很多棘手的问题会让你陷入困境。理解决策树的最好方法是通过一个小例子,这个小例子足够复杂,能够展示一些共同点,人们突然会说,“不知道这里发生了什么…?”。

因此,这篇文章更像是一个教程或演示,我将通过我创建的玩具数据集来理解以下内容:

1.什么是决策树:根节点、子节点、终端/叶节点

2.分割标准:熵、信息增益与基尼系数

3.子节点如何分割

4.为什么树木过度生长,如何阻止这种现象

5.如何使用决策树进行预测

那么,让我们开始演示吧…

1。 决策树是做什么的?

让我们从核心问题真正开始。例如,我们试图根据各种预测变量(如空腹血糖、身体质量指数、血压等)对患者是否患有糖尿病进行分类。这显然是一个新患者的预测问题。我们也有 1000 份病历来帮助我们了解哪些特征在预测中最有用。与逻辑回归等其他分类算法不同,决策树在运行和识别哪些变量是重要的方面有所不同。

在决策树中要理解的第一件事是,它们将预测器空间,即目标变量分割成不同的子组,从目标变量的角度来看,这些子组相对更同质。例如,如果目标变量是二进制的,类别为 1 和 0(在下图中用绿色和红色的点表示),那么决策树会将目标变量空间划分为多个子组,这些子组在具有 1 或 0 方面更加同质。

目标变量拆分过程(图片来源:作者)

这是总体概念。让我们从理解决策树的各种元素开始。

理解决策树的组成部分

决策树是一个分支流程图或树形图。它由以下组件组成:

。目标变量如糖尿病与否及其初始分布。

  • 根节点:这是通过查找最能拆分目标变量的变量来开始拆分过程的节点
  • 节点纯度:决策节点通常是不纯的,或者是两类目标变量的混合(图像中的 0,1 或绿色和红色点)。纯节点是那些只有一个类的节点,因此称为纯节点。它们要么只有图像中的绿点,要么只有红点。
  • 决策节点:这些是后续或中间节点,其中目标变量再次被其他变量进一步分割
  • 叶节点或终端节点是纯节点,因此用于进行数值或类别的预测。

让我们直观地看看这个..

决策树的结构(图片来源:我的收藏)

一般来说,决策树采用一个陈述或假设或条件,然后决定该条件是否成立。条件沿分支显示,应用于目标变量的条件结果显示在节点上。

远离节点的箭头表示应用于该节点的条件。指向某个节点的箭头表示满足某个条件。

这是决策树的第一层——理解将决策空间分割成更小空间的流程,这些空间最终在目标变量中变得越来越同质。这最终导致了一个预测。

决策树提供了极大的灵活性,因为我们可以使用数字和分类变量来分割目标数据。类别数据在变量中被分成不同的类。数字有点复杂,因为我们必须为被测试的条件分割成阈值,例如<18 and ≥18, for example. A numeric variable can appear multiple times in the data with different cut offs or thresholds. Also final classifications can be repeated.

The important things from data science perspective are:

1. Flow of information through the Decision Tree

2. How does Decision Trees select which variable to split on at decision nodes?

3. How does it decide that the tree has enough branches and that it should stop splitting?

Now let us look at a simplified toy example to understand the above process more concretely.

首先是问题:

我们有学生数据的 15 个数据点的数据,关于在线 ML 考试的通过或失败。为了理解基本过程,我们从一个数据集开始,该数据集包括一个二元(通过/失败)目标变量和各种二元或分类预测变量,例如:

  • 是否注册了其他在线课程
  • 学生是否来自数学、计算机科学或其他背景
  • 不管工作还是不工作

数据集如下所示:

在线 ML 考试的玩具数据集(来源:作者)

请注意,只有一个变量,“学生背景”有两个以上的级别或类别——数学、计算机科学、其他。这是决策树与其他分类模型(如逻辑回归或 SVM)相比的优势之一,我们不需要进行一次热编码来将它们变成虚拟变量。

让我们首先看看决策树是如何工作的,然后我们将深入研究决策实际上是如何做出的…

决策树流程

决策树从目标变量开始。这通常称为父节点。然后,决策树根据对该目标变量的影响的层次顺序进行一系列拆分。从分析的角度来看,第一个节点是根节点,这是拆分目标变量的第一个变量。

为了识别根节点,我们将评估我们当前拥有的所有变量对目标变量的影响,以识别将考试通过/失败类分成最同质组的变量。我们拆分这个的候选人是:背景,工作状态,其他在线课程。

我们希望通过这种拆分达到什么目的?假设我们从工作状态作为根节点开始。这分为两个子节点,分别用于工作和不工作。因此,在每个子节点中分别更新通过/失败状态。

示例决策树流程(图片来源:作者创建

这是决策树的基本流程。只要在一个子节点中存在通过和失败的混合,就有进一步分裂的余地,以试图使它成为唯一的一个类别。这被称为节点的纯度。例如,不工作具有 5 个通过和 1 个失败,因此它比具有 5P 和 4F 的工作节点更纯。叶节点将是仅包含通过或失败类的节点。

不纯的节点可以进一步分支以提高纯度。然而,大多数时候我们不一定要深入到每一片叶子都是“纯净”的地步。了解每个节点都是独立的也很重要,因此最好地分割“工作”节点的属性可能不是最好地分割“不工作”节点的属性。

现在,让我们继续学习决策树的核心部分——关键问题:

这棵树如何决定在每一层分出哪个变量?

贪婪的自上而下的做法

决策树遵循一种自上而下的贪婪方法,这种方法被称为递归二进制分裂。递归二进制分裂方法是自上而下的,因为它从树的顶部开始,然后它连续分裂预测器空间。在每次分割时,预测器空间被分成两部分,并通过两个指向下方的新分支显示。该算法被称为贪婪算法,因为在该过程的每一步中,该步骤都进行了最佳分割。它不会向前投射并尝试选择对整个树来说更好的分割。

因此,该算法根据一些统计标准评估所有变量,然后选择根据该标准表现最佳的变量。

变量选择标准

这就是决策的真正复杂性和复杂性所在。变量是根据应用于每个决策节点的复杂统计标准选择的。现在,决策树中的变量选择标准可以通过两种方法来实现:

1.熵和信息增益

2.基尼指数

这两个标准大体相似,并且试图确定哪个变量将拆分数据以导致底层子节点最同质或最纯粹。两者都用在不同的决策树算法中。更令人困惑的是,不清楚哪一种是首选方法。因此,人们必须对两者都有所了解。

让我们从熵和信息增益准则开始

什么是熵?

熵是一个来自物理学的术语,意思是无序的度量。更具体地说,我们可以将其定义为:

是一个科学概念,也是一个可测量的物理属性,通常与无序、随机或不确定状态相关联。这个术语和概念被用于不同的领域,从第一次被认识的经典热力学到统计物理学的微观描述,以及信息论的原理。

https://en.wikipedia.org/wiki/Entropy

信息论中,随机变量是该变量可能结果固有的“信息”、“惊喜”或“不确定性”的平均水平。

https://en . Wikipedia . org/wiki/Entropy _(information _ theory)

在决策树的上下文中,熵是节点中无序或不纯的度量。因此,具有更多可变组成的节点,例如 2 个通过和 2 个失败,将被认为比仅通过或仅失败的节点具有更高的熵。熵或无序的最大水平由 1 给出,最小熵由值 0 给出。

所有实例都属于 1 类的叶节点的熵为 0。然而,类被平均划分的节点的熵将是 1。

熵的度量公式如下:

其中 pi 是随机选择类别 I 中的一个示例的概率。让我们在示例的上下文中更好地理解这一点。因此,父节点的初始熵由通过与失败的概率给出。在我们的数据集中,目标变量有 9 次通过,6 次失败。因此,熵公式的概率为:

现在本质上,决策树确定根节点的工作是计算每个变量的熵和它的潜在分裂。为此,我们必须计算每个变量的潜在分裂,计算两个或所有节点的平均熵,然后计算相对于父节点的熵变化。这种熵的变化被称为信息增益,代表一个特征为目标变量提供了多少信息。

Entropy_parent 是父节点的熵,Entropy_children 表示跟随该变量的子节点的平均熵。在当前情况下,因为我们有 3 个变量,必须从分割的角度进行计算。

1.就业状况

2.在线课程状态

3.学生背景

为了计算熵,首先让我们将熵和信息增益的公式放在数据集中的变量上:

  1. 每个节点通过和失败的概率,即 Pi:

2.熵:

3.子节点的平均熵:

请注意,平均熵是父节点拆分成的所有子节点的加权平均值。因此,在我们的示例中,工作状态有 2 个子节点,学生背景有 3 个子节点。

4.信息增益:

父节点计算

首先,我们将使用上面的公式计算父节点熵。使用任何在线 log2 计算器计算对数值。在我们的案例中,他们计算出:

(数学注释:任何小于 1 的以 2 为底的对数都是负数,因此我们乘以一个负号得到一个正数)

到目前为止,这只是父节点的熵。现在我们必须决定使用哪个属性或变量来分割它以获得根节点。

计算根节点

为此,我们必须计算每个变量的潜在分裂,计算两个节点的平均熵,然后通过父节点计算熵的变化。

让我们从工作状态变量开始,计算分裂的熵。

然后,我们计算工作状态分裂的平均熵,作为来自落在每个子节点中的观察总数的观察份额的加权平均值。

信息增益=熵 _ 父—熵 _ 子=

0.9183–0.8119 = .1858

(计算显示在下面的电子表格中)

以类似的方式,我们可以评估学生背景和在线课程变量的熵和信息增益。结果如下表所示:

我们将暂时放弃这个变量,然后继续评估其他变量。下面的电子表格显示了所有变量的熵计算:

根节点熵计算(来源:作者)

为了找到根节点属性,我们查看来自学生背景的相对于初始父熵的信息增益。这表明最大降幅为 0.5370。因此,这是将被选为根节点的属性。另一个变量——“工作状态”和“在线课程”显示,相对于父节点,熵的减少要小得多。

因此,根据上面的计算,我们已经确定了根节点是什么。该树现在看起来如下所示:

决策树的根节点

学生背景将目标变量分为 3 组。来自 CS 背景的每个人都清楚地通过,因此这是终端或叶节点。其他背景的人都失败了,这也是一个终端节点。数学背景分为 3 次通过和 4 次未通过,因此是不纯的,还有进一步分离以达到更高纯度的空间。

现在,为了分割数学背景子节点,我们需要计算剩余变量的熵和信息增益,即工作状态和在线课程。然后,我们将选择显示最高信息增益的变量。

数学背景节点的熵和信息增益计算可以在下表中看到。注意,我们现在有数学背景作为被分裂的节点,因此分裂的平均熵是以它为基础计算的。

注意:输入 log(0)会抛出一个错误。然而数学上我们可以使用极限。通常我们只是在计算中不包括 pj=0 的情况。然而,我已经包括只是为了显示完整的计算。

按照惯例 0 log 0 = 0 ,由于 y log y → 0 为 y → 0。

https://www.icg.isy.liu.se/courses/infotheory/lect1.pdf

每个潜在分裂的熵是:

拆分数学子节点(图片来源:作者)

我们可以看到,工作状态变量的信息增益更高。因此,这是用于继续分支的变量。

数学节点分支

我们现在看到数学节点已经分裂成右边的一个终端节点和一个仍然不纯的节点。注意,现在几乎所有的节点都是终端节点。只有一个节点不是终端。我们可以尝试使用其他在线课程进一步拆分。总之,你明白了。在任何情况下,大多数决策树不一定分裂到每个节点都是终端节点的点。大多数算法都内置了止损点,我们将在后面讨论。此外,如果决策树继续分裂,我们还有另一个问题,即过度拟合。在我们简要回顾了使用基尼指数开发决策树的另一种方法之后,我们将在下面再次讨论这一点。

基尼指数

另一种分割决策树的方法是通过基尼指数。熵和信息增益方法关注节点中的纯度和杂质。基尼指数或杂质衡量随机选择时随机实例被错误分类的概率。基尼指数越低,分类错误的可能性就越低。

基尼指数公式

其中 j 代表目标变量中的类的数量——在我们的例子中是通过和失败

P(i)表示通过/节点中观察总数的比率。

那么,让我们从上面的决策树中取一个例子。让我们从根节点开始,计算每个分裂的基尼指数。基尼系数的最小值(最高纯度)为 0。它的最大值为 0.5。如果基尼系数是 0 . 5,它表明阶级的随机分配。

现在让我们计算学生背景属性的根节点的基尼指数。在这种情况下,我们有 3 个节点。基尼公式要求我们计算每个子节点的基尼指数。然后进行加权平均,计算出该节点的总体基尼指数。

数学子节点:4 通过,3 失败

CS 子节点:4 通过,0 失败

其他子节点:0 通过,4 失败

如我们所见,CS 节点中错误分类的概率为零,因为每个人都通过了。同样,在其他节点上没有错误分类的余地,因为每个人都会失败。只有数学节点有可能被错误分类,这是相当高的,因为最大基尼系数是 0.5。

这种分割的总体基尼系数的计算类似于熵,即 3 个节点分布的加权平均值。

同样,我们也可以计算工作状态和在线课程的基尼指数。这些内容如下:

工作/不工作

在线课程

学生背景变量的基尼指数最低。因此,类似于熵和信息增益标准,我们选择这个变量作为根节点。以类似的方式,我们将再次沿着树向下移动,在节点纯度较低的地方执行拆分

基尼指数与信息增益

根据使用的杂质测量,树分类结果可能会有所不同。这可能对您的模型产生很小(有时很大)的影响。不同的决策树算法似乎没有一个首选的方法。比如 CART 用基尼;ID3 和 C4.5 使用熵。

基尼指数的最大杂质为 0.5,最大纯度为 0,而熵的最大杂质为 1,最大纯度为 0。

如何在决策树中做出预测

既然我们已经了解了,希望是详细了解了,决策树是如何进行分裂和变量选择的,我们可以继续研究它们是如何进行预测的。实际上,一旦一棵树经过训练和测试,预测就很容易了。该树基本上提供了一个基于各种预测变量的流程图。假设我们有一个新的实例和它的不同预测变量的值一起进入流程。与定型和测试数据不同,它没有目标属性的类。我们试图通过沿着树向下移动来预测这个类,在不同的分支测试不同预测变量的值。最终,新实例将移动到叶节点中,并根据叶节点中的主要类进行分类。

假设它看起来像下面的配置:

基于我们的树,我们将首先检查数学分支,然后工作是分支。如我们所见,这是一个叶节点,新的观察结果将基于该节点中的多数投票进行分类,即,因为它是通过,所以该新的观察结果也将被预测为通过。

在实践中,当算法评估新的示例并到达叶节点时,预测基于叶节点中类别的模态值。从上面的例子可以看出,工作节点并不是完全纯的。然而,我们继续预测模态值,这是通过。通常,大多数叶节点不是纯的,因此对于分类预测,我们使用模态值进行预测。如果是数值预测(回归树),我们预测每个叶节点的目标值的平均值。

过度拟合和决策树

过度适应对决策树来说是一个很大的挑战。甚至在我们的玩具例子中,我们可以看到算法继续分裂,直到它到达一个叶节点。通常叶节点可能只有一个或两个实例。这显然会导致一个复杂的树形结构,可能无法很好地概括测试场景。这是因为每个叶子将表示在训练数据中看到的一组非常具体的属性组合,并且树将无法对在训练数据中没有看到的属性组合进行分类。有几种方法可以防止决策树变得过于笨拙:有三种避免过度拟合的方法:

  1. 预修剪或提前停止:防止树长得太大或太深
  2. 后期修剪:让一棵树长到最大,然后根据不同的标准去掉不同的树枝

3.集合或使用多个模型的平均值,如随机森林

在这里,我们将只简要概述预修剪和后修剪技术。像随机森林这样的集合技术需要更多的解释,因此将在另一篇文章中讨论。

修剪

预修剪

预修剪技术指的是提前停止决策树的生长。预修剪技术包括在训练流水线之前调整决策树模型的超参数。SkLearn 中决策树分类器的超参数包括**max_depth****min_samples_leaf****min_samples_split** ,可以对这些超参数进行调优,以提前停止树的生长,防止模型过拟合。最好的方法是使用 GridSearchCV 技术的 sklearn 实现来为决策树模型找到最佳的超参数集。

早期停止方法的一个挑战是它面临一个“地平线”问题,早期停止可能会阻止一些更有成效的拆分。

后期修剪

在这项技术中,我们允许树长到它的最大深度。然后,我们删除部分树,以防止过度拟合。我们有效地考虑了完整树的子树,这些子树根据一个标准被评估,然后被删除。因此,我们有效地沿着树向上走,将树叶转换成节点和子树。特定整合是否通过的标准通常是回归树的 MSE 和分类树的分类误差。

后剪枝的一个挑战是决策树可能会变得非常深和大,因此评估每个分支在计算上可能是昂贵的。一个重要的后期修剪技术是成本复杂性修剪 (ccp),它在这方面提供了一个更有效的解决方案。CCP 是一项复杂而先进的技术,由 Scikit 学习决策树分类器模块中的参数α进行参数化。

那么,CCP 是如何工作的,它做了什么?

CCP 提出的基本问题是:如何确定修剪一棵树的最佳方式?直觉上,我们会选择一个子树来修剪,这样它的移除会导致更低的测试错误率。这可以通过交叉验证来完成,或者,如果我们有足够的样本,可以通过验证集方法来完成。然而,给定一棵完全成长的树中的子树的数量,即使是一个小样本,这也可能是一个非常计算和时间密集型的过程。

成本复杂性修剪—也称为最弱链接修剪—为我们提供了一种方法。我们不是考虑每一个可能的子树,而是考虑由非负调整参数α索引的树序列。…….

调整参数α控制子树的复杂度和它对训练数据的拟合度之间的折衷。当α = 0 时,那么子树 T 将简单地等于 T0,因为然后(8.4)只是测量训练误差。然而,随着α的增加,拥有一个有许多终端节点的树是有代价的,因此对于一个更小的子树,数量(8.4)将趋于最小化

统计学习导论,第 333 页

本质上,参数α非常类似于 Lasso 回归中的惩罚项。CCP 的基本方程式如下

CCP 方程,哈斯蒂,p332(图片来源:詹姆士

这是一个复杂的等式。但是让我们试着更深入地理解一下。一些定义:

Rm: Rm 是对应于第 m 个终端节点的矩形(即预测器空间的子集)

yrm 是与第 m 个末端叶子相关的预测响应

(yi-yˉRm)—由终端节点 m 引用的子树的 MSE(我们对该方程使用回归树方法。为了简单起见,我采用了詹姆斯等人(2014 年)中的公式和方法。

|T|:是树 T 中终端节点的数量

现在让我们看看这个方程在做什么。我们本质上是在所有终端节点上最小化由(yi-yrm)给出的成本或损失。现在α是一个乘以树中终端节点总数的项。如果α=0,那么我们将最小化训练损失。该树将与原始树相同。然而,当α>0 时,我们添加一个随着终端节点数|T|而增加的惩罚项。这意味着对于更小的子树,总成本会最小化。

最小成本复杂性修剪递归地找到具有“最弱链接”的节点。最弱的链路由有效α来表征,其中具有最小有效α的节点首先被修剪。

https://sci kit-learn . org/stable/auto _ examples/tree/plot _ cost _ complexity _ pruning . html

这是什么意思?这意味着该算法正在搜寻训练损失已经很高的节点,因此只能用小的α来最小化。另一方面,作为最小化的一部分,训练损失较小的节点可以容纳较大的惩罚项。

为了了解什么值的ccp_alpha可能是合适的,scikit-learn 提供了[**DecisionTreeClassifier.cost_complexity_pruning_path**](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier.cost_complexity_pruning_path),它在修剪过程的每一步返回有效 alphas 和相应的总叶子杂质。

https://sci kit-learn . org/stable/auto _ examples/tree/plot _ cost _ complexity _ pruning . html

随着α的增加,更多的树被修剪。然后,我们在偏差和方差之间进行权衡。与*ccp_alpha*

实际上,我们增加了模型的偏差,也就是说,我们简化了模型。然而,从反面来看,这意味着我们必须容忍终端节点中的杂质水平不断增加。我们看到,随着α的增加,节点数量和树深度都在减少。

如何确定最优α

绘制ccp_alpha与训练和测试精度的关系图我们看到,当α =0 并且保持[**DecisionTreeClassifier**](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier)的其他默认参数时,树过拟合,导致 100%的训练精度和 88%的测试精度。随着 alpha 的增加,更多的树被修剪,从而创建一个更好的决策树。然而,在某种程度上,随着模型变得过于简化,α的进一步增加实际上会导致测试精度的降低。在本例中,设置ccp_alpha=0.015可最大化测试精度。(详见https://sci kit-learn . org/stable/auto _ examples/tree/plot _ cost _ complexity _ pruning . html)。

精确度与 Alpha(图像源 SkLearn )

决策树的优缺点

1.树给出了用于分类的变量之间关系的可视化模式,因此更易于解释。树的层次结构提供了对可变重要性的洞察。

2.有时,他们实际上可以反映决策过程。

3.白盒模型是可解释的,我们可以追溯到模型的每个结果。这与神经网络等黑盒模型形成对比。

4.一般来说,不太需要准备和清理数据,如标准化和分类变量和缺失值的热编码。

注意 Sklearn 实现目前不支持分类变量,所以我们需要创建虚拟变量。同样,它也不支持缺少值。但理论上两者都可以处理。

5.模型可以通过统计来验证

缺点

1.倾向于过度拟合并因此降低预测准确性

2.决策树可能不稳定,因为数据的微小变化可能会导致生成完全不同的树。例如,这个问题可以通过在集成中使用决策树来缓解

3.可能是不稳健的,即数据的小变化会导致最终估计树的大变化

4.基于相关终端节点的预测是近似的。因此,这可能不是将模型结果外推至未知情况的最佳方法。

5.如果某些类占主导地位,决策树学习者会创建有偏见的树。在与决策树拟合之前,需要平衡数据集。

这就是决策树从开始到至少三分之二的过程。有很多复杂的事情,因此我不能说结束。我希望你喜欢这个关于决策树内部运作的博客。有一点很清楚,这绝不是一项简单的技术。到目前为止,我只回顾了如何选择变量层次结构、如何构建树结构以及如何修剪的复杂性。即使在 Scikit Learn 中也有许多类型的决策树算法。其中包括:ID3、C4.5、C5.0 和 CART。对这些模型的探索将在另一个博客中进行。

参考文献

  1. G .詹姆士D .威滕T .哈斯蒂R .蒂布拉尼 (2013),《统计学习导论:在 R、斯普林格、 ( 2013 )中的应用
  2. Satyam Kumar 、【避免决策树过度拟合的 3 种技术】、走向数据科学https://towardsdatascience . com/3-避免决策树过度拟合的技术-1e7d3d985a09
  3. https://sci kit-learn . org/stable/modules/tree . html # minimal-cost-complex-pruning
  4. https://towards data science . com/learn-how-decision-trees-grown-22 bc3d 22 FB 51 #:~:text = Training % 20a % 20 decision % 20 tree&text = This % 20 approach % 20 makes % 20 the % 20 decision % 20 features % 20 from % 20 the % 20 data。
  5. https://www . science direct . com/topics/computer-science/Cost-complexity #:~:text = Cost % 2d complexity % 20 pruning % 20 is % 20 based,leaf % 20 of % 20 the % 20 subtree % 20 concerned。
  6. https://en . Wikipedia . org/wiki/Entropy _(information _ theory)

决策树、随机森林和梯度推进:有什么区别?

原文:https://towardsdatascience.com/decision-trees-random-forests-and-gradient-boosting-whats-the-difference-ae435cbb67ad

基于决策树的算法简介

简·侯伯Unsplash 上拍照

介绍

基于决策树的算法由于其效率和预测性能而非常受欢迎。

一个很好的例子就是 XGBoost ,它已经帮助赢得了许多 Kaggle 比赛。为了理解这些算法是如何工作的,了解决策树、随机森林和梯度推进之间的差异很重要。

因为这是一个更容易直观解释的话题,我在下面链接了我的视频,你也可以看看。

除了阅读这篇文章,你还可以看看我在 YouTube 上关于这个话题的视频。

决策树

西蒙·威尔克斯的照片

决策树是非常简单的预测工具。

基本上,一个决策树代表了你需要采取的一系列有条件的步骤来做决定。

让我们从一个非常基本的例子开始。

示例 1

比方说,我正试图决定是否值得购买一部新手机,下面有一个决策树来帮助我做出决定。

一个非常简单的决策树。(图片由作者提供)

从树顶开始,第一个问题是:我的手机还能用吗?

既然我的手机还能用,我们就顺着“是”的路径走,然后就完事了。我不买新手机。

现在我知道你在想什么:这个“决策树”几乎不是一棵树。因此,让我们来看一些更复杂的东西,比如下一个例子。

示例 2

稍微不那么简单的决策树。(图片由作者提供)

我们已经回答了第一个问题“是”,所以我们可以继续下一个问题:我的手机还有足够的内存来存储视频和照片吗?

我的回答是不。

因此,我们再次沿着“不”的路径,进入最后一个问题:我有足够的可支配收入来购买一部新手机吗?

我的答案是肯定的,所以最后的决定是买一部新手机。

你可以看到,如果我们真的想,我们可以继续添加问题到树中,以增加其复杂性。相比之下,我们也可以从树中删除问题(称为“修剪”)以使其更简单。

如上面的例子所示,决策树为制定决策提供了清晰的视觉效果。它们也很容易通过计算来构建。

然而,这种简单性带来了一些严重的缺点。

最主要的是过度拟合。这是当模型(在这种情况下,单个决策树)变得非常擅长为特定数据集(只有我)做出预测(决策)时,它在不同的数据集(其他人)上表现很差。

在我们的例子中,我们越往下走,我决定购买新手机的场景就越具体。通过增加几个问题,很容易使这个决策树变得过满,使这个树对我来说太具体,而对其他人来说太笼统

此外,决策树可能看起来非常不同,这取决于它开始时的问题。如果我们一开始就用不同的问题创建决策树,那么问题在树中的顺序可能会非常不同。

总之,尽管决策树很容易构建,但它本身并没有那么有用。只有一个决策树作为通用模型来进行预测是不理想的

这就是我们引入随机森林的地方。

随机森林

veeterzyUnsplash 上拍照

随机森林使用集体智慧的概念:当一组事物一起工作时出现的智慧和增强的能力。

顾名思义,随机森林独立构建了一堆决策树。每个决策树都是一个简单的预测器,但是它们的结果被聚合成一个结果。理论上,这应该更接近我们通过集体智慧寻找的真实结果。

正如我前面提到的,根据数据的不同,每个决策树可能看起来非常不同;随机森林将随机构造决策树,以尝试获得各种不同的预测。

随机森林的一个缺点是它们比单一的决策树更难解释。由于随机森林需要独立地构建和评估每个决策树,所以构建起来也比较慢。

梯度推进

照片由 Ivars UtinānsUnsplash 上拍摄

像随机森林一样,我们也有梯度推进。像 XGBoostCatBoost 这样的流行算法就是使用梯度推进框架的好例子。

本质上,梯度推进只是弱预测器的集合,它们通常是决策树。

随机森林和梯度推进的主要区别在于决策树是如何创建和聚合的。与随机森林不同,梯度提升中的决策树是附加构建的;换句话说,每个决策树都是一个接一个建立起来的。

然而,这些树并不是无目的地被添加的。每一棵新树都是为了改进先前树的不足而构建的,这个概念被称为增强

梯度提升的梯度部分来自于在算法构建每棵树时最小化损失函数的梯度。

如果这没有任何意义,那么现在不用担心。主要的一点是每次都添加一棵树来改进整个模型。这与随机森林形成对比,随机森林独立地构建和计算每个决策树。

随机森林和梯度增强之间的另一个关键区别是它们聚合结果的方式。在随机森林中,决策树的结果在过程结束时被聚集。梯度推进不会这样做,而是聚合沿途每个决策树的结果来计算最终结果。

总体而言,梯度推进通常比随机森林表现得更好,但它们容易过度拟合;为了避免这种情况,我们需要记住仔细调整参数。

结论

由于其效率和性能,梯度提升如今非常受欢迎。希望这篇文章能够澄清这些算法之间的一些差异。

这是初学者数据科学系列的一部分,所以如果你喜欢这篇文章,你可以在 YouTube 上查看我的其他视频。如果你想通过电子邮件了解我在做什么,你可以考虑注册我的简讯

原载于 2022 年 1 月 5 日 https://leonlok.co.ukhttps://leonlok.co.uk/blog/decision-trees-random-forests-gradient-boosting-whats-the-difference/

在 SQL 中声明变量

原文:https://towardsdatascience.com/declaring-variables-within-sql-fe6a479a7f9c

确保在 SQL 代码的开头声明关键变量有助于自动化代码的重用。

帕特里克·帕金斯在 Unsplash 上的照片

W 当开始使用 SQL 代码时,许多数据科学家会根据他们想要完成的任务来编写代码。有了创建的输出,用户就可以进入数据之旅的下一个阶段,这可能是数据可视化。努力创建一个样板代码,在代码的开头定义硬编码的变量值,确保在将来的使用中不会遗漏任何变量。

重新分解 SQL 代码以确保自动化任务可以有效地执行,这有助于更好地测试代码。反过来,在与其他利益相关者共享代码时,它也为用户提供了更多的信心。许多利益相关者可能不知道已经共享的 SQL 查询的内部工作方式,所以作者必须小心清楚地定义所有关键项。

在本文中,我们展示了如何将 declare 语句与一些基本变量一起使用。许多这样的例子将展示创建更简化的 SQL 代码的潜力。

资料组

对于这篇分析,希腊的地震数据集取自 Kaggle。

https://www.kaggle.com/nickdoulos/greeces-earthquakes

本文中显示的 SQL 代码是使用 Microsoft SQL Server Management Studio 18 的一个实例创建的。

入门指南

第一次使用 declare 语句时,用户会注意到它与其他编程语言的相似之处。每个变量最初都可以用特定的数据类型来声明,这可以确保用户键入适当的数据。设置新变量提供了将在随后的 SQL 代码中使用的第一个数据输入。

用于探索性数据分析(EDA)的 SQL 代码 1.1 简单声明和设置语句(图片由作者提供)

正如读者所见,在 declare 语句中,我们创建了两个日期变量和一个字符变量。start 变量的第一个 set 语句创建一个用户定义的日期值。通过包含可调整的日期值,用户可以快速查询数据集。但是,对于所有用户定义的值,都有可能出现键入错误。因此,必须小心确保提供适当的数据类型值。在代码中保留一个变量为最终用户提供了灵活性。

对于第二个日期变量 stop,我们使用一个内部关键字变量来提供今天的日期值。应用这个系统生成的变量允许 SQL 代码在用户不做任何调整的情况下工作。我们可以从 SELECT 语句中看到,函数 DATEADD 被提供了第二个日期变量。DATEADD 函数创建的是今天日期前三个月的月份值。

以这种方式创建变量值可以确保从查询结果中排除最近三个月的数据。有时,开发人员会将此操作纳入他们的数据分析中,因为最新的时间序列数据可能会在结果中包含一些波动性。这种易变性的含义可能与创建数据的团队没有完全验证数据有关。因为在最初的数据分析阶段,所有的数据源可能没有提供完全核实的输入。如果是这种情况,那么可以给数据源提供者分配时间来验证他们提供的数据。如果需要任何调整,则可以提供这些调整,以便适当制作管理信息报告。

SQL output 1.1 结果显示了使用日期过滤器的初始行(图片由作者提供)

包括一个声明的变量,该变量使用今天的日期,并在代码中对其进行调整,以将三个月前的值作为结束日期,这突出了如何安排该查询来自动创建输出。有了输出的结果,我们就能够共享数据的适当快照。

计数器

很多时候,当使用 SQL 代码时,我们希望了解我们在这个过程中进行到了什么程度。利用计数器可以提供最近阶段的概观。用计数器跟踪度量有助于确保我们总是知道所取得的进展,特别是如果程序有许多迭代要执行的话。

SQL 代码 1.2 基本计数器,用于检查已完成的代码阶段(图片由作者提供)

将计数器变量创建为整数数据类型允许用户以适合他们要完成的任务的格式设置变量值。通过取整数值零,用户可以迭代直到任务完成五个步骤。然而,如果用户更喜欢以降序查看计数器,则可以设置更高的值,例如 5,然后在每次迭代中递减。随着上面显示的计数器值的更新,有两种不同的方法可用。第一种方法显示了如何通过将计数器值加 1 来更新计数器变量。对于第二种方法,采用一种快捷方式来更新计数器变量。在每次迭代中,值会随着每种方法而增加。这些结果可以在下面看到。

SQL 输出 1.2 计数器的每次迭代都可以打印到结果日志中(图片由作者提供)

WHILE 循环

与上一节中的计数器类似,另一个可以使用的选项是 while 循环。正如在其他编程语言中看到的那样,while 循环为循环的工作提供了一个端点。在循环的每次迭代中,该函数检查以了解循环是否达到了最终迭代值。当没有到达最后一次迭代时,程序将继续执行所请求的任务。

SQL 代码 1.3 使用 while 循环创建计数器变量(图片由作者提供)

声明计数器变量时遵循与前面相同的步骤。变量被声明为整数数据类型。变量再次被设置为初始值。其中计数器变量需要连续复制代码来使用先前的计数器方法更新变量值。while 循环优化了代码,只在一行中包含了计数器变量的更新。代码循环部分的开始和结束包含两个关键字。

SQL 输出 1.3 使用 while 循环迭代计数器的结果(图片由作者提供)

使用 while 循环进行的演示会显示第一个和最后一个计数器值。

日期格式

在 SQL 代码的开头可以调整的另一个选项是决定使用哪种日期格式。开发人员可以使用许多日期格式选项,我们在这里不做介绍。对于这个例子,我们强调如何使用 set 语句来提供我们需要的日期格式。

SQL 代码 1.4 使用日期格式关键字(图片由作者提供)

该代码旨在检查并查看所声明的 date1 变量是否被 SQL 解释器视为 SQL 日期。通过在下面的输出中返回结果 1,我们能够确认情况确实如此。使用不同的日期格式将允许开发者改变输出日期类型以适应当地的期望。

日期评估的 SQL 输出 1.4 结果(图片由作者提供)

上面用红色下划线标出的代码表明 SQL 解释器还不知道这个值正在被创建。由于这段代码之前的 go 语句重新启动了解释器,因此先前对这个声明变量的了解就丢失了。要再次使用声明的变量,我们需要注释掉前面的 go 语句。或者,我们也可以再次创建声明的变量并提供不同的值。使用 SQL 解释器了解这些子句可以确保在不应该发生的时候不会出现潜在的错误。包含 go 关键字有助于将代码分成不同的部分,这取决于您的工作需求。

语言

到目前为止,我们已经通过声明公共变量来处理数据。另一个可用的选项是调整后台使用的语言。在处理日期时,我们可能需要使用该功能。在示例代码中,我们展示了两种语言之间的区别。使用语言选项创建不同的变量名有助于使用内置数据。

SQL 代码 1.5 通过设置语言选项来调整日期结果(图片由作者提供)

在使用每种语言选项时,代码都保持非常相似。这有助于显示自动创建的不同输出,而无需更改大量代码。我们还会看到如何使用 go 关键字来创建两个代码块。包含不同的初始日期值还会突出显示可以使用多少种日期格式。

SQL 输出 1.5 两种语言的区别(图片由作者提供)

拥有维护代码语法的能力有助于让开发人员的生活变得更加轻松。随着代码自动为月份名称和工作日生成相关的字符值,我们可以看到有多少选项可用于生成管理信息。

这些例子仅仅触及了使用 declare 和 set 语句属性时的皮毛。通过修改查询中的一些内部设置,开发人员可以更自由地使用相似的代码产生不同的结果。不必不断地创建大量相似的代码一直是人们的梦想。

结论

在本文中,我们已经完成了许多变量的声明。利用许多内置方法允许代码自动化。正是这种自动化可以通过设置调度处理流程来实现定期报告的复制。处理日期时,有许多日期格式可供使用。注意理解适当的日期有助于确保正确筛选数据。通过使用计数器或 while 循环,我们能够维护代码当前迭代的记录。调整 SQL 解释器使用的语言显示了如何使用相同的代码产生适合所需语言的结果。

留下您的评论,非常感谢您的阅读!

您可以使用下面的链接成为高级媒体会员,并访问我的所有故事和数以千计的其他故事:

https://jamesmcneill06.medium.com/membership

你可以在 LinkedIn 上联系我,友好地聊一聊所有的事情数据。我分享过的其他故事:

[1] : Kaggle dataset 许可来自https://www.kaggle.com/nickdoulos/greeces-earthquakes的地震,许可协议来自https://opendatacommons.org/licenses/dbcl/1-0/

使用 Python 整理您的 Gmail 收件箱

原文:https://towardsdatascience.com/declutter-your-gmail-inbox-with-python-e06a4102b0b7

使用 ezgmail 自动阅读和删除电子邮件

斯蒂芬·菲利普的照片Unsplash

介绍

不得不在 gmail 应用程序中手动删除一封又一封电子邮件绝对不是对时间的良好利用,尤其是当你可以自动化这个过程的时候。在这篇文章中,我将向你展示如何自动删除不想要的邮件,并将邮件标记为已读。这样,当你在做一件重要的事情或者看你最喜欢的电视节目时,你可以让一个程序来帮你做这件事😉。

就在不久前,我发现了由 Al Sweigart 创建的 ezgmail python 包,我认为与我以前见过的其他包相比,它的功能非常简单。

要求

  • 安装在您电脑上的 python 版本。如果您没有安装 python,请按照这里的说明来安装。
  • 您选择的 ide。Jupyter 笔记本,spyder,pycharm 等。
  • 安装 ezgmail 包,这可以通过在终端中运行pip install EZgmail来完成

设置您的 gmail 开发者帐户

您不能跳过这一步,否则 ezgmail 包将无法工作。这一部分很长,但是当你读到最后的时候,它是完全值得的。

  • 创建一个您将在其中工作的新文件夹。ezgmail_python是个不错的选择。
  • 点击此处并使用您的 gmail 帐户登录。
  • 按照页面上的步骤 1 运行:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

按照页面上的步骤 2 创建 quickstart.py 文件,并将步骤 2 中的代码添加到文件中。

  • 将 quickstart.py 保存在开始时创建的文件夹中。ezgmail_python文件夹
  • 不要像页面上说的那样运行 python quickstart.py
  • 如有必要,点击此处并登录。
  • 点击Select a project -> New Project -> Enter a Project name in the field provided(Eg: ezgmail)可以忽略位置字段
  • 点击CREATE
  • 从弹出的通知中选择您的ezgmail项目。您将被转到以下页面:

ezgmail 项目的仪表板

  • 在屏幕的左窗格中,点击APIs & Services->-OAuth consent screen,如下图所示:

选择 OAuth 同意屏幕选项

  • 从显示的 OAuth 同意屏幕中,点击external 选项,然后点击create ,如下图所示:

为用户类型选择“外部”选项

  • 接下来会出现一个编辑应用程序注册表单。填写以下字段:

— App 信息

  • App name = ezgmail
  • User support email =你自己的 gmail
  • App logo 可以忽略

—应用领域

  • 忽略此处的所有字段,因为它们不是必填字段

—授权域

  • 忽略此部分

—开发人员联系信息

  • Enter your own email here

单击保存并继续

忽略下一部分,它是关于你的应用程序的范围的。

单击保存并继续

对于下一部分,即测试用户部分,点击+Add Users按钮和add your own email address,点击添加

单击保存并继续

之后,你提供了你所提供的所有数据的摘要,如果一切正常,点击 BACK TO DASHBOARD

现在,点击屏幕左侧的Credentials 选项,您将看到以下内容:

“创建身份证明”页

点击上面的+CREATE CREDENTIALS选项,然后选择OAuth Client ID选项。从应用类型下拉列表中,选择Desktop App选项,您可以保留名称不变。点击CREATE。预期屏幕如下所示:

“创建 OAuth 客户机 ID”页

点击CREATE按钮后会出现另一个弹出窗口,显示您的凭证;你的Client IDClient Secret。确保不要与任何人分享这些。点击DOWNLOAD JSON按钮下载这些凭证。请务必将文件重命名为credentials.json,并保存在您开始创建的ezgmail_python文件夹中。

使用 ezgmail 包

我强烈建议下面的命令在终端中运行,而不是在你的 ide 中。

使用cd 命令进入你的ezgmail_python文件夹。运行以下命令:

python - >运行这个命令可以让您开始在终端中编写 python 代码

import ezgmail - >运行此程序后,浏览器中会打开一个窗口,要求您选择希望访问您的应用程序的电子邮件帐户。选择你想要清理的自己的电子邮件。然后你会看到另一个页面,告诉你这个应用程序没有通过验证,以及你是否要继续。点击continue

关于应用程序验证的谷歌警告页面

再次点击continue 以确保您允许 ezgmail 访问您的 google 帐户。有一个页面说:The authentication flow has completed.你也会意识到在成功认证后,你的 ezgmail_python 文件夹中有一个名为token.json的新文件。

命令在终端中运行

如果上面的 import ezgmail步骤不起作用,请尝试ezgmail.init()

嚄😅,你都准备好了!使用下面介绍的功能非常简单。准备好体验魔法了吗?

将邮件标记为已读

以下代码现在可以在您的 ide 中运行,而不是在终端中运行。

unreadThreads = ezgmail.unread(maxResults=300)
print(f'There are {len(unreadThreads)} unread emails in your account')
for unread in unreadThreads:
    print(ezgmail.summary(unread), ‘\n’)
    unread.markAsRead()

就这么简单!😲在上面代码片段的第一行,您可以将maxResults 参数更改为您选择的任何数字,因为它控制着返回的未读邮件的数量。从那里,收件箱中未读邮件的实际数量被返回并打印到您的屏幕上。第四行返回你的未读邮件的摘要,在最后一行,这些邮件被程序标记为已读。

删除邮件

对于删除电子邮件,你必须非常确定,然后再继续。下面的代码可以帮助你删除。

  • 通过查找关键字删除电子邮件
keywords = ['LinkedIn', 'Cesium Forum', 'ASOS']
for keyword in keywords: 
   threads = ezgmail.search(keyword, maxResults=300)
   print(f’There are {len(threads)} emails with the keyword,     {keyword}’)
   ezgmail.summary(threads) #You can check a summary just to make sure you’re not deleting anything important
    for i in range(len(threads)):
       if len(threads)==0:
          print(‘No \’{keyword}\’ messages to delete, moving on to the next’)
       else:
         threads[i].trash()

上面的关键词列表应该被你感兴趣的关键词所取代,它们可以是你想要的任意多的单词。使用 for 循环遍历关键字列表,ezgmail 搜索包含这些特定关键字的每封电子邮件,并返回它们。对于每一个关键词线索,在它们最终被删除之前,存在的电子邮件的数量被计数。

如果你意识到你删除了一些重要的东西,不要惊慌,你可以在 gmail 应用程序的垃圾箱中找到被删除的邮件。

  • 删除最近的邮件
threads = ezgmail.recent(maxResults=20)
print(f’There are {len(threads)} recent emails in your inbox’)
ezgmail.summary(threads) #You can check a summary just to make sure you’re not deleting anything important
for i in range(len(threads)):
     if len(threads)==0:
        print(‘No recent emails')
     else:
        threads[i].trash()

介绍了最近的功能和关键字的概念,它也可以用来标记电子邮件为已读,如果你认为这是一个更安全的选择。

离别的思绪

您还可以使用其他 ezgmail 功能,如re(用于正则表达式),它可以帮助您在继续标记为已读或删除之前,在电子邮件中查找特定模式。值得探讨。我希望在看完这篇文章后,你会采用一些清理收件箱的方法。感谢阅读!👋🏾

来源

Ezgmail 文档

GAN 中的基本数学解码—简化版

原文:https://towardsdatascience.com/decoding-the-basic-math-in-gan-simplified-version-6fb6b079793

生成性对抗网络的数学基础是什么?

在过去十年中,生成对抗网络(GANs)是深度学习领域的一场革命。自 Ian Goodfellow 等人在 2014 年发布第一个 GAN 版本以来,已经有许多 GAN 的变体,其中一些应用令人惊叹。

在本文中,我们将尝试用简单的术语来理解 GAN 背后的基本数学基础。

为了对 GANs 如何工作有一个高层次的直觉,建议您首先浏览下面的文章:对 GANs 的直观介绍

GAN 概述

甘输入输出流程(图片作者提供)

生成器将随机向量[z]作为输入,并生成输出图像[G(z)]。鉴别器将生成的图像[G(z)]或真实图像[x]作为输入,并生成输出[D]。

在鉴别器训练期间,生成器的权重和偏差被冻结。当鉴别器预测真实图像是假的或者假图像是真的时,它将受到惩罚。鉴频器的权重和偏置通过来自鉴频器损耗的反向传播来更新。

类似地,在生成器训练期间,鉴别器的权重和偏差被冻结。生成器的目标是欺骗鉴别器。因此,如果鉴别器能够识别出生成器生成的图像是假图像,则生成器会受到处罚。在这种情况下,损耗通过发电机网络反向传播,以更新其权重和偏差。

双人极大极小游戏

GAN 在概念上可以被认为是发生器模型和鉴别器模型之间的极小极大博弈。两个模型同时被训练,其中一个模型试图最小化损失,而另一个试图最大化损失。

根据原始 GAN 论文,GAN 的损失函数如下

图片来自甘— 2014 论文

让我们试着推导这个等式来更好地理解它。

二元交叉熵损失

生成器和鉴别器都使用二进制交叉熵损失来训练模型。一般来说,二元交叉熵损失可以写成:

鉴频器损耗

鉴别器可以有两种可能的输入,真的或假的。

  1. 对于实数输入,y = 1。在二元交叉熵损失函数中代入这个值,得到下面的损失值:

2.类似地,对于伪输入,y = 0,这给出了下面的损耗值:

结合鉴频器的上述损耗,一个是真实输入,另一个是虚假输入,得出以下鉴频器损耗

鉴别器的目标是最小化这种损失,

移除负号会将最小值变为最大值,因此单个数据点的最终鉴频器损耗可写为:

发电机损耗

对于发生器,损耗是根据鉴频器损耗计算的。在发电机训练期间,鉴别器被冻结。因此,对于鉴别器来说,只有一个输入是可能的,这就是伪输入。这使得鉴频器损耗等式中的第一项为零。

生成器试图欺骗鉴别器将假数据归类为真实数据。这意味着发生器试图最小化鉴频器损耗方程中的第二项。单个生成数据点的发电机损耗函数可写为:

GAN —损耗方程

将鉴频器损耗和发生器损耗结合起来,我们可以得到一个单一数据点的等式,如下所示。

这是发生器和鉴别器之间的极小极大博弈。发电机试图最小化损耗,而鉴别器试图最大化损耗。

将该方程推广到多个数据点的期望值(E)给出了如下的最终方程,

现在将其与 GAN 论文中给出的损失函数进行比较,以便更好地理解它。

图片来自甘— 2014 论文

GAN 的局限性

以下是 GAN 中常见的一些问题

  1. 消失梯度问题,由于该问题,发电机训练可能会失败
  2. 模型折叠,其中生成器可能重复创建相同的输出
  3. 无法收敛,因为鉴频器反馈对发生器的意义变得更小,从而影响其质量

结论

自问世以来,GANs 已经有了很大的改进,研究人员已经找到了在很大程度上克服这些局限性的方法。选择正确的损失函数是热门的研究课题之一,许多备选方案正在被提出和评估。

希望你喜欢这篇文章。尽情享受甘的各种风味吧。

分解特征分解

原文:https://towardsdatascience.com/decomposing-eigendecomposition-f68f48470830

像专业人士一样和 DoWhy 解除关系

原文:https://towardsdatascience.com/deconfounding-like-a-pro-with-dowhy-c7e03fa03a3f

因果分析帮助你解决很多关键的决策问题。许多开源框架都来帮忙了,微软的 DoWhy 是最有前途的一个。

Unsplash 上由 Nadir sYzYgY 拍摄的照片

在之前的一篇文章中,我提出了因果关系的理由,奠定了一些理论基础,并认为对因果机制的深刻理解应该成为每个数据科学家通过数据做出关键决策的工具。

因果推断分析通常需要绘制一张什么可能导致什么的图表,识别混杂因素,并对其进行分层,以发现治疗对结果的影响。这样做可以让你远离虚假的相关性和荒谬的主张。

图 1 —来自https://www.tylervigen.com/spurious-correlations。人造黄油主要消费量和人均消费量之间似乎有因果关系,但事实上并非如此。

因此,找到正确的混杂因素,即影响治疗和结果的因素,是解决因果关系的关键。

DoWhy 是最强大的库之一,由微软研究团队完全开源。5 月 31 日,亚马逊科学与微软联手创建了一个更广泛的因果分析生态系统,名为 PyWhy,增强了技术和数据科学界对简化该领域的巨大兴趣。下面是公告。

接下来,我将用一个真实的案例场景(出于显而易见的原因,使用假数据)来简要介绍它的潜力。

DoWhy 底漆

用例是衡量一个媒体公司中增加流媒体数量的活动的实际影响。假设不可能为一项试验抽取一个随机对照组,那么唯一可能的测量方法就是通过观察研究。我们可以通过比较目标群体与所有因为未同意联系而未包括在活动中的客户来衡量活动的结果。

第一步是将问题构建成有向无环图(DAG)。DoWhy 既支持 gml 又支持格式,我在这里使用的就是这两种格式。我初始化一个有向图,然后设置节点和边。我选择了一个平凡的用例,但是这些图会很快变得复杂。

用 Graphviz 打印图表后,我们可以识别两个混杂因素。社会人口统计聚类可能会影响治疗,因为一些群体可能更倾向于同意联系并参与活动,但也可能直接影响流的数量。对于活动前的流数量也可以进行同样的考虑。为了衡量治疗(活动)如何偶然影响接受治疗的客户的平均流媒体数量,有必要控制集群流媒体 _pre。在随意推理的行话中,这被称为阻塞从变量到结果的后门路径

图 2 —因果图。作者图片

在下面的步骤中,我为不同的客户使用不同的正态分布组合来生成一些假数据。在现实世界中,您几乎不会看到客户的行为可以用高斯钟形曲线来建模,但是对于这个示例来说,这就足够了。

上面的代码生成了下面这个非常简单的数据集。

图 3 —合成数据集。作者图片

在进行分析之前,我们可以观察到营销活动后的平均流差异为 2.5,但这一差异中有多大一部分归因于营销活动?

图 4 —活动后按客户划分的平均流。作者图片

有了手头的数据,我们现在可以从工具箱中取出 DoWhy 了。我通过传递用 Graphviz 定义的图来初始化因果模型(注意删除示例中显示的制表符和换行符),并指定目标和结果变量。

我们可以继续确定一个估计量,这是我们需要控制的变量,以便计算因果效应。这就是道威通过从上面定义的 DAG 推断来施展他的魔法的地方。

接下来的步骤是实际的评估,软件包提供了许多方法。我在这里选择了倾向得分分层,其中 DoWhy 计算了每个虚假客户的治疗倾向,然后根据该得分将每个客户分配到一个阶层,并计算目标群体和对照组之间在活动后的结果差异。这应该足以抵消假设的混杂因素的影响。我们基本上是在比较非常相似的群体,其中相似性是通过分享对活动的相同倾向来给出的。

目标单位是被治疗者的平均治疗效果(att ),因为在这种情况下,我只对参加活动的人的效果感兴趣。

最终的因果估计值是活动产生的平均 1.92 个流,低于最初分析的 2.5 个流。这两个值之间的差异可以解释为客户的惯性行为,不管活动如何,我们都会看到这一点。

图 5 —估计效果方法的输出。作者图片

对我们分析的正确性的最后检查是通过一个反驳测试,使用安慰剂治疗。这里我们用一个随机值代替治疗变量,重新计算效果。

然后,新效果的值应该为 0,其概率高于阈值(p 值)。在这种情况下会发生!

图 6 —反驳输出。作者图片

最后,我们可以通过倾向水平来形象化提升。DoWhy 向初始数据集添加了一些变量。特别是倾向分数和阶层。

图 7 —初始数据集的额外属性。作者图片

按阶层比较运动前治疗组和对照组的分组,我们可以看到橙色曲线如何在不同阶层一致地向上移动。

图 8。—按阶层划分的活动前流。作者图片

这为该活动如何影响所有倾向群体提供了一些额外的视觉洞察。

图九。—按阶层划分的活动后流。作者图片

我只是用一个小例子触及了解开因果关系的潜在可能性的表面。与 ALICE 项目(因果关系和经济学的自动学习和智能)的 EconMl 库的集成以及最近与亚马逊的合作极大地扩展了它的功能。看到因果关系将如何有助于向人工智能进军将是令人兴奋的。

注:本笔记本中的代码也可在本笔记本中找到:

数据科学家的装饰技巧

原文:https://towardsdatascience.com/decorator-tricks-for-data-scientists-87911eea41ca

如果您还没有使用 Python decorators,那么您应该使用。纯句法糖。

图片作者。

我记得我第一次在 Python 代码中看到函数顶部有一个“@”符号。我觉得有必要研究一下这个奇怪的语法。它前后都标了 a,这是肯定的。函数顶部的“@”符号称为装饰器,它装饰的函数的一个函数。

你可以花几年时间做数据科学家,而不用装饰者。或者也许你已经使用过它们,但是还没有学会如何编写自己的代码。这个故事旨在构建在许多数据科学任务中有用的具体 decorator 示例,并且在这个过程中,学习如何一步一步地编写 decorator。

装饰者的主要目的是:

  • 向现有函数和类添加功能
  • 当做一些可能不会通过代码审查的事情时,作为代码中的一个注释

这个故事将关注前者,向现有的函数和类添加功能。

为了说明这一点,假设您有许多查询数据库的函数。在编写了从数据库中查询所需的所有内容的函数之后,您意识到每十次尝试中就有一次会由于一些随机的连接错误而失败。我们都知道如何使用数据库。

您需要为所有函数添加一些重试逻辑。所述逻辑将形成为多次尝试的循环,其间可能有一些等待时间——样板代码。然而,基本的逻辑已经编码在函数中。

所以你有两个选择:

  • 向所有函数添加样板文件;这将需要工业规模的复制粘贴,并改变每个功能的逻辑以适应重试。又乱又臭的代码。
  • 编写一个重试装饰器,并将其添加到数据库函数之上(一行代码);否则,保持函数的逻辑不变。干净优雅的代码。

我当然知道我会选择哪个选项。

故事结构如下:

  • 装饰基础
  • 重试装饰器
  • 类注册表装饰器
  • 周期性执行装饰器
  • 最后的话

装饰基础

最简单的装饰器是一个函数,它将它所装饰的函数作为参数,并返回另一个函数,该函数反过来返回被装饰的函数应该返回的任何内容。绕口令。

通过例子更容易学习:

decorator_no_info 实际返回的是 _wrapper 函数。那是装饰者的本质;一个功能换一个功能。 _wrapper 函数接受修饰函数( func )接受的任何参数和关键字参数,并返回 func 返回的任何参数。在 _wrapper 中的一个附加打印显示代码块已经运行。

关于类型提示,RT(返回类型)的目的是表明无论装饰函数返回什么,装饰器都会返回。

让我们来看一个装饰者的例子:

打印:

这是装饰包装纸

2.0

_ 包装

这里我们看到 _wrapper 运行并返回了 test_deco 应该返回的内容。到目前为止,一切顺利。然而,修饰函数的名字已经改变了!是 _wrapper 而不是 test_deco

为了解决这个问题,我们使用了来自 functools 模块的包装装饰器。它将复制原始函数的信息,并将其传递给 _wrapper 函数:

现在,如果我们运行示例,我们会看到名称是正确的,修饰函数的参数被传递给了 _wrapper 函数。

打印:

这是装饰包装纸

2.0

测试 _ 装饰

这是装饰包装纸

5.0

测试 _ 装饰 _ 添加

接下来是一个可以接受参数的装饰器。在这种情况下,我们创建一个外部函数,它接受装饰器参数,并返回与没有参数的情况相同的装饰器:

例如:

版画:

这是 decorator _wrapper,deco_arg_str='foo '

5.0

测试 _ 装饰 _ 添加

按照预期工作,装饰器参数被传递给 _wrapper 函数。

重试装饰器

最后,第一个例子。这里我们实现了引言中讨论的重试修饰器。装饰器逻辑与上一节中更简单的例子相同。然而,这里我们将重试逻辑引入装饰器。

这个装饰器执行被装饰的函数,并返回它应该返回的任何东西,但是捕捉到一个定制的异常并重试 n_retry 次。每次重试,睡眠时间都会增加。

我们使用日志记录器来了解是否有重试尝试。记录器可以作为参数提供,如果没有提供,将创建一个新的记录器。

在这个例子中,我们想要捕捉值错误。它尝试了两次并记录了异常,但是没有崩溃;第二次之后,程序崩溃,因为异常被引发。

在本例中,该函数第一次引发了 ValueError (崩溃),因为我们想要捕获 RunTimeError

要捕捉所有异常(不包括系统退出的异常),使用异常作为异常重试,因为所有非系统退出的异常都是子类。

类注册表装饰器

第二个例子是类装饰器。是的,类也可以被修饰,不仅仅是函数。为了装饰一个类,装饰器返回一个函数,但是这个函数(在我们的例子中是 _wrapper )返回一个未实例化的类。

这个特殊的例子实现了一个类注册表。对于插件或者接口实现很有帮助。有时,我们直到运行时才知道对象的存在。

想想一个插件对象;该对象遵循一个接口,但是主程序不知道这些插件,也不需要它们运行。主程序加载任何可用的插件。因此,我们可以在不改变主代码的情况下添加任意数量的插件。

事不宜迟,下面是类注册装饰器的例子:

装饰器只接受注册表的名称作为参数。为了避免重复,注册表由注册表名和类名的元组索引。

此外,还有一个查询注册表的功能(这是主程序了解插件的方式)。

例如,让我们将这些类添加到注册表中:

我们查询注册表:

版画:

富(中国姓氏)

{'Foo': ,'巴兹': }

班级在那里。我们是黄金。

周期性执行装饰器

最后一个例子是周期函数装饰器。有时我们想在应用程序中的函数中添加一些调度逻辑。例如,HTTP 请求每隔 n 分钟获取一些数据。

因为这是重试修饰器的动机,我们不想通过添加调度逻辑来扰乱实际函数的逻辑。因此,我们创建了一个装饰器来为我们做这件事。更干净。

我们要求的唯一要求是装饰函数是 void,也就是说,它不返回任何值。我们将执行其中的代码,不关心函数的返回。

如您所见,我们使用线程模块在一个单独的线程中运行预定的功能,而不会阻塞主线程。这样,您可以运行该函数,并且调度循环不会阻塞:

打印:

在周期性的
foo 1656078663 . 629107
foo 1656078664 . 6331909
foo 165607865 . 6351948
foo 165607866 . 635

正如您所看到的,“after periodic”在其余的函数定期执行(即非阻塞执行)之前打印。

最后的话

这个故事是对装饰者和数据科学项目中的一些应用的快速浏览。希望您能编写更多自己的装饰器,并在代码中使用它们。我强烈建议您开始使用它们,直到您有信心在产品代码中使用它们。

喜欢这个故事吗?通过我下面的推荐链接成为一个媒体成员来支持我的写作。无限制地访问我的故事和许多其他内容。

https://medium.com/@diego-barba/membership

我希望这个故事对你有用。如果你想知道更多类似的故事,请订阅。

https://medium.com/subscribe/@diego-barba

通过融合人工智能(AI)和人类智能(HI)降低您的运营成本

原文:https://towardsdatascience.com/decrease-your-operational-costs-by-blending-the-artificial-intelligence-ai-human-intelligence-650e815ac785

提高您的机器人和代理性能的有效方法

AI-HI 更紧密的合作减少了运营成本和处理客户的时间。

安德里亚·德·森蒂斯峰在的照片

使用对话人工智能机器人技术是公司降低运营成本和建立长期公司的最佳方式之一。

C onversational AI 是机器学习的一个分支,它理解用户的查询并提供响应来解决他们的查询。然而,这个人工智能还没有达到能够解决复杂问题的状态,这些问题需要人类的技能、直觉和同情心来解决。因此,大多数公司现在意识到,要提供出色的客户体验,就必须用人工智能来增强基于对话式人工智能的聊天或语音机器人。

让人工智能站在最前沿,让人类代理作为后备,比只让单个代理降低了 30%的成本。

如果我们改善人工智能和人类代理之间的工作关系,我们可以更快地解决客户的查询,进一步降低成本,增加收入。

机器人和代理独立工作来解决客户的查询。

大多数公司现在都熟悉解决客户问题的“机器人和代理”设置。

一个对话式人工智能机器人作为第一个接触点,当它不理解查询时,它将转移到人类代理。这种转移通常是由驱动机器人的人工智能中的限制触发的,有时,业务规则被设置为自动路由到人类代理。业务规则通常是为产生高收入或需要人工智能无法推断的多个系统上下文的查询而设置的。这些规则可能要复杂得多,公司提供专用工具来配置这些规则。

机器人可以处理无限数量的对话,但在聊天世界中,一个人类代理通常会处理大约 6-10 次对话,而在语音世界中,一次处理 1 或 2 次。因此,通常情况下,当触发转移时,用户需要等待一段时间。由于聊天交互具有成本效益,大多数支持语音机器人的组织更喜欢转移到支持聊天的人工代理。

在传输过程中,bot 收集的信息将传递给代理,以便从中断的地方继续对话。这些信息不仅来自用户与机器人的交互,还来自用户交互的渠道、简档和历史。

正如我们在这些交互中看到的,机器人和代理在很大程度上是独立工作的。如果我们在机器人和人工代理之间引入更紧密的合作,可以节省更多的成本。

许多顶级聊天和语音机器人提供商正在试验机器人和人类代理之间更紧密的合作。这里有一些大多数 bot 供应商正在努力的事情,以带来更好的合作,并为企业家节省更多的成本。

这些是人工智能和人类智能更紧密合作的几种方式。

机器人协助人工代理解决用户的查询

出于各种原因,这些公司希望人工代理来处理高收入的查询。在这些情况下,机器人可以在许多方面帮助代理。

  • 自动填写回答
  • 基于对话历史显示上下文
  • 建议在哪里可以找到答案的知识文章
  • 自动调整文本以获得更多共鸣等

例如:当客户购买一幅画时,机器人可以通过显示适用于完成销售的各种折扣来帮助代理。

当机器人感到困惑时,人类代理会消除歧义

当机器人对用户的问题感到困惑时,代理可以消除特定查询的歧义,并指导机器人完成对话。例如,当用户键入一个长句子,而机器人没有经过处理这些长句子的训练时,人工代理可以快速消除这个查询的歧义,并使机器人回到正轨。这个人工代理消除了问题的歧义,并将其传递回机器人来处理对话的其余部分,从而节省了更高的成本。此外,用户不必排队等待查询结果。

机器人处理平凡或简单的任务

在对话被转移到代理之后,有许多普通的任务,例如收集支付信息、地址、基本个人资料细节等,可以被传递给机器人,并且代理一旦收集到这些信息就接管。

这种收集日常信息的过程不仅有助于代理更快地解决客户查询,而且有助于自动存储信息,以便以后可以利用。

自动调整代理对话

一种可能的未来解决方案是,当代理解析查询时,根据用户的对话风格,机器人可以自动调整代理响应以匹配这种对话风格。这种自动调谐有助于提供个性化的体验,有助于增加用户对品牌的参与度。

这是一项仍处于研究阶段的新时代技术,基于对话式人工智能的机器人提供商可能需要几年时间来采用这项技术。

总结

尽管人工智能机器人技术在过去十年里有了很大的进步,但处理复杂的查询仍然需要人类的智慧。大多数应用程序都有一个 bot 来帮助解决用户查询,当 bot 不处理查询时,它会将用户查询转移到人工代理。然而,当人工智能和人类智能有更紧密的合作时,这些应用程序的性能会变得更好。

[1]Smallbizgenius.net,丹妮卡·乔维奇

删除重复数据并清理数百万条位置记录

原文:https://towardsdatascience.com/deduplicate-and-clean-up-millions-of-location-records-abcffb308ebf

记录链接和地理编码相结合如何提高数据质量

拉夫(拉维)凯登在 Unsplash 上的照片

大公司出于不同的目的将数据存储在几个系统中(ERP、CRM、本地文件)。每一个都可能保存客户数据,并且不是所有的数据(如果有的话)都同步。此外,跨来源的链接要么不存在,要么没有得到适当维护。结果是重复记录、不一致和总体数据质量差。这是我们展示算法解决方案的绝佳机会。

这篇文章是关于具有地址属性的记录。我的建议在合理的时间内对数百万张唱片都适用。可能适用于大多数大型公司的主要用例是具有帐单或工作地点地址的客户记录。因此,我们将解决企业的以下棘手问题:

  • 我们如何消除每个客户数据源中的所有重复记录?我们如何将所有数据源中的记录联系起来,以对任何一个客户进行全方位总结?
  • 我们对每个地址记录的质量有多少信心?我们如何快速识别并修复无效或不完整的记录?

我的建议包括两个部分,记录链接和地理编码。这两个步骤的输出有助于加速不可避免的手工审查过程:我们从一百万条记录开始。然后,算法总结出一个可行的可能的质量问题清单,熟练的评审人员花几个小时(或几天)评估结果。

我对位置的算法记录链接的了解

这篇文章是关于有地址的记录。如果你的只有地址,没有别的,跳到下一部分。我下面的例子是关于客户位置记录——有名字的地址。同样的想法也适用于更复杂的情况,如数量、日期和时间等。,如合同记录。因此,假设我们要处理来自比荷卢三国的一大张客户位置表,下面给出了其中的 7 个位置。

有重复的位置记录的几个例子。这是人工生成的数据,灵感来自作者在真实世界用例中看到的记录(图片由作者提供)。

在最简单的情况下,如果所有相关属性都相同,则两条记录代表相同的实体。但这并没有考虑到拼写错误、语言或其他名称和地址的变化。因此,我们需要一种适用于单词和其他字符串的相似感(或距离感)。这就是记录链接有所帮助的地方,至少有十几个开源框架;参见本概述。我使用 Python 和 RecordLinkage 包来说明这个过程和关键的学习。我们从文本预处理开始,它可以在匹配质量上产生很大的差异。

首先,我们将国家标准化。对于索引阻塞来说,这是一个简单而重要的步骤,稍后将介绍。第二,我们使用了 RecordLinkage 的默认清理方法(全部小写,没有标点符号,编码等。).通过借鉴 NLP 社区的想法(和代码),我们可以做得更多。如果您想了解更多信息,请从 TextHero 开始:

例如,所谓的“停用词移除”,在我们的示例中,这可以翻译成移除诸如荷兰语“N.V .”的合法形式或诸如“酒店”的其他常用词(假设我们有许多酒店作为客户)。

记录链接可能是计算密集型。一百万条记录可能转化为一万亿对的比较。索引技术减少了候选对的数量,最简单的称为“阻塞”:只比较那些具有共同属性的候选对。我首选的阻止方式是地址所在的国家。这是质量最好的属性,或者至少修复起来很简单。如果按国家的索引阻塞导致太多的操作需要处理:在第二个高质量属性上结合排序的邻域索引,比如城市或邮政编码(或者客户名称,如果您没有选择的话)。

准备好候选人后,我们在下面的代码片段中定义如何度量他们的相似性。这个包提供了几个内置选项来测量单个字符串组件的相似性——参见 string 类的文档。Jaro-Winkler 非常适合(短)名称,它将更多的重要性放在字符串的开头附近。Levenshtein 对邮政编码更有意义。

相似性得分表(图片由作者提供)。

我添加了一个加权分数,权重基于直觉。这样的标量总结和阈值允许我们做出最终的“是”或“否”决定。或者,我们可以在一个相对较小的、平衡的标签样本集上拟合一个分类模型。但是在解释模型性能时要小心,因为在现实中,我们面临着一个极其不平衡的问题(非链接比链接多得多)。

通常有不止一个副本;有时,有几十个相同的实体。并且手动审查过程受益于并排拥有单个地址的所有可能副本。我用的是 Python 的 NetworkX 包:记录是节点,超过一个阈值的相似度是边。每个子图都是这样一个可能的副本或链接的集合。

原始数据由相似记录的聚类扩展(图片由作者提供)。

我们错过了将记录 1 和 2 放入它们的集群。我们可能会选择一个较低的阈值,但这有可能会给我们的输出增加假阳性。那么如何以编程方式选择阈值呢?下图说明了一个简单的解决方案。

使用 4 万对真实世界的位置记录比较创建了一个直方图。阈值(虚线)的选择试图“最好地分离”正确和不正确匹配的两个未知分布,假设它们大致是单峰和对称的(图片由作者提供)。

或者,你可以从分类文献中借用一个解决方案:知道混淆矩阵中所有四种情况的成本和收益,并估计它们作为阈值函数的频率。但是由于高度的不平衡,这将需要相对较大的一组标记的例子。

最后,我们为一个聚类中的每个记录添加汇总统计信息,以表明我们对匹配质量的信心。

对于分配给一个聚类的每条记录,我们计算与同一聚类中所有其他记录的最小、平均和最大相似性得分(按作者分类的图像)。

熟练的评审人员可以使用这些统计数据来排序并快速处理几乎完美的匹配,并将时间花在人工评审最重要的地方。

geoapify.com 如何帮助提高质量和丰富位置记录

地理编码是将地址转换为纬度和经度的过程。如果你处理的只是一小部分地址,有很多免费的服务——查看一下 GeoPy 。但是如果你的数据超过了,比如说,一千条记录,所有这些方法都是不可行的(并且可能是不合法的)。在现实世界中 1000 人还是很少的。即使你开始寻找像谷歌地图这样的商业提供商,你也会意识到他们要么不提供“批量”地理编码服务,要么价格昂贵。幸运的是,geoapify.com 的填补了这个空白。这还不是唯一的好消息:他们的网络服务利用了 openstreetmap.org 的生态系统。在内部数据和公开数据之间建立联系带来了位置数据质量之外的机会。

好吧,但当主题是数据质量时,我们为什么要讨论地理编码呢?首先,它是一种特殊的地址记录链接解决方案。实际上,我们甚至可以在前面的部分中使用它作为预处理步骤。但主要原因是该服务并不期待用户完美的搜索输入。nomist im(OpenStreetMap 的地理编码引擎)从搜索文本中提取要素,并应用评分逻辑来确定与已知位置记录的最佳匹配。最佳匹配以结构化的形式提供,包括地理坐标和几个置信度得分:街道、城市和总体置信度。这三个数字中的任何一个分数低都表明质量差,这有助于快速识别原始输入中的数据质量问题。

我们继续上一节的例子。如果你想复读,你必须在geoapify.com注册并生成你的密钥。广泛的免费层允许您每天免费对多达 6000 个地址进行地理编码。

批量地理编码服务接受字符串列表作为输入,每个地址一个字符串。我们将结构化的地址数据连接起来,请求批量地理编码,并将选定的输出属性解析到数据帧中。

geoapify.com 批量地理编码服务的输出被解析为数据帧。最后三列表示原始输入数据中的数据质量问题(图片由作者提供)。

该服务返回的不仅仅是地址。它还指示位置的类型。我们不期望地址 5 中的整个地区—原来的输入结果是一个邮政信箱。

结论和展望

公司通过有机增长或并购增长。他们的数据也是如此。通常,质量跟不上增长。本文提出了一种方法来加速清理杂乱的位置记录(客户的帐单地址、工作地点等)。).它从基于算法关联和地理编码的两步程序开始。这些算法可以很好地扩展到数百万条记录,并且根据我的经验,熟练的审核人员在使用输出时可以在非常短的时间内处理手动检查。

我们使用 geoapify.com 的批量地理编码服务来验证地址数据的质量。这只是他们的网络服务带来的众多机会之一。

  • 通过地理坐标丰富我们的数据允许我们将位置智能添加到我们可以用数据科学处理的许多问题中;参见这本关于空间数据科学的介绍的开源书籍。你处理客户流失吗?你有没有检查过附近的顾客是否也有离开的风险?
  • Geoapify.com 利用 OpenStreetMap 生态系统,该生态系统链接到维基数据。因此,我们可以将我们的内部位置记录与大量开源数据集联系起来。place_id 属性是每个地理编码输出的一部分。这能告诉我们更多关于地点的信息。同样,我们可以使用另一个名为 Place Details API 的 geoapify.com 端点来帮助我们完成这项工作。例如,使用地址 7 Hotel Astoria 的 place_id,我们得到更多的细节,例如到他们网站的链接和 Wikidata Id Q649690 。另一方面,Places API 可以告诉我们,在任何给定的地区,我们在客户数据库中遗漏了哪些酒店。

这是我第一篇关于位置智能和相关主题的文章。更多将很快跟进。

深度卷积 GAN——如何使用 DCGAN 在 Python 中生成图像

原文:https://towardsdatascience.com/deep-convolutional-gan-how-to-use-a-dcgan-to-generate-images-in-python-b08afd4d124e

神经网络

DCGAN 架构概述,以及使用 Keras / Tensorflow 构建该架构的分步指南

图片由52 赫兹发自 Pixabay

介绍

数据科学家将生成性对抗网络(GANs)用于广泛的任务,图像生成是最常见的任务之一。一种称为 DCGAN(深度卷积 GAN)的特定类型的 GAN 就是为此而专门开发的。

在本文中,我将解释 DCGANs,并向您展示如何使用 Keras/Tensorflow 库在 Python 中构建一个。然后,我们将使用它来生成盆景树的图像。

内容

  • 机器学习算法领域中的 DCGANs
  • DCGAN 架构及其组件概述
  • Python 示例向您展示了如何从头开始构建 DCGAN

机器学习算法领域中的 DCGANs

机器学习算法之间存在相似之处,这使我们能够根据架构和用例对它们进行分类。因此,我创造了下面的观想,帮助我们看到整个 ML 宇宙。

这张旭日图是交互式的,所以请点击探索 ML 类别👇在不同区域展示更多内容。

你会在神经网络分支生成对抗网络子类下找到 DCGANs。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的电子邮件。如果你不是中等会员,可以在这里加入https://bit.ly/36Mozgu

DCGAN 架构及其组件概述

首先让我强调一下,DCGAN 利用了卷积转置卷积层,并战略性地将其嵌入到 GAN 架构中。

我们将在下面的章节中熟悉这三个项目。然而,如果你希望看到更多的解释和例子,你也可以阅读我关于卷积神经网络转置卷积GANs 的深入文章。

卷积层

卷积是从数据中提取有意义信息的一种方式。它们特别适合处理图像,使网络能够有效地学习关键特征。

卷积有三个部分:输入(如 2D 图像)、滤波器(又称内核)和输出(又称卷积特征)。下面的 gif 展示了我们如何在一个 5 x 5 的图像上应用一个 3 x 3 的过滤器来创建一个 3 x 3 的卷积特征。

卷积在起作用。Gif 图片由作者提供。

神经网络可以包含多个卷积层和附加池层,以减小卷积特征的大小。

我们将在鉴别器模型生成器模型的最后一层中使用卷积层(在 GAN 部分有更多相关信息)。

转置卷积层

转置卷积也使用过滤器来处理数据,但它们的目标与常规卷积相反。也就是说,我们使用它们来上采样数据到更大的输出特征图,而常规卷积下采样

下面的 gif 说明了转置卷积是如何工作的。该示例使用步长 1,通过 2 x 2 滤波器从 2 x 2 输入移动到 3 x 3 输出。

转置卷积的作用。Gif 图片由作者提供。

我们将只使用发生器模型内的转置卷积(见下一节)。

DCGAN 架构

现在我们知道了什么是卷积和转置卷积,让我们看看它们如何适合 GAN 设置。

一个生成式对抗网络(GAN)将生成器鉴别器模型结合在一起,它们在零和游戏中相互竞争,使它们成为对手。

发生器制造假图像试图欺骗鉴别者相信它们是真的。同时,鉴别器学习图片的本质特征,以便区分*真假样本。*

下图说明了这两种模型在 DCGAN 架构中的交互方式。

深度卷积生成对抗网络。图片由作者提供。

如您所见,鉴别器模型只是一个卷积分类模型。相比之下,生成器模型更复杂,因为它学习在转置和规则卷积的帮助下将潜在输入转换为实际图像。

**

Python 示例向您展示了如何从头开始构建 DCGAN

理解 DCGAN 模型如何工作的最好方法是自己创建一个。在本例中,我们将训练一个 DCGAN 模型来生成盆景树的低分辨率(64 x 64 像素)图像。

请注意,该示例也适用于生成更高分辨率的图像。但是,分辨率越高,模型需要学习的参数(权重)就越多。因此,生成高分辨率图像所需的计算能力将显著增加。

设置

我们需要获得以下数据和库:

  • 加州理工学院 101 图像数据集(来源)

数据许可: 归属 4.0 国际(CC BY 4.0)

参考 :李,f-f,安德烈托,m,兰扎托,m . a .&,,P. (2022)。加州理工 101(版本 1.0)[数据集]。CaltechDATA。https://doi.org/10.22002/D1.20086

让我们导入库:

上面的代码打印了本例中使用的包版本:

*Tensorflow/Keras: 2.7.0
numpy: 1.21.4
sklearn: 1.0.1
OpenCV: 4.5.5
matplotlib: 3.5.1
graphviz: 0.19.1*

接下来,我们下载、保存和摄取加州理工学院 101 图像数据集。我们将只使用盆景树的图像(类别=“盆景”),而不是 101 个类别的完整列表。此外,我们将设置分辨率为(64 x 64)像素。

上面的代码打印出我们数据的形状,是【样本,行,列,通道】

*Shape of data_lowres:  (128, 64, 64, 3)*

让我们展示一些低分辨率图像,看看我们将在哪些方面训练我们的模型。

训练数据中的低分辨率图像。来自加州理工学院 101 的原始图像数据。组合图片由作者提供。

最后,让我们将当前范围为[0,1]的图像输入数据缩放到范围[-1,1]。我们这样做,所以我们可以在发生器输出中使用 tanh 激活函数,因为它通常会产生更好的结果。

然而,在发生器输出中使用 sigmoid 激活函数也很常见,这不需要您进一步缩放图像。

创建 DCGAN 模型

完成数据准备后,让我们定义和组装我们的模型。我们将从发电机开始:

发电机模型图。图片由作者提供。

我们从一个 100 节点的潜在向量开始,在将其重新整形为 8 x 8 x 128 之前,我们将其连接到 8192 节点的密集层。然后,我们通过转置卷积将数据上采样为 64 x 64 的输出。

请注意,我们还在输出层使用常规卷积,因为我们将过滤器从 512 个减少到只有 3 个,代表不同的颜色通道。

接下来,让我们定义一个鉴别器模型:

鉴别器模型图。图片由作者提供。

如果您将它与生成器模型进行比较,您会注意到鉴别器模型做的正好相反。即,它获取 64×64 的图像,并将其通过多个卷积层,以将其简化为真/假的二进制分类输出。

接下来,我们将这两个模型结合起来创建一个 DCGAN。下面代码中的一个关键细节是我们使鉴别器模型不可训练。我们这样做是因为我们希望使用真实和虚假(生成)数据的组合来分别训练鉴别器。稍后您将看到我们是如何做到这一点的。

DCGAN 模型图。图片由作者提供。

为发生器和鉴频器准备输入

我们将创建三个简单的函数来帮助我们为这两个模型采样/生成数据。

第一种方法从训练数据中抽取真实图像,第二种方法从潜在空间中抽取随机向量,第三种方法将潜在变量传递到生成器模型中以生成假样本。

模型训练和评估

最后两个函数将帮助我们训练模型,并在指定的时间间隔评估结果。

让我们首先创建模型性能评估函数:

正如您所看到的,上面的函数分别在真实和虚假(生成)点上评估鉴别器。此外,它还展示了一些模型生成的图像。

注意,为了显示假的(生成的)图像,我们需要将底层数据从[-1,1]范围逆变换到[0,1]范围。

最后,培训功能:

如前所述,我们通过传递一批 50%真实和 50%虚假(生成)的样本来分别训练鉴别器。同时,发电机训练通过组合的 DCGAN 模型进行。

结果

让我们调用训练函数来训练模型并显示一些结果。

*# Train DCGAN model
train(gen_model, dis_model, gan_model, data, latent_dim)*

以下是在对模型进行 2000 个纪元的训练后生成的一些假图像:

DCGAN 在 2000 个训练时期后生成盆景树图像。图片由作者提供。

3000 年后又多了一些:

DCGAN 在 3000 个训练时期后生成盆景树图像。图片由作者提供。

正如你所看到的,一些图像很糟糕,而另一些看起来很艺术。然而,很明显需要更多的训练。

结束语

我鼓励你用我的例子做进一步的实验,并分享你的结果。我敢肯定,通过一些额外的培训或替代模型参数,您可以获得更好的结果。

如果你想收到我即将发表的关于其他类型 gan 的文章,请用你的电子邮件订阅,我一发表这些文章,它们就会马上到达你的收件箱。或者,请随意浏览我已经在 Medium 上发布的关于机器学习的 40 多篇文章。

最后,你可以在我的 GitHub 库 上找到本文使用的完整 Python 代码作为 Jupyter 笔记本。

干杯!🤓
索尔·多比拉斯

如果你已经花光了这个月的学习预算,下次请记得我。 我的个性化加盟链接媒介:

*https://bit.ly/3J6StZI *

使用随机森林深入 HPLBs 进行 A/B 测试

原文:https://towardsdatascience.com/deep-dive-into-hplbs-for-a-b-testing-using-random-forest-11f0fdd73044

测试中 p 的替代值

叶戈尔·迈兹尼克Unsplash 上拍摄的照片

在最近的一篇文章中,我们基于我们关于 arXiv 的文章介绍了电视距离的高概率下限(HPLB)的概念:

https://arxiv.org/abs/2005.06006

洛里斯·米歇尔的合作。

在当前的文章中,我们深入到这个主题的详细处理,并且顺便触及一些非常有用的(并且漂亮的)统计概念。特别是,我们需要从一个装有 m 个球和 n 个方块的瓮中抽取球,而不需要更换。非常感谢 Maybritt Schillinger 的大量建设性意见!

大纲

一段时间以来,人们已经知道强大的分类器(如随机森林分类器)可以用于双样本或 A/B 测试,如这里的所解释的:我们观察两组独立的样本,一组来自分布 P (如治疗前的血压),另一组来自分布 Q (如治疗后独立人群的血压),我们想要测试的是H _ 0:P = Q给定这两组数据,我们给一组 0 的标签,给另一组 1 的标签,训练一个分类器,然后在一些独立的数据上评估这个分类器。那么很直观的是,分类器越能区分这两个组,就有越多的证据反对零。这可以是正式的,当 p 值小于预先指定的α时,导致有效的 p 值和拒绝决定。

这很好,因为今天的分类器是强大的,因此这种方法导致强大的双样本测试,可以潜在地检测出 PQ 之间的任何差异。另一方面,我们都听说过 p 值和经典测试的问题。特别是,显著的 P 值不会告诉一个P 和 Q 有多不同(这与医学中的效应大小有关)。下图展示了一个例子,其中 PQ 的差异越来越大。在每种情况下,即使强有力的双样本测试也只会给出二元拒绝决定。

因此,如果我们能够以某种方式有意义地计算出 P 与 Q 的不同程度,这将会更加有趣,理想的情况是在这个过程中仍然使用一个强大的分类器。这里,我们基于对 PQ 之间的电视距离的估计来构建一个有意义的方法。

p(红色)和 Q(蓝色)的差异越来越大。一个测试,无论多么强大,都只是拒绝而没有给出额外的信息。来源:作者。

在下文中,我们假设观察来自 P 的内径样本 X_1,…,X_m 和来自 Q 的独立内径样本 Y_1,…,Y_n 。然后,我们使用分类器的概率估计值(例如,属于类别 1 的概率)作为“投影”,该“投影”采用数据向量并将它们作为概率估计值映射到真实线上。使用这些值构建单变量顺序统计量,并找到与 TV(P,Q) 的联系,然后我们将能够构建我们的下界。在下文中,我们有时也会将 TV(P,Q) 写成λ

使用(强大的)分类器得到单变量问题

本节中的概念:用 m 个圆和 n 个正方形从瓮中画出没有替换的圆:使用超几何分布进行双样本测试。

一般来说,来自 PQ 的样本是 d 维随机向量。这里,分类器已经开始发挥作用:由于大多数分类器可以获取这些 m+n 个样本点,并将它们转换成一个实数序列,所以对观察值 i 具有标签 1 的概率的预测。因此,我们可以只关注 0 和 1 之间的实数来构造我们的估计量。当然,我们真正的下限是概率估计的电视距离。因此,重要的是分类器要强大,以确保我们不会丢失太多信息。

所以,让我们假设我们有一个 N=m+n 实数的样本,并且我们知道这些实数中的每一个的原始观察值是来自 P 还是 Q 。然后我们就可以构建这个神奇的东西叫做顺序统计量。也就是说,我们取 N 个数字,从最小到最大排序。为了说明这一点,让我们将来自 P 的样本表示为圆形,将来自 Q 的样本表示为方形。那么订单统计可能如下所示:

现在这里是重要的一点:分类器试图尽可能精确地估计一个观察值属于第 1 类,或者来自 Q,或者是正方形的概率。因此,如果分类器是好的,我们应该期望在右边看到更多的正方形,因为正方形的估计概率应该比圆形大!因此,顺序统计就像一个离心机:如果在 PQ 之间有一个可辨别的差异,并且分类器能够检测到它,概率的顺序统计将圆圈推向左边,将方块推向右边。由于仍然存在随机性和估计误差,这通常看起来并不完美。然而,我们希望它“充分不同于随机性”。

量化这一点的一个非常优雅的方式是我们称之为 V_z、z 下面的圆的数量的统计。这个统计量已经被用于(单变量)测试很长时间了。也就是说,在任意一点 z=1,…,N ,我们简单地数一数 z 下面有多少个圆:

如果 PQ 相同,我们应该期待什么?在这种情况下,我们只需从单个分布中提取 N 个独立同分布。因此,在顺序统计中,应该只有圆形和方形的随机排列,没有模式。在这种情况下, V_z 实际上是以均匀的概率从一个有 m 个圆和 n 个正方形的瓮中画出没有替换的圆。因此,这又回到了概率的基础。数学上:

参见例如这篇好看的文章

在 H_0: P=Q 下, V_z 是超几何的:它是你从一个有 m 个圆和 n 个正方形的骨灰盒中画出 z 乘以而没有替换的次数。

很酷的是,我们现在甚至能够在 zq(z,alpha),中找到任何 alpha,的函数,这样当 P=Q 时:

找到这个 q(z,alpha) 可以通过使用渐近理论(例如参见我们的论文和其中的参考文献)或者简单地通过模拟来完成。主要的一点是,我们知道 V_z 的分布,并且它总是相同的,不管 PQ 到底是什么。因此,即使我们没有最大值的封闭分布,我们仍然可以很容易地逼近 q(z,alpha) 。这个可以直接用于一个(单变量)双样本检验!如果 max_z V_z-q(z,alpha) 过零,我们可以拒绝 P=Q

好的,这很好,但是这篇文章的要点是,我们想要摆脱简单的拒绝决策,而是得到总变化距离的下限。不幸的是,在 PQ 不同的一般情况下(即 TV(P,Q) > 0 ),不再知道 V_z 的分布!现在的目标是找到另一个更容易分析的过程 B_z ,并且对于所有的 z=1,…,N, B_z ≥ V_z 。如果我们能够正确地限制这个过程,那么这个限制也适用于 V_z

玩电视(P,Q)

本节中的概念:使用 TV 的抽样解释来引入分布见证的概念,并使用它来确定 P=Q 成立的区域,即使 P 通常不等于 Q。

通过在上一节中找到 q(z,alpha) ,我们基本上找到了当 TV(P,Q)=0 时,即如果 PQ 之间没有差异,则构造下界的第一步。我们现在通过联系我们的第一篇文章来扩展这一点,并摆弄一下 PQ 之间的电视距离的定义。一般来说,这是给定的

因此,我们从所有可能的集合中寻找集合 A ,使得 P(A)Q(A) 之间的差异最大。让我们做得更具体一些:让 pqPQ 的密度(作为一个技术细节,数据不需要通常意义上的连续,我们可以总是这样做)。那么最大值 A 被给定为

以便

让下面的 X 有分配 PY 分配 Q 。现在关键的部分来了:我们可以用这个来定义一个新的密度

那么这是一个有效的密度(积分为 1),我们可以类似地定义一个密度 q_+ 。这意味着什么是最好的图形:

电视概念的图解。左图:两个原始密度 p 和 q,红色的 p_+右图:密度 p_+为红色,h 为蓝色,q_+为绿色。来源:作者

该图显示,密度 pq 可以分解为密度 p_+q_+、和一些中间部分,这对应于两个密度的最小值,如果我们用 1-TV(P,Q) 将其标准化,则正好积分为 1:

我们现在可以看到从混合物中提取的 X ,而不是直接从 PY 中提取的 X

这意味着以下内容:在我们画出 X 之前,我们抛硬币,用概率 TV(P,Q) ,我们从红色密度 p_+ 中画出 X ,用概率( 1-TV(P,Q) 中画出 h 。对于 X 的分布,我们如何看待它并不重要,最终, X 将具有分布 P. 但是很明显, X 实际上是来自于 p_+ 还是来自于 h ,因为前者对应于 p 的“唯一”部分。实际上,无论是从图形还是密度本身来看,我们都会看到p _+所以 X 要么来自 p_+ 要么来自 h ,同样, Y 要么来自 q_+ 要么来自 h 。最关键的是,如果 YX 都是从密度 h 中抽取的,那么它们显然来自同一个分布,没有办法区分它们,这就好像我们在零下面。

因此,对于 i.i.d .观察值 X_1、…、X_mY_1、…、Y_n、而言,每个观察值要么来自特定部分( p_+q_+ ),要么来自联合部分 h 。我们把从特定部分 p_+(q_+) 得出的观察结果称为 P (Q)的见证。

从特定部分 p_+得出的观察称为见证(对于 P)。从 h 得出的观测值不能微分,所以这对应于 P 和 Q 相同的部分。

好的,如果我们回到订单统计,我们现在可以这样想:

带有蓝色十字的圆圈对应于来自 P 的证人,而带有蓝色十字的方块对应于来自 Q 的证人。基本上从划掉的观测值中我们可以了解到一些关于 P,Q 的差异,而没有划掉的观测值基本上来自零。从某种意义上说,所有这些只是一个思想实验——我们无法知道 X_i 是来自 p_+ 还是 h 。所以我们不知道哪些点是证人,其实我们连有几个都不知道。尽管如此,这个思想实验将有助于构建我们的估计量。

下面我们将为 TV(P,Q)提出一个候选者,比如说 lambda_c ,然后检查这个候选者是否符合条件。如果是的话,我们选择一个新的候选值 lambda_c 高于旧的候选值,并再次检查条件。我们这样做,直到λ_ c违反条件。

让我们做些清洁吧

本节中的概念:以高概率限制见证的数量,并使用直观的“清理操作”来获得行为更好的进程 B_z,它总是大于或等于 V_z。

我们现在要用这个思路,一些点是见证点,另一些点来自于 P=Q 的部分对于上面提到的统计 V_z. ,我们其实并不知道哪些点是见证点!那没问题,我们需要的实际上只是见证人的数量,尽管我们甚至不知道。但是,我们可以为这个数找到一个上界

回想一下,我们假设 X_1,…,X_m 是通过从 p_+ 中以概率 TV(P,Q)和从 h 中以概率 1-TV(P,Q) 抽取的进行采样的。所以在 m 观察值中的见证人数量,表示为 w_p ,实际上遵循一个成功概率 TV(P,Q) 的二项分布。因此,如果我们有一个候选人 lambda_c ,我们怀疑这应该是真实的电视距离,证人的数量应该遵循分布

这仍然是随机的,因此我们不知道给定样本的确切结果。但是因为我们知道分布,我们可以找到一个更高的分位数 W_p ,这样 w_p 以小于α/3的概率超过这个分位数。例如,对于 m=10lambda_c=0.1 ,这可以被发现为

lambda_c<-0.1
m<-10
alpha<-0.05

W_p<-qbinom(p=alpha/3, size=m, prob=lambda_c, lower.tail=F)

# test
w_p<-rbinom(n=3000, size=m, prob=lambda_c)
hist(wp,main="")
abline(v=W_p, col="darkred")

我们可以为证人做同样的事情。

所以我们有了一个候选者 lambda_c ,基于这个候选者,两个值 W_pW_q 以高概率绑定 w_pw_q 。特别是, W_pW_q 直接依赖于 lambda_c,所以实际上最好是写 W_p(lambda_c)W_q(lambda_c),但是那样会过分夸大符号。

为了获得新的进程 B_z ,我们首先按照上面的顺序统计组成新的见证:

红色方块现在表示我们指定为见证的随机点。这样做是为了使见证人的数量与上限 W_pW_q 相匹配。这在我们的上下文中是可以的,因为它实际上并没有改变 V_z。

现在我们执行我们的清洁操作。这将使我们从 V_zB_z ,保证 B_z 至少与 V_z 一样大。我们从左到右和从右到左查看订单统计数据。一、从左到右,每次看到一个没有十字的圆,从右边的 P (有十字的圆)中随机选择一个见证人,放在空圆之前。我们这样做并没有改变没有十字的正方形和圆形的顺序,就像这样:

第一个圈子已经是一个见证,所以我们让它保持原样。第二个圈是非等待圈,所以我们随机将一个见证圈从更远的地方移到之前的位置。接下来是一个没有见证的正方形,所以我们从它的右边移动了一个圆形见证,以此类推。整个想法就是简单地将所有见证人从 P 移动到左边,将所有见证人从 Q 移动到右边,而不改变非见证人自身的顺序:

现在 B_z 正在计算在这个新的排序中属于 P 的低于 z=1,…,N 的观测值的数量!注意,对于第一组观察值来说,B_z 只是线性增加 1。然后是中间部分,其中 B_z 的行为类似于超几何过程:

最后,最后几个观测值只是平方,所以 B_z 的值刚好达到 m 并停留在那里。

## Using the function defineB_z below

## Define n + m and confidence alpha
n<-50
m<-100
alpha<-0.05

# Define the candidate
lambda_c <- 0.4

plot(1:(m+n),defineB_z(m,n,alpha,lambda_c), type="l", cex=0.5, col="darkblue")

for (b in (1:100)){

  lines(defineB_z(m,n,alpha,lambda_c), col="darkblue")

} 

关键是我们把所有的圆圈都比以前更向左移动了!这就是为什么 B_z 总是大于(或等于)V _ z。特别是,对于λ= 0,我们期望 W_p=0 ,因此B _ z = V _ z。

library(stats)

defineB_z <- function(m,n,alpha,lambda_c){

## Upper bound witesses for the given m, n, alpha and lamba_c

W_p<-qbinom(p=alpha/3, size=m, prob=lambda_c, lower.tail=F)
W_q<-qbinom(p=alpha/3, size=n, prob=lambda_c, lower.tail=F)

B_z<-matrix(0, nrow=n+m)

# First part: B_z=z
B_z[1:W_p,] <- 1:W_p

# Last part: B_z=m
B_z[(m+n-W_q):(m+n),] <- m

# Middle part: Hypergeometric
for (z in (W_p+1):(m+n-W_q-1) ){

  B_z[z,]<-rhyper(1, m-W_p, n-W_q, z-W_p)+W_p

}

return(B_z)
}

把所有的放在一起

本节中的概念:使用上面的方法得到 B_z 上的一个边界,从而得到 V_z 上的一个边界,并得到我们可以使用的 HPLBλ。它是通过一个下确界定义的,为了找到它,我们需要遍历几个候选项。

接下来,给定λ= TV(P,Q) ,我们要找到一个 Q(z,α,λ)它具有

这将用于在一秒钟内定义最终估计值。至关重要的是, Q(z,alpha,lambda_c) 需要为任何 lambda_c 定义,但概率语句只需要在真候选 lambda_c=lambda 处为真。所以这是我目前关注的“候选人”。

从上面我们知道,对于第一个 W_p 值, B_z 只是线性增加。所以 B_z=z 我们也可以设置 Q(z,alpha,lambda)=z ,对于 z=1,…,W_p 。类似地,另一方面,当所有 m 个圆都被计数时,我们知道 B_z=m ,因此我们可以为所有的 z=m+n-W_q,…m +n 设置 Q(z,alpha,lambda)=m 。(记住,在每种情况下,λ= TV(P,Q) 通过 W_pW_q 进入。)

剩下的是中间的部分,其行为就像在空值下一样。对于 z=W_p+1,…,m+n-W_q 也是如此。但是由于这里的 B_z 又是超几何的,我们可以使用与上面相同的 q 函数来得到

我们需要的 alpha/3 ,因为我们可能会对 W_pW_q 犯错误,也就是说,有一个 alpha/3 的机会我们没有高估。

所以我们考虑了所有情况!对于 z=1,…,W_p,B_z-Q(z,alpha,lambda)=0 ,同样适用于最后的W _ Q zs,中间部分最终被上述等式覆盖。但是由于 B_z 大于 V_z ,我们也有

使用这个 Q 函数,我们可以将最终估计量定义为

这看起来很恐怖,但它的意思只是从 lambda_c=0 开始,你(1)计算 W_p(lambda_c),W_q(lambda_c) ,从而 Q(z,alpha,lambda_c) ,并且(2)检查是否

是真的。如果是,可以增加 lambda_c 一点点,重复步骤(1)和(2)。如果不是真的,你停下来把估计器设置为 lambda_c

从数学上讲,为什么估计量的 inf 定义有效?这仅仅意味着λhat是最小的λc这样

是真的。所以如果真的λ(= TV(P,Q)) 小于那个最小值(我们的估计量),这个条件就不成立,反而上面的> 0 条件成立。但是上面我们刚刚看到,这个> 0 条件有一个发生的概率≤α,所以我们没事。

所有这些都可以在克兰的 HPLB 计划中找到。下一节给出两个例子。

一些例子

这里,我们在两个例子中使用上一节中得到的估计量。在第一个例子中,我们使用属于类别 1 的概率的随机森林诱导估计,如上所述。在第二个例子中,我们实际上使用了一个回归函数,表明我们可以概括这里讨论的概念。

在第一篇文章中,我们已经研究了下面的例子

library(mvtnorm)
library(HPLB)set.seed(1)
n<-2000
p<-2#Larger delta -> more difference between P and Q
#Smaller delta -> Less difference between P and Q
delta<-0# Simulate X~P and Y~Q for given delta
U<-runif(n)
X<-rmvnorm(n=n, sig=diag(p))
Y<- (U <=delta)*rmvnorm(n=n, mean=rep(2,p), sig=diag(p))+ (1-(U <=delta))*rmvnorm(n=n, sig=diag(p))plot(Y, cex=0.8, col="darkblue")
points(X, cex=0.8, col="red")

在上面的模拟中, delta 参数决定了 PQ 的差异程度,从 delta=0 ,其中 P=Q ,到 delta=1 ,其中 P 是一个均值为(0,0)的二元正态,而 Q 是一个均值为(2,2)的二元正态。即使是强有力的双样本测试也会简单地拒绝所有这些情况。通过我们的方法,使用随机森林概率估计,我们得到

#Estimate HPLB for each case (vary delta and rerun the code)
t.train<- c(rep(0,n/2), rep(1,n/2) )
xy.train <-rbind(X[1:(n/2),], Y[1:(n/2),])
t.test<- c(rep(0,n/2), rep(1,n/2) )
xy.test <-rbind(X[(n/2+1):n,], Y[(n/2+1):n,])
rf <- ranger::ranger(t~., data.frame(t=t.train,x=xy.train))
rho <- predict(rf, data.frame(t=t.test,x=xy.test))$predictions
tvhat <- HPLB(t = t.test, rho = rho, estimator.type = "adapt")
tvhat

正如我们所希望的:当分布相同时,下限为零(即隐式测试不能拒绝),并且随着 PQ 变得更加不同而逐渐增加(即 delta 增加)。

我们还可以看一个更一般的例子。假设我们观察到一个(或多或少)独立的样本,但是在中间有一个均值偏移:

在本例中,让我们将观察值的索引视为时间 t,从左到右,我们从时间 t=1 移动到 t=1000 。代码如下。我们现在可以在每个感兴趣的点(例如每个采样点)检查左侧的P=点和右侧的Q=点之间的电视距离有多大。这可以通过再次使用每个 t 的概率估计来完成,但是为了加快速度,我们改为对观测值 z_t. 使用时间 t 的回归,也就是说,我们检查观测值是否给我们一个指示,即它是更偏向左边还是右边。

下图显示了红色的 true TV 和黑色的 HPLB:

可以看出,当我们在图中从左向右移动时,该方法很好地检测到电视距离的增加。然后在分布发生变化的点达到峰值,表明主要变化发生在那里。当然,从下面的代码中可以看出,我在这里作弊了一点;我将整个过程生成了两次,一次用于训练,一次用于测试。一般来说,在这个例子中,人们必须更加小心地选择训练集和测试集。

重要的是,在我们计算电视距离的每一点,我们隐式地计算一个双样本测试。

library(HPLB)
library(ranger)
library(distrEx)

n <- 500
mean.shift <- 2
t.train <- runif(n, 0 ,1)
x.train <- ifelse(t.train>0.5, stats::rnorm(n, mean.shift), stats::rnorm(n))
rf <- ranger::ranger(t~x, data.frame(t=t.train,x=x.train))

n <- 500
t.test <- runif(n, 0 ,1)
x.test <- ifelse(t.test>0.5, stats::rnorm(n, mean.shift), stats::rnorm(n))
rho <- predict(rf, data.frame(t=t.test,x=x.test))$predictions

## out-of-sample
tv.oos <- HPLB(t = t.test, rho = rho, s = seq(0.1,0.9,0.1), estimator.type = "adapt")

## total variation values
tv <- c()
for (s in seq(0.1,0.9,0.1)) {

  if (s<=0.5) {

    D.left <- Norm(0,1)
  } else {

    D.left <- UnivarMixingDistribution(Dlist = list(Norm(0,1),Norm(mean.shift,1)),
                                       mixCoeff = c(ifelse(s<=0.5, 1, 0.5/s), ifelse(s<=0.5, 0, (s-0.5)/s)))
  }
  if (s < 0.5) {

    D.right <- UnivarMixingDistribution(Dlist = list(Norm(0,1),Norm(mean.shift,1)),
                                        mixCoeff = c(ifelse(s<=0.5, (0.5-s)/(1-s), 0), ifelse(s<=0.5, (0.5/(1-s)), 1)))
  } else {

    D.right <- Norm(mean.shift,1)
  }
  tv <- c(tv, TotalVarDist(e1 = D.left, e2 = D.right))
}

## plot
oldpar <- par(no.readonly =TRUE)
par(mfrow=c(2,1))
plot(t.test,x.test,pch=19,xlab="t",ylab="x")
plot(seq(0.1,0.9,0.1), tv.oos$tvhat,type="l",ylim=c(0,1),xlab="t", ylab="TV")
lines(seq(0.1,0.9,0.1), tv, col="red",type="l")
par(oldpar) 

结论

在本文中,我们深入探讨了电视远程 HPLB 的构建。当然,我们真正的下限是概率估计的电视距离。事实上,面临的挑战是找到一个足够强大的“投影”或分类器,仍然可以找到信号。像我们在这里使用的随机森林算法就是这种强大方法的例子,而且不需要任何调整。

我们希望通过这里提供的代码和 HPLB 软件包中的 CRAN,我们在这里做的这些基本概率考虑实际上可以用于一些现实世界的问题。

深入研究 SQL 窗口函数

原文:https://towardsdatascience.com/deep-dive-into-sql-window-functions-bdcb29b05853

SQL 窗口函数跨一组表行执行计算,以简化数据分析

照片由维达尔·诺德里-马西森Unsplash 拍摄

M 在数据分析过程中,我们随时都会面临为一组行创建聚合计算的挑战。出现了许多不得不跨多个临时表创建多个汇总输出的例子。创建新结果后,它们将与原始数据集合并,以继续数据分析过程。创建和附加汇总统计数据的这种连续循环引用会导致比所需时间更长的流程。利用 SQL 窗口函数可以让开发人员避免产生大量数据和消耗内存的情况。

在本文中,我们将展示 SQL 窗口函数可以帮助解决的一些初始属性。此外,还将介绍一些时间序列功能,以展示跨时间处理数据的步骤。

数据集和软件

由于许多人对房地产价值感兴趣,我们将回顾一个提供宏观经济视角的房价指数。我们将回顾从data.gov.ie获得的爱尔兰共和国住宅价格指数。请注意,对数据集完成了一些预处理,以使其更易于使用。字符变量“住宅物业类型”包含国家、地区和物业类型的详细信息。因此,在数据集中把这个变量分成两个独立的变量“Area”和“Property_type”是有意义的。

https://data.gov.ie/dataset/hpm09-residential-property-price-index?package_type=dataset

本文中显示的 SQL 代码是使用 Microsoft SQL Server Management Studio 18 的一个实例创建的。

当使用 SQL 窗口函数时,有许多不同的类别可用。我们正在审查三个常见的类别级别;聚合、分析和排名功能。添加的时间序列处理部分显示了可用于查看的时间元素。

聚合函数

聚合提供了正在审查的数据集的汇总统计信息。这些基本的统计数据有助于我们理解数字变量的结构。从这种见解中,我们能够更好地理解数据的故事。通常,这些汇总值是在单独的查询中创建的,然后将统计数据联接回第一个数据集。创建临时表可以处理即席分析。然而,如果我们想要改变被评估的值的范围,这可能会导致错误,因为需要更新多个代码段。拥有一个更加自动化的方法确实有助于提高代码的可用性。

这就是窗口函数可以帮助提供更大灵活性的地方。在下面的代码中,我们对窗口函数的运行有了基本的了解。over 关键字指定聚合函数的窗口子句。在括号中,第一个选项是要使用的分区依据字段。它是定义窗口函数将在其上操作的窗口或行集的分区字段列表。接下来,我们有 order by 选项,它将对分区变量的行进行排序。

SQL 代码 1.1 聚合窗口函数的范围(图片由作者提供)

从上面的代码中,我们展示了四个可用的统计选项。count 函数将记录窗口中的行数。使用其他三个函数时,它们将在窗口中一次一行地前进。

SQL 输出 1.1 聚合函数值随日期变化(图片由作者提供)

对于别名 avgValue,average 方法用于了解平均值如何随着更多行的添加而变化。包括最小值和最大值,有助于检查值的界限。当第 7 行出现一个新窗口时,我们可以看到聚合变量值将如何重置。

分析函数

要使用窗口功能执行更高级的操作,可以使用分析功能区域。我们将看到,这些函数中有许多将有助于从窗口的不同部分移动数据点。

SQL 代码 1.2 使用滞后分析函数(图片由作者提供)

从上面的代码中,我们已经了解了 lag 函数的许多不同选项。默认情况下,滞后函数将采用第一个滞后值。对于下面输出中的第 2 行,我们可以看到 lagValue 从第一行的 Value 变量中获取了数据。要查找三个月前的滞后数据,可将参数值 3 添加到滞后函数括号中。

剩下的两个函数强调了当数据不可用时,如何为空值赋值。首先,使用 coalesce 将检查函数并了解是否会产生非空值。我们必须记住,coalesce 函数的输入顺序是关键,如果第一个输入可用,就访问它,然后使用它的值。如果不是这种情况,那么将采用第二个参数值零。我们可以看到,lag 函数也提供了这个功能,并且在第 20 行中包含了第三个参数零,展示了如何在一个步骤中完成这个方法。

SQL 输出 1.2 使用滞后函数设置的不同影响(图片由作者提供)

在对数据集进行时间序列比较时,获取滞后值的能力会有所帮助。窗口函数再次帮助自动化许多滞后值选项,而不是必须执行多个连接。通过获取三个月的滞后值,这有助于审查季度变化。包括六个月或十二个月的差异将显示半年或一年的变动。

SQL 代码 1.3 了解前导值、第一个值和最后一个值选项(图片由作者提供)

lead 函数将把未来的值带回当前行。类似于 lag 函数,使用默认选项 1,也可以指定空值的填充。使用 first_value 函数提供窗口开始的细节。如果在考虑某个时间序列日期的情况下查看值的范围,这可能会有所帮助。对于本例,第一个值是日历年的年初。但是,当使用 last_value 函数时,需要一个附加子句来确保对整个值窗口进行检查。包括在开始(前面)和结束(后面)的无界范围,完成了这个过程。

范围开始和结束处的 SQL output 1.3 值可以帮助定义边界(图片由作者提供)

第一个和最后一个值将有助于显示中间值在窗口的时间段内是如何移动的。如果数据分析需要与这些数据点进行相对比较,那么计算可以快速完成。

排名功能

将排名函数合并到数据分析中将对指定字段的值进行排名。很多时候我们感兴趣的是了解某个值的前(N)条记录。其他时候,根据所提的问题,底部(N)记录可能提供更多信息。

SQL code 1.4 排名选项通过分区分组提供对值范围的洞察(图片由作者提供)

等级函数旨在理解从最高到最低的值。通过此评估,我们将查看前(N)个值。正如我们在代码中看到的,添加了一个 descending 关键字,以确保 order by 子句正确处理这些值。为了保持输入数据集的原始顺序,我们在第 16 行包含了一个 order by 子句,以便在窗口函数完成后对结果进行重新排序。能够对结果重新排序显示了窗口功能的灵活性。

对于行号函数,使用 partition by 子句可以确保为每个窗口重置行数。这个变量的输出类似于前面的 count 函数。

SQL 输出 1.4 等级之间的差异可以帮助理解值的分布(图片由作者提供)

如果结果被保持在窗口函数输出顺序中,那么数据的时间特性将会丢失。正如我们所看到的,第 1 行和第 2 行交换了位置。如果数据与时间序列无关,那么这个输出应该是好的。必须注意确保输出结果与预期一致。

时间序列处理

当回顾房价指数时,数据分析集中于价值随时间的变化。使用从本文前面部分获得的知识,我们能够创建一些初始时间序列函数。了解不同时期的趋势有助于更深入地了解生成数据的宏观经济环境。

SQL 代码 1.5 使用时间序列选项来了解移动平均值和百分比变化(图片由作者提供)

首先生成一个聚合值,对一个时间窗口内的值进行平均。简单的移动平均线有助于降低时间序列数据点的波动性。该操作的结果应该显示数据点如何随时间变化的更平滑的数据可视化。为了创建三个月的平均值,包含了 rows 选项以减少要包含的行数。添加的选项将确保使用的数据点在当前行和前两行之间。

要创建三个月的百分比变化,我们可以使用之前的滞后函数。在当前行和三个月前的滞后值之间进行比较。通过使用除法并从结果中减去 1,负输出将显示减少,正输出将显示增加。

SQL 输出 1.5 使用时间序列分析来了解价值变动(图片由作者提供)

使用百分比变化逻辑时,零默认值已被排除。如果包含了这个值,那么 SQL 解释器将返回一个被零除的错误。这样做的结果是在计算开始时出现三个空值。随着更多的数据被加入到计算中,一个更平滑的趋势开始出现。

结论

本文使用了 SQL windows 函数来强调 SQL 查询可以实现的功能。所发生的特征工程过程显示了如何为每个窗口产生集合统计。以前,我们必须在一个临时表中生成这些统计数据,然后将它连接到原始表中。但是,我们已经了解到,窗口功能提供了在流程中避免这一步骤的选项。对于分析和排名功能,我们看到了如何生成时间序列指标来帮助数据可视化。在机器学习模型中,我们可以使用这些新功能来帮助预测未来的价值。

留下您的评论,非常感谢您的阅读!

您可以使用下面的链接成为高级媒体会员,并访问我的所有故事和数以千计的其他故事:

https://jamesmcneill06.medium.com/membership

你可以在 LinkedIn 上联系我,友好地聊一聊所有的事情数据。我分享过的其他故事:

[1]数据集摘自网站 DATA.GOV.IEhttps://data . gov . ie/Dataset/HPM 09-住宅-房产-价格-指数?package_type=dataset ,该数据的版权归https://creativecommons.org/licenses/by/4.0/所有

深入探讨 Python 中特性选择的 ML 技术——第 1 部分

原文:https://towardsdatascience.com/deep-dive-on-ml-techniques-for-feature-selection-in-python-part-1-3574269d5c69

Python 中 AI 驱动的特征选择!

基于最大似然的特征选择系列的第一部分,其中我们讨论了流行的过滤方法,如皮尔逊、斯皮尔曼、点双序列相关、克莱姆 v 和信息值

照片由 Edu 格兰德Unsplash

“垃圾进,垃圾出!”

这是一个任何建立机器学习模型的人都可以尽早使用的短语。简单地说,如果模型的输入数据有很多噪声,那么输出数据也会有噪声。

如今,数据集拥有数百万行和数千个要素,我们需要工具来有效地识别为我们的模型选择哪些要素。这就是特征选择算法适合模型开发旅程的地方。这里有一个它的好处的快速总结—

  • 随着过度拟合和虚假关系的机会减少,模型精度提高
  • 可以解决多重共线性的问题
  • 减少数据量,有助于模型训练更快
  • 具有少量相关特征的模型更容易解释

我已经在不同的公司建立数据科学模型 5 年多了,我发现特征选择算法和用于预测的实际模型一样重要。在机器学习建模的这个关键方面,最近已经有了很多改进。将所有最新和最流行的要素选择方法连同它们的 python 实现一起放在一个地方不是很好吗?嗯,这是这个博客的灵感!

博客系列部分

  • 特征选择方法的类型(上)
  • 相关性:皮尔逊,点双序列,克莱姆的 V(第一部分)
  • 证据权重和信息价值(上)
  • 贝塔系数(第二部分)
  • 套索回归(下)
  • 递归特征选择和顺序特征选择器(第二部分)
  • 博鲁塔:博鲁塔比,博鲁塔沙普(第三部分)
  • 将所有内容整合在一起(第 3 部分)

a)特征选择方法的类型

关于特征选择算法,首先要注意的是,它们分为 3 类:

  1. 过滤方法:根据单变量统计指标对每个特征进行排名,并挑选排名最高的特征。

    优点 : 它们是模型不可知的,最容易计算和解释 缺点 : 最大的缺点是它们不能识别本身是弱预测器但当与其他特征组合时是重要预测器的特征 示例:相关性,信息值

  2. 包装方法:使用特征子集,然后使用它们训练用户定义的模型。根据模型的性能,它会添加或删除子集中的特征,并训练另一个模型。这个过程一直持续到达到期望的特征数量或者性能度量达到期望的阈值。

  3. 嵌入方法:像 Lasso 回归这样的模型有它们自己的内置特征选择方法,它们在回归方程中添加一个惩罚项以减少过度拟合

    优点 : 比过滤方法更快的计算和更好的准确性
    缺点 : 具有内置特征选择方法的有限模型 示例: Lasso 回归

下面是这些方法的简要总结:

作者图片

对于这个博客系列,我们使用来自 UCI 机器学习资源库的信用卡客户数据 Se 的" 默认值"。它很好地结合了记录(30K)和特征数(24)。每个特性的具体细节可以在网站上找到。我已经完成了数据集的一些基本准备工作(分配列类型、分类变量的编码等)。)并且代码可以在这里找到。

b)相关性:皮尔逊、点双序列、克拉默 V

相关性是两个变量之间关系的强度和方向的量化(在我们的例子中,是特征和目标变量之间的量化)。最常见的相关类型是皮尔逊相关,使用以下公式计算:

作者图片

但是皮尔逊相关只能计算两个连续变量之间的关系,这是一个主要的限制(特别是对于目标变量是分类变量的分类问题)。没有一个相关性度量可以单独量化所有分类变量对和连续变量对之间的关系。因此,我们需要根据变量类型使用不同的指标。我们还需要记住,出于特性选择的目的,指标应该是可比较的。考虑到这些因素,我倾向于以下组合:

作者图片

我们已经查看了人员关联公式,让我们快速浏览一下表格中提到的其他内容:

我)斯皮尔曼:

第一步是分别计算每列的每个观察值的等级(最高值为等级 1)。然后使用以下公式:

作者图片

计算斯皮尔曼相关的例子可以在这里找到。

ii)点双连载:

点双序列相关假设分类变量有两个值 0 和 1。我们首先将数据分为两组:

组 0:其中分类变量= 0
组 1:其中分类变量= 1

然后我们使用下面的公式

作者图片

计算点双序列相关性的例子可以在这里.)找到。

三)克莱姆氏 V:

其计算方法为: √(X2/n) / min(c-1,r-1)

其中:

  • n: 观察次数
  • c: 列数
  • 行数
  • X2: 卡方统计

计算克莱姆 V 的例子可以在这里找到。

根据优缺点,可以考虑各种其他相关性指标(有关更多详细信息,请参考此处的https://medium.com/@outside2SDs/an-overview-of-correlation-measures-between-categorical-and-continuous-variables-4c7f85610365),但是上述指标是可比的,并且具有相同的范围。

这里是一个 python 函数,用于实现所有的相关性指标:

为了计算皮尔逊和双序列相关性,scipy包有函数corrpointbiserialr。python 中没有计算 Cramer 的 V 的直接函数,因此我在下面的代码片段中添加了一个函数。功能corr_feature_selection基于用户指定的相关性类型计算相关性,并且还基于用户提供的阈值选择特征。例如,如果pearson_threshold是. 5,它将选择绝对皮尔逊相关超过 50%的特征。

*#1.Select the top n features based on absolute correlation with train_target variable# Correlationpearson_list = []
point_bi_serial_list = ['LIMIT_BAL', 'AGE', 'BILL_AMT1', 
                        'BILL_AMT2', 'BILL_AMT3', 'BILL_AMT4',
                        'BILL_AMT5', 'BILL_AMT6', 'PAY_AMT1', 
                        'PAY_AMT2', 'PAY_AMT3','PAY_AMT4', 
                        'PAY_AMT5', 'PAY_AMT6']cramer_list = ['SEX_woe', 'EDUCATION_woe',
               'MARRIAGE_woe', 'PAY_0_woe', 
               'PAY_2_woe', 'PAY_3_woe', 'PAY_4_woe',
               'PAY_5_woe', 'PAY_6_woe']pearson_threshold = .5
point_bi_serial_threshold = .5
cramer_threshold = .1################################ Functions ############################################################## Function to calculate Cramer's V
def cramers_V(var1,var2) :
  crosstab=np.array(pd.crosstab(var1,var2, 
                                 rownames=None, colnames=None))
  stat = chi2_contingency(crosstab)[0]
  obs = np.sum(crosstab) 
  mini = min(crosstab.shape)-1 
  return (stat/(obs*mini))# Overall Correlation Function
def corr_feature_selection(data,target,pearson_list,
                           point_bi_serial_list,cramer_list,
                           pearson_threshold,
                           point_bi_serial_threshold,
                           cramer_threshold):

    #Inputs
    # data - Input feature data
    # target - Target Variable
    # pearson_list - list of continuous features (if target is continuous)
    # point_bi_serial_list - list of continuous features (if target is categorical)/
    #                        list of categorical features (if target is continuous)   
    # cramer_list - list of categorical features (if target is categorical)
    # pearson_threshold - select features if pearson corrrelation is above this
    # point_bi_serial_threshold - select features if biserial corrrelation is above this
    # cramer_threshold - select features if cramer's v is above this  

    corr_data = pd.DataFrame()# Calculate point bi-serial
    for i in point_bi_serial_list:
        # Manual Change in Parameters - Point Bi-Serial
        # Link to function parameters - [https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.stats.pointbiserialr.html](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.stats.pointbiserialr.html)
        pbc = pointbiserialr(target, data[i])   
        corr_temp_data = [[i,pbc.correlation,"point_bi_serial"]]
        corr_temp_df = pd.DataFrame(corr_temp_data, 
                                    columns = ['Feature', 
                                               'Correlation',
                                               'Correlation_Type'])
        corr_data = corr_data.append(corr_temp_df)# Calculate cramer's v
    for i in cramer_list:
        cramer = cramers_V(target, data[i])
        corr_temp_data = [[i,cramer,"cramer_v"]]
        corr_temp_df = pd.DataFrame(corr_temp_data,
                                    columns = ['Feature',
                                               'Correlation',
                                               'Correlation_Type'])
        corr_data = corr_data.append(corr_temp_df)# Calculate pearson correlation
    for i in pearson_list:
        # Manual Change in Parameters - Perason
        # Link to function parameters - [https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.corr.html](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.corr.html)
        pearson = target.corr(data[i])
        corr_temp_data = [[i,pearson,"pearson"]]
        corr_temp_df = pd.DataFrame(corr_temp_data,
                                    columns = ['Feature',
                                               'Correlation',
                                               'Correlation_Type'])
        corr_data = corr_data.append(corr_temp_df)# Filter NA and sort based on absolute correlation
    corr_data = corr_data.iloc[corr_data.Correlation.abs().argsort()] 
    corr_data = corr_data[corr_data['Correlation'].notna()]
    corr_data = corr_data.loc[corr_data['Correlation'] != 1]

    # Add thresholds

    # initialize list of lists
    data = [['pearson', pearson_threshold],
            ['point_bi_serial', point_bi_serial_threshold],
            ['cramer_v', cramer_threshold]] threshold_df = pd.DataFrame(data,
                                columns=['Correlation_Type',
                                         'Threshold'])
    corr_data = pd.merge(corr_data,threshold_df,
                         on=['Correlation_Type'],how = 'left')# Select Features with greater than user dfined absolute correlation
    corr_data2 = corr_data.loc[corr_data['Correlation'].abs() > corr_data['Threshold']]
    corr_top_features = corr_data2['Feature'].tolist()
    print(corr_top_features)
    corr_top_features_df = pd.DataFrame(corr_top_features,columns = ['Feature'])
    corr_top_features_df['Method'] = 'Correlation'
    return corr_data,corr_top_features_df################################ Calculate Correlation #############################################################corr_data,corr_top_features_df = corr_feature_selection(train_features_v2,train_target,
                                   pearson_list,point_bi_serial_list,
                                   cramer_list,pearson_threshold,
                                   point_bi_serial_threshold,cramer_threshold)

corr_data.tail(30)*

作者图片

c)证据权重和信息价值

这两个术语已经广泛用于多个领域的特征选择(尤其是信用评分模型)。WOE 表示一个特征的预测能力的程度。它假设模型中的目标变量是二分法的(即有两个值,如事件和非事件)。使用以下步骤进行计算:

  1. 对于连续特征,将数据分割成箱
  2. 对于每个箱,计算事件下的观察百分比和非事件下的观察百分比。
  3. 使用以下公式计算每个箱的权重

作者图片

如上所示,WOE 计算特征中每个条柱的预测能力。然后,我们可以使用 IV 来聚合 WOE,以获得该特征作为一个整体的预测能力。其计算方法如下:

作者图片

其中 h 是仓的数量。

下面是一个计算 WOE 和 IV 的 python 函数:

类似于函数corr_feature_selectioniv_woe计算 WOE 和 IV,并基于用户提供的阈值选择特征。例如,如果iv_threshold是. 1,它将选择 IV 大于. 1 的特征。用户还可以选择连续变量需要多少个箱。

*#2\. Select top features based on information value# Information valueshow_woe = True
iv_bins = 10
iv_threshold = .1################################ Functions #############################################################def iv_woe(data, target, iv_bins,iv_threshold, show_woe):

    #Inputs
    # data - Input Data including target variable
    # target - Target Variable name
    # iv_bins - Number of iv_bins
    # show_woe - show all the iv_bins and features
    # iv_threshold - select features with IV greater than this

    #Empty Dataframe
    newDF,woeDF = pd.DataFrame(), pd.DataFrame()

    #Extract Column Names
    cols = data.columns

    #Run WOE and IV on all the independent variables
    for ivars in cols[~cols.isin([target])]:
        if (data[ivars].dtype.kind in 'bifc') and (len(np.unique(data[ivars]))>10):
            binned_x = pd.qcut(data[ivars], iv_bins,  duplicates='drop')
            d0 = pd.DataFrame({'x': binned_x, 'y': data[target]})
        else:
            d0 = pd.DataFrame({'x': data[ivars], 'y': data[target]})# Calculate the number of events in each group (bin)
        d = d0.groupby("x", as_index=False).agg({"y": ["count", "sum"]})
        d.columns = ['Cutoff', 'N', 'Events']

        # Calculate % of events in each group.
        d['% of Events'] = np.maximum(d['Events'], 0.5) / d['Events'].sum()# Calculate the non events in each group.
        d['Non-Events'] = d['N'] - d['Events']
        # Calculate % of non events in each group.
        d['% of Non-Events'] = np.maximum(d['Non-Events'], 0.5) / d['Non-Events'].sum()# Calculate WOE by taking natural log of division of % 
        # of non-events and % of events
        d['WoE'] = np.log(d['% of Events']/d['% of Non-Events'])
        d['IV'] = d['WoE'] * (d['% of Events'] - d['% of Non-Events'])
        d.insert(loc=0, column='Variable', value=ivars)
        print("Information value of " + ivars + " is " + 
              str(round(d['IV'].sum(),6)))
        temp =pd.DataFrame({"Variable" : [ivars],
                            "IV" : [d['IV'].sum()]},
                           columns = ["Variable", "IV"])
        newDF=pd.concat([newDF,temp], axis=0)
        woeDF=pd.concat([woeDF,d], axis=0)#Show WOE Table
        if show_woe == True:
            print(d)

    # Aggregate IV at feature level
    woeDF_v2 = pd.DataFrame(woeDF.groupby('Variable')['IV'].agg('sum'),
                            columns= ['IV']).reset_index()
    woeDF_v3 = woeDF_v2.sort_values(['IV'], ascending = False)
    IV_df = woeDF_v2[woeDF_v2['IV']> iv_threshold]
    woe_top_features = IV_df['Variable'].tolist()
    print(woe_top_features)
    woe_top_features_df = pd.DataFrame(woe_top_features,columns = ['Feature'])
    woe_top_features_df['Method'] = 'Information_value'
    return newDF, woeDF,IV_df, woe_top_features_df################################ Calculate IV #############################################################train_features_v3_temp = pd.concat([train_target, train_features_v2],
                                   axis =1)newDF, woeDF,IV_df, woe_top_features_df = iv_woe(train_features_v3_temp, 
                                              target,iv_bins,iv_threshold, 
                                              show_woe)
woeDF.head(n=50)*

作者图片

以下是基于 IV 的常见特征分类:

作者图片

鉴于 WoE 和 IV 善于解释线性关系,记住使用 IV 选择的特征可能不是非线性模型的最佳特征集是很重要的。此外,这种特征选择方法应该仅用于分类问题。

最后的话

我希望这个博客系列能够帮助其他数据科学家使用最大似然法提供的最佳方法来识别他们数据集中真正的瑰宝。整个端到端的分析可以在这里找到。在第一部分中,我们讨论了以下内容:

  • 各类特征选择方法概述
  • 用于特征选择的相关性指标类型——理论和 python 实现
  • 什么是 WOE 和 IV,如何计算?和他们的 python 实现

尽管我们在博客中讨论的过滤方法易于计算和理解,但它们并不是多变量建模(具有多个特征的模型)的最佳选择。这就是为什么我会敦促读者去看看下一个博客,它关注一些有趣的包装器和嵌入式方法,比如套索回归、Beta 系数、递归特征选择等等..

你对这个博客有什么问题或建议吗?请随时留言。

参考材料

我们连线吧!

如果你和我一样,对 AI、数据科学或经济学充满热情,请随时在 LinkedInGithubMedium 上添加/关注我。

皮特·佩德罗萨在 Unsplash 上的照片

深入探讨 Python 中特性选择的 ML 技术—第 2 部分

原文:https://towardsdatascience.com/deep-dive-on-ml-techniques-for-feature-selection-in-python-part-2-c258f8a2ac43

Python 中 AI 驱动的特征选择!

基于最大似然的特征选择系列的第二部分,我们将讨论流行的嵌入和包装方法,如套索回归、贝塔系数、递归特征选择等。

照片由 凯文 Ku的 Unsplash

欢迎来到我关于基于 ML 的特征选择的博客系列的第二部分!我们都知道大数据时代已经到来,但有时很难理解大数据到底有多“大”。我最近读了一篇有趣的博客,这篇博客很好地诠释了这一点:

  1. 2020 年,每天会产生 2.5 万亿个数据字节
  2. 2020 年,平均每个人每秒至少产生 1.7 MB 的数据
  3. 到 2025 年,云数据存储将达到 200+ Zettabytes

似乎有点令人生畏,对吧?我从数据量的快速增长中得出的一个结论是,我们不能再使用“厨房水槽”的方法来建模。这种方法仅仅意味着我们把“除了厨房水槽以外的所有东西”都扔进模型,希望找到某种模式。用这种方法,我们的模型根本无法理解大数据集。这就是使用特征选择方法首先剔除数据中的噪声,然后继续开发模型的关键所在。

在第一篇博客中,我们概述了不同类型的特征选择方法,并讨论了一些过滤方法,如信息值。在第二部分,我们将深入探讨以下有趣的方法:

a)贝塔系数
B)套索回归
C)递归特征选择
D)顺序特征选择器

关于数据集的细节和整个代码(包括数据准备)可以在这个 Github repo 中找到。所以事不宜迟,让我们开始吧!

A)贝塔系数

关于贝塔系数,首先要理解的是,它是基于回归模型的。最简单的回归模型是线性回归,它试图拟合一个方程,该方程线性地解释目标变量和特征之间的关系。它表示为:

Y = b0 + b1X1 + b2X2 + b3X3 + …。bnXn + u

其中 Y 是目标变量,X1…Xn 是特征,b1…bn 是系数,u 是误差项。系数告诉我们,如果特性增加 1 个单位,那么对目标变量的边际(保持所有其他特性不变)影响是什么。深入回归模型(如假设、诊断检查等。),我会建议通过以下方式:

  1. 回归分析回顾
  2. 线性回归假设
  3. 多元回归分析简介
  4. 基础计量经济学(第 2-13 章)(深入涵盖所有回归相关主题)

我们为什么要标准化特征? 关于系数,需要注意的一件有趣的事情是,它们受特征比例的影响。例如,如果我们的目标是 GDP,而特征是以百万卢比为单位的预算赤字和以%为单位的中央银行回购利率,我们无法比较系数来判断哪个特征对 GDP 的影响更大。为了使系数具有可比性,我们需要将特征标准化。标准化特征包括以下步骤:

  1. 计算特征的平均值
  2. 计算特征的标准偏差
  3. 对于每个观察值,减去平均值,然后除以标准偏差

标准化后,每个特征的平均值为 0,标准偏差为 1,因此具有可比性。这种标准化特征的系数称为β系数。我们可以根据 beta 系数的绝对值对特性进行排序,并挑选出前 n 个特性(n 可以由开发人员根据业务上下文和自由度来决定)。

logistic 回归的贝塔系数: 我们前面说的回归模型是一种线性回归模型,只能预测连续变量。为了计算分类问题的贝塔系数,我们需要使用逻辑回归。它可以表示为:

ln(pi/(1-pi))=B0+B1 * X1+B2 * X2+B3 * X3+…。bnXn + u*

其中一切都保持不变,除了不是预测目标变量(它将有不同的类别,如事件和非事件),系数告诉我们,如果特征增加 1 个单位,那么对目标变量的概率对数的边际影响是什么。

例如,假设我们的目标是政府是否会违约,特征是以百万卢比为单位的预算赤字和以%为单位的中央银行回购利率。预算赤字系数告诉我们,如果赤字增加 100 万卢比,那么对政府违约概率的对数,即 log(违约概率/未违约概率)的边际影响是什么。

尽管逻辑回归和线性回归之间存在许多其他差异(如使用 OLS 计算线性回归,而逻辑回归使用最大似然估计,因为后者是非线性的),但我们在这里不会深入探讨这些差异。关于逻辑回归的更多细节,我建议浏览以下内容:

  1. 什么是逻辑回归?
  2. 逻辑回归的最大似然估计
  3. 详细概述
  4. 基础计量经济学(第十五章)
  5. 逻辑的贝塔系数

计算逻辑回归贝塔系数的 Python 函数:

为了使用逻辑回归计算β系数,我们首先使用StandardScaler函数来标准化数据集,然后使用 scikit-learn 包中的LogisticRegression函数来拟合没有惩罚项和截距的逻辑回归。函数beta_coeff计算每个特征的贝塔系数,并根据贝塔系数的绝对值选择前 n 个(n 由用户作为参数beta_threshold提供)特征。然后,它将另一个套索回归与所选要素进行拟合,以允许开发人员查看所选要素的回归行为(如要素的符号和重要性)。

#3\. Select  the top n features based on absolute value of beta coefficient of features# Beta Coefficientsbeta_threshold = 10################################ Functions #############################################################def beta_coeff(data, train_target,beta_threshold):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # beta_threshold - select n features with highest absolute beta coeficient value

    # Standardise dataset scaler = StandardScaler()
    data_v2 = pd.DataFrame(scaler.fit_transform(data))
    data_v2.columns = data.columns# Fit Logistic on Standardised dataset
    # Manual Change in Parameters - Logistic Regression
    # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html)       
    log = LogisticRegression(fit_intercept = False, penalty = 'none')
    log.fit(data_v2, train_target)
    coef_table = pd.DataFrame(list(data_v2.columns)).copy()
    coef_table.insert(len(coef_table.columns), "Coefs",    log.coef_.transpose())
    coef_table = coef_table.iloc[coef_table.Coefs.abs().argsort()]
    sr_data2 = coef_table.tail(beta_threshold)
    beta_top_features = sr_data2.iloc[:,0].tolist()
    print(beta_top_features)

    beta_top_features_df = pd.DataFrame(beta_top_features,columns = ['Feature'])
    beta_top_features_df['Method'] = 'Beta_coefficients' log_v2 = sm.Logit(train_target,\
                     sm.add_constant(data[beta_top_features])).fit()
    print('Logistic Regression with selected features')
    print(log_v2.summary())

    return log,log_v2,beta_top_features_df################################ Calculate Beta Coeff ################################################standardised_logistic,logistic_beta_features,beta_top_features_df = beta_coeff(train_features_v2,train_target,beta_threshold)beta_top_features_df.head(n=20)

作者图片

计算线性回归贝塔系数的代码可以在这里找到。

拉索回归

套索回归是可用于特征选择的少数嵌入式方法之一。这是线性/逻辑回归的自然延伸,其中使用惩罚项来选择特征。lasso 回归的成本函数(需要最小化以获得最佳系数值的函数)可以表示为:

作者图片

附加罚项本质上是系数的绝对和乘以一个因子(称为调谐参数,我们将很快讨论),这个过程称为 L1 正则化。因此,随着我们不断增加功能,我们会不断将功能的系数添加到惩罚项中,这将增加成本。这种类型的正则化(L1)导致一些最不重要的特征的系数为零,因此,我们可以使用这种模型来选择特征(要了解更多关于正则化的信息,请查看此博客)。同样,可以将相同的惩罚项应用于逻辑回归成本函数来选择特征。

设置调整参数 使用 L1 正则化时要考虑的主要问题是调整参数λ的值,因为它控制惩罚的强度。从等式中可以看出,将其设置为 0 相当于线性回归。设置调节参数的一种方法是尝试一系列值,为每个值建立回归,并选择 AIC 分数最低的一个值。可以从测试 0.01 和 1.0 之间的值开始,网格间距为 0.01。

关于 lasso 回归的更多细节,可以浏览下面的讲座幻灯片

向逻辑回归添加 L1 正则化的 Python 函数(带逻辑回归的 Lasso):

函数lasso用用户提供的调整参数值作为参数lasso_param拟合带有 l1 惩罚项的逻辑回归。与上一个函数类似,它随后会用所选要素拟合另一个套索回归。

#4\. Select the features identified by Lasso regression# Lassolasso_param = .01################################ Functions #############################################################def lasso(data, train_target,lasso_param):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # lasso_param - Lasso l1 penalty term

    #Fit Logistic
    # Manual Change in Parameters - Logistic Regression
    # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html)       
    log = LogisticRegression(penalty ='l1', solver = 'liblinear',\
                             C = lasso_param)
    log.fit(data, train_target)

    #Select Features
    lasso_df = pd.DataFrame(columns = ['Feature', 'Lasso_Coef'])
    lasso_df['Feature'] = data.columns
    lasso_df['Lasso_Coef'] = log.coef_.squeeze().tolist()
    lasso_df_v2 = lasso_df[lasso_df['Lasso_Coef'] !=0]
    lasso_top_features = lasso_df_v2['Feature'].tolist()

    lasso_top_features_df = pd.DataFrame(lasso_top_features,\
                                         columns = ['Feature'])
    lasso_top_features_df['Method'] = 'Lasso'# Logistic Regression with selected features
    log_v2 = sm.Logit(train_target,\
                   sm.add_constant(data[lasso_top_features])).fit()    
    print('Logistic Regression with selected features')
    print(log_v2.summary())

    return log_v2,lasso_top_features_df################################ Calculate Lasso ################################################logistic_lasso_features,lasso_top_features_df = lasso(train_features_v2,train_target,lasso_param)lasso_top_features_df.head(n=20)

作者图片

通过用LinearRegression改变LogicticRegression函数,用OLS改变Logit,同样的函数可以很容易地用于线性回归

c)递归特征消除(RFE)

这是 python 的Scikit-learn包为特征选择提供的两种流行的特征选择方法之一。虽然 RFE 在技术上是一个包装器风格的方法,但它是基于基于过滤器的方法所使用的过程的。让我们看看怎么做。

RFE 是如何工作的?

  1. RFE 首先在整个特征集上训练用户定义的模型。用户定义的模型可以是任何具有fit方法的监督学习估计器,该方法提供关于特征重要性的信息。
  2. 然后,它根据训练好的模型计算每个特征的重要性。重要性可以通过任何特定于模型的属性(如coef_feature_importances_)或可以为每个特性计算的用户定义的指标来获得。
  3. 然后,它会丢弃最不重要的特征,并使用数量减少的特征重复步骤 1 和 2
  4. 重复步骤 3,直到剩余用户定义的特征数量

RFE 被认为是一种包装方法,因为它“包装”在外部估计量周围。但是它也是基于基于过滤器的方法的原理,这是由于基于重要性的度量对特征进行排序。

如何使用 RFE 自动拾取特征的数量? 挑选特性的数量并不简单,值得庆幸的是Scikit-learn软件包有一个新的功能叫做[**RFECV**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV)(带交叉验证的递归特性消除),它把我们从这个负担中解放了出来。交叉验证是一种模型性能测试策略,最流行的版本是 k-fold 交叉验证。我们首先将数据分成 k 个随机部分,然后在除了一个(k-1)部分之外的所有部分上训练模型,最后,在不用于训练的部分上评估模型。这被重复 k 次,每次保留不同的部分用于评估。

如果我们有 n 个特性,[**RFECV**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV)实际上执行 n-1 次 RFE 和交叉验证(在每次迭代中丢弃最不重要的特性)。然后,它挑选交叉验证分数最大化的特征的数量。

Python 函数用于实现[**RFECV**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV):

我们在这里使用 scikit-learn 的RFECV函数,关于算法的选择,它的配置就像 RFE 类一样。功能rfecv_feature_selection允许用户从 5 种流行的基于树的算法中进行选择:XG Boost、Random Forest、Catboost、Light GBM 和决策树。我将很快就每种方法写一个新的博客系列。该函数从用户使用rfe_estimator参数指定的列表中设置任何估计值。如果没有提到,默认情况下决策树是合适的。他们还可以更改许多其他功能,如评分标准,以选择最佳数量的功能(参见下面代码中函数的输入部分)。

#5\. Select features based on Recursive Feature Selection method# RFECVrfe_estimator = "XGBoost"
rfe_step = 2
rfe_cv = 5
rfe_scoring = 'f1'################################ Functions #############################################################def rfecv_feature_selection(data, train_target,rfe_estimator,rfe_step,rfe_cv,rfe_scoring):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # rfe_estimator - base model (default: Decision Tree)
    # rfe_step -  number of features to remove at each iteration
    # rfe_cv - cross-validation splitting strategy
    # rfe_scoring - CV performance scoring metric## Initialize RFE if rfe_estimator == "XGBoost":
        # Manual Change in Parameters - XGBoost
        # Link to function parameters - [https://xgboost.readthedocs.io/en/stable/parameter.html](https://xgboost.readthedocs.io/en/stable/parameter.html)       
        estimator_rfe = XGBClassifier(n_jobs = -1, random_state=101)
    elif rfe_estimator == "RandomForest":
        # Manual Change in Parameters - RandomForest
        # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
        estimator_rfe = RandomForestClassifier(n_jobs = -1, random_state=101)
    elif rfe_estimator == "CatBoost":
        # Manual Change in Parameters - CatBoost
        # Link to function parameters - [https://catboost.ai/en/docs/concepts/python-reference_catboostclassifier](https://catboost.ai/en/docs/concepts/python-reference_catboostclassifier)
        estimator_rfe = CatBoostClassifier(iterations=50,verbose=0,random_state=101)
    elif rfe_estimator == "LightGBM":
        # Manual Change in Parameters - LightGBM
        # Link to function parameters - [https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html)
        estimator_rfe = lgb.LGBMClassifier(n_jobs = -1, random_state=101)
    else:
        # Manual Change in Parameters - DecisionTree
        # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)
        estimator_rfe = DecisionTreeClassifier(random_state=101)# Fit RFECV
    # Manual Change in Parameters - RFECV
    # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html)
    # Scoring metrics - [https://scikit-learn.org/stable/modules/model_evaluation.html](https://scikit-learn.org/stable/modules/model_evaluation.html)
    rfecv = RFECV(estimator = estimator_rfe, step = rfe_step, cv = rfe_cv, scoring = rfe_scoring)
    rfecv.fit(data, train_target)# Select feature based on RFE
    print('Optimal number of features: {}'.format(rfecv.n_features_))
    rfe_df = pd.DataFrame(columns = ['Feature', 'rfe_filter'])
    rfe_df['Feature'] = data.columns
    rfe_df['rfe_filter'] = rfecv.support_.tolist()
    rfe_df_v2 = rfe_df[rfe_df['rfe_filter']==True]
    rfe_top_features = rfe_df_v2['Feature'].tolist()
    print(rfe_top_features)

    rfe_top_features_df = pd.DataFrame(rfe_top_features,columns = ['Feature'])
    rfe_top_features_df['Method'] = 'RFECV'# Plot CV results
    %matplotlib inline
    plt.figure(figsize=(16, 9))
    plt.title('Recursive Feature Elimination with Cross-Validation', fontsize=18, fontweight='bold', pad=20)
    plt.xlabel('Number of features selected', fontsize=14, labelpad=20)
    plt.ylabel('f1 acore', fontsize=14, labelpad=20)
    plt.plot(range(1, len(rfecv.grid_scores_) + 1), rfecv.grid_scores_, color='#303F9F', linewidth=3) plt.show()

    return rfe_top_features_df,rfecv################################ Calculate RFECV #############################################################rfe_top_features_df,rfecv = rfecv_feature_selection(train_features_v2,train_target,rfe_estimator,rfe_step,rfe_cv,rfe_scoring)
rfe_top_features_df.head(n=20)

作者图片

我个人不使用[**RFECV**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV) 带回归模型作为基于系数值的排序特征,如果特征不在同一尺度上是不可取的。

d)顺序特征选择(SFS)

顺序特征选择(SFS)是Scikit-learn包提供的另一种包装器类型的特征选择方法。RFE 和 SFS 的区别在于,它不需要底层模型来计算特征重要性分数。它还可以灵活地进行向前(从 1 个特征开始,随后向模型添加特征)或向后(从所有特征开始,随后向模型移除特征)特征选择,而 RFE 只能进行向后选择。

此外,SFS 通常比 RFECV 慢。例如,在后向选择中,使用 k 重交叉验证从 n 个特征到 n-1 个特征的迭代需要拟合 n*k 个模型,而 RFECV 将只需要 k 次拟合。

SFS 是如何工作的?

前向 SFS:

  1. SFS 最初从没有特征开始,并找到使交叉验证分数最大化的特征
  2. 一旦选择了第一个特征,SFS 通过向现有的所选特征添加新特征来重复该过程。
  3. 程序继续进行,直到达到由n_features_to_select参数确定的所选特征的所需数量。如果用户没有指定要选择的功能的确切数量或性能改善的阈值,该算法会自动选择现有功能列表的一半。

向后 SFS:

它具有相同的过程,但工作方向相反:它从所有特征开始,然后开始从集合中删除特征。

如何选择方向(向后 vs 向前)?

direction参数控制使用前向还是后向 SFS。同样重要的是要注意向前和向后选择通常不会产生相同的结果。我们应该根据我们希望在模型中保留多少特征来选择方向。如果我们想保留大多数特征,我们应该选择“向后”,反之亦然。

Python 函数用于实现[SequentialFeatureSelector](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SequentialFeatureSelector.html#sklearn.feature_selection.SequentialFeatureSelector):

我们正在使用 scikit 的SequentialFeatureSelector功能——点击此处了解。函数sfs_feature_selection符合逻辑回归。我在这里使用逻辑回归,因为我们可以根据它们对 r 平方的影响来选择特征,并且我们已经在前面的方法中拟合了基于树的模型。用户还可以更改许多特性,例如要保留的特性数量(参见下面代码中函数的输入部分)。

#6\. Select features based on Sequential Feature Selector# Sequential Feature Selectorsfs_feature = 10
sfs_direction = 'backward'
sfs_cv = 2
sfs_scoring = 'r2'################################ Functions #############################################################def sfs_feature_selection(data, train_target,sfs_feature,sfs_direction,sfs_cv,sfs_scoring):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # sfs_feature - no. of features to select
    # sfs_direction -  forward and backward selection
    # sfs_cv - cross-validation splitting strategy
    # sfs_scoring - CV performance scoring metric logistic = LogisticRegression(penalty = None) sfs=SequentialFeatureSelector(estimator = logistic,
                                  n_features_to_select=sfs_feature,   
                                  direction = sfs_direction,
                                  cv = sfs_cv,
                                  scoring = sfs_scoring)
    sfs.fit(train_features_v2, train_target)
    sfs.get_support() sfs_df = pd.DataFrame(columns = ['Feature', 'SFS_filter'])
    sfs_df['Feature'] = train_features_v2.columns
    sfs_df['SFS_filter'] = sfs.get_support().tolist() sfs_df_v2 = sfs_df[sfs_df['SFS_filter']==True]
    sfs_top_features = sfs_df_v2['Feature'].tolist()
    print(sfs_top_features) x_temp = sm.add_constant(train_features_v2[sfs_top_features]) log_v2=sm.Logit(train_target,x_temp).fit() print(log_v2.summary())

    sfs_top_features_df=pd.DataFrame(sfs_top_features\
                                     ,columns = ['Feature'])
    sfs_top_features_df['Method']='Sequential_feature_selector' return sfs_top_features_df,sfs################################ Calculate RFECV #############################################################sfs_top_features_df,sfs = sfs_feature_selection(train_features_v2,train_target,sfs_feature,sfs_direction,sfs_cv,sfs_scoring)
sfs_top_features_df.head(n=20)

作者图片

如前所述,用户可以通过更改代码的这一部分来适应任何其他方法:

sfs=SequentialFeatureSelector(estimator = #add model here#
                                  ,n_features_to_select=sfs_feature,   
                                  direction = sfs_direction,
                                  cv = sfs_cv,
                                  scoring = sfs_scoring)

最后的话

我们现在已经到了 3 部分博客系列的第 2 部分的结尾。读者现在应该熟悉 6 种不同的特征选择技术。他们还应该了解与其 python 实现相关的最佳实践。我想在这里强调的是,特征选择是艺术和科学的结合。虽然我们已经详细讨论了它背后的科学,但是随着经验的积累,人们会变得更好。

请务必阅读本系列的最后一篇部分,在那里我们讨论了我们可以使用的最先进的技术(Borutapy,Borutashap ),并讨论了如何结合所有这些方法。最后,分析的全部代码可以在这里找到。

你对这个博客有什么问题或建议吗?请随时留言。

参考材料

  1. 回归分析的复习课
  2. 线性回归假设
  3. 多元回归分析简介
  4. 基础计量经济学(第 2-13 章)(深入涵盖所有回归相关主题)
  5. 什么是逻辑回归?
  6. 逻辑回归的最大似然估计
  7. 详细概述
  8. 基础计量经济学(第十五章)
  9. 逻辑斯谛的贝塔系数

我们连线吧!

如果你和我一样,对人工智能、数据科学或经济学充满热情,请随时在 LinkedInGithubMedium 上添加/关注我。

照片由 Howie RUnsplash 上拍摄

深入探讨 Python 中特性选择的 ML 技术—第 3 部分

原文:https://towardsdatascience.com/deep-dive-on-ml-techniques-for-feature-selection-in-python-part-3-de2a7593247f

Python 中 AI 驱动的特征选择!

基于 ML 的特征选择系列文章的最后一部分,在这里我们将讨论像 Borutapy 和 Borutashap 这样的高级方法。此外,讨论如何组合多种方法的结果。

https://unsplash.com/photos/bTRsbY5RLr4

我首先要感谢并祝贺那些完成了基于 ML 的特征选择方法系列博客最后部分的读者!
如果您还没有看完前两部分,请仔细阅读:

深入探讨 Python 中用于特征选择的 ML 技术—第 1 部分

深入探讨 Python 中用于特征选择的 ML 技术—第二部分

我们已经在前面的章节中介绍了以下内容:

A)特征选择方法的类型(第一部分)
B)相关性:皮尔逊、点双序列、克莱姆 V(第一部分)
C)证据和信息值的权重(第一部分)
D)贝塔系数(第二部分)
E)拉索回归(第二部分)
F)递归特征选择和顺序特征选择器(第二部分)

我们将重点关注以下内容:

A)博鲁塔派(第三部分)
B)博鲁塔派(第三部分)
C)把所有的东西结合在一起(第三部分)

如前所述,数据集的细节和整个代码(包括数据准备)可以在这个 Github repo 中找到。所以让我们开始吧!

a)突变性

Boruta 在 2010 年作为 R ( paper link )的一个包首次亮相。它很快成为最流行和最先进的特征选择方法之一,它基于两个概念:

概念 1:阴影特征

在 Boruta 中,特性的选择是基于它们相对于随机版本的性能。主要的逻辑是,只有当一个特性的性能优于随机特性时,它才是有用的。以下是其背后的主要步骤:

  1. 基于特征上的原始数据帧,通过随机洗牌产生另一个数据帧。置换特征被称为阴影特征。
  2. 然后阴影数据帧与原始数据帧结合
  3. 在此组合数据框架上拟合用户定义的模型
  4. 获取所有特征的重要性(原始特征+阴影特征)
  5. 获得阴影特征中记录的最高特征重要性(从现在起我们称之为阈值)。python 版本在这一步有一个小变化,我们将在后面讨论。
  6. 当原始特征的重要性高于这个阈值时,我们称之为“命中”

将阴影要素的最大值作为选择要素的阈值有时非常保守,因此 python 包允许用户将阴影要素重要性的百分比设置为阈值。默认值 100 相当于 R 版本的 Boruta 所做的。

概念二:二项分布

下一个想法是基于多次重复前面提到的步骤,以使结果更加可靠。如果我们称每次迭代为试验,那么在试验中选择一个特性的概率是多少?鉴于我们事先不知道某个特征是否重要,概率是 50%,并且由于每个独立试验可以给出二元结果(命中或未命中),这一系列的 n 试验遵循二项式分布。

如果我们以 50%的成功概率和 5%的显著性水平绘制 20 次试验的二项式概率分布,概率曲线将看起来像:

作者图片

在 Borutapy 中,特性分为 3 个部分。这些部分基于分布的两个极端部分,称为尾部(由显著性水平决定):

  • 拒绝(红色区域):这里的特征被认为是噪声,应该丢弃。在上面的图片中,如果一个特性的命中率低于 20 分之 4,那么它就在这个区域。
  • 优柔寡断(蓝色区域):此处的特征并不确定,因此,该模型无法自信地说放弃/保留。在上面的图片中,如果一个特性的点击次数少于 16 次,多于 20 次中的 3 次,那么它就在这个区域。
  • 接受(绿色区域):这里的特征是一个重要的估计量。在我们上面的图片中,如果一个功能在 20 次点击中超过 15 次,那么它就在这个区域。

如何计算 BorutaPy 的详细示例可以在这里找到。

boru tapy 的 Python 实现

python 包比 R 包有一些改进:

  • 由于 scikit-learn,运行时间更快
  • Scikit-learn like 接口
  • 兼容 scikit-learn 的任何集成方法
  • 自动 n _ 估算器选择
  • 功能排名
  • 特征重要性源自基尼系数,而非随机森林 R 包的 MDA。

另外,强烈建议我们使用修剪过的深度在 3-7 之间的树。关于 python 包的更多细节可以在这里找到。功能borutapy_feature_selection允许用户从 3 种流行的基于树的算法中选择:XG Boost,Random Forest 和 Light GBM。该函数从用户使用borutapy_estimator参数指定的列表中设置任何估计值。如果没有提到,默认情况下 XG boost 模型是合适的。他们还可以改变许多其他特性,比如试验次数(参见下面代码中函数的输入部分)。

#7\. Select features based on BorutaPy method# BorutaPy:
borutapy_estimator = "XGBoost"
borutapy_trials = 10
borutapy_green_blue = "both"################################ Functions #############################################################def borutapy_feature_selection(data, train_target,borutapy_estimator,borutapy_trials,borutapy_green_blue):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # borutapy_estimator - base model (default: XG Boost)
    # borutapy_trials -  number of iteration
    # borutapy_green_blue - choice for green and blue features## Initialize borutapy

    if borutapy_estimator == "RandomForest":
        # Manual Change in Parameters - RandomForest
        # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
        estimator_borutapy=RandomForestClassifier(n_jobs = -1,
                                                  random_state=101,
                                                  max_depth=7)
    elif borutapy_estimator == "LightGBM":
        # Manual Change in Parameters - LightGBM
        # Link to function parameters - [https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html)
        estimator_borutapy=lgb.LGBMClassifier(n_jobs = -1,
                                              random_state=101,
                                              max_depth=7)
    else:
        # Manual Change in Parameters - XGBoost
        # Link to function parameters - [https://xgboost.readthedocs.io/en/stable/parameter.html](https://xgboost.readthedocs.io/en/stable/parameter.html)       
        estimator_borutapy = XGBClassifier(n_jobs = -1,
                                           random_state=101,
                                           max_depth=7)## fit Borutapy
    # Manual Change in Parameters - Borutapy
    # Link to function parameters - [https://github.com/scikit-learn-contrib/boruta_py](https://github.com/scikit-learn-contrib/boruta_py)
    borutapy = BorutaPy(estimator = estimator_borutapy,
                        n_estimators = 'auto', 
                        max_iter = borutapy_trials)
    borutapy.fit(np.array(data), np.array(train_target))

    ## print results
    green_area = data.columns[borutapy.support_].to_list()
    blue_area = data.columns[borutapy.support_weak_].to_list()
    print('features in the green area:', green_area)
    print('features in the blue area:', blue_area) if borutapy_green_blue == "both":
        borutapy_top_features = green_area + blue_area
    else:
        borutapy_top_features = green_area

    borutapy_top_features_df =pd.DataFrame(borutapy_top_features,
                                           columns = ['Feature'])
    borutapy_top_features_df['Method'] = 'Borutapy'

    return borutapy_top_features_df,borutapy################################ Calculate borutapy #############################################################borutapy_top_features_df,boruta = borutapy_feature_selection(train_features_v2, train_target,borutapy_estimator,borutapy_trials,borutapy_green_blue)borutapy_top_features_df.head(n=20)

作者图片

博鲁塔·SHAP

BorutaPy 的致命弱点是它强烈依赖于可能有偏差的特征重要性的计算。这就是沙普利附加解释公司(SHAP)适合这个难题的地方。简单来说,SHAP 值可以解释一个复杂的模型如何做出决策。它实际上是为每个观察值计算所有排列中每个特征的平均边际贡献。由于计算的附加性质,我们可以对每个观察值取这些边际贡献的平均值,以获得全局特征重要性。如何计算 SHAP 值的详细例子可以在找到。

这种方法的唯一缺点是评估时间,因为需要计算许多排列。

Python 实现的 BorutaShap

功能borutapy_feature_selection允许用户从 3 种流行的基于树的算法中选择:XG Boost、随机森林和轻型 GBM。该功能从用户使用borutapy_estimator参数指定的列表中设置任何估计值。如果没有提到,默认情况下 XG boost 模型是合适的。他们还可以改变许多其他特性,比如试验次数(参见下面代码中函数的输入部分)。

#8\. Select features based on BorutaShap method# BorutaShap:
borutashap_estimator = "XGBoost"
borutashap_trials = 10
borutashap_green_blue = 'both'################################ Functions #############################################################def borutashap_feature_selection(data, train_target,borutashap_estimator,borutashap_trials,borutashap_green_blue):

    #Inputs
    # data - Input feature data 
    # train_target - Target variable training data
    # borutashap_estimator - base model (default: XG Boost)
    # borutashap_trials -  number of iteration
    # borutashap_green_blue - choice for green and blue features## Initialize borutashap

    if borutashap_estimator == "RandomForest":
        # Manual Change in Parameters - RandomForest
        # Link to function parameters - [https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
        estimator_borutashap=RandomForestClassifier(n_jobs = -1,
                                                    random_state=1,
                                                    max_depth=7)
    elif borutashap_estimator == "LightGBM":
        # Manual Change in Parameters - LightGBM
        # Link to function parameters - [https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html)
        estimator_borutashap=lgb.LGBMClassifier(n_jobs = -1,
                                                random_state=101,
                                                max_depth=7)
    else:
        # Manual Change in Parameters - XGBoost
        # Link to function parameters - [https://xgboost.readthedocs.io/en/stable/parameter.html](https://xgboost.readthedocs.io/en/stable/parameter.html)       
        estimator_borutashap=XGBClassifier(n_jobs = -1,
                                           random_state=101,
                                           max_depth=7)## fit BorutaShap
    # Manual Change in Parameters - BorutaShap
    # Link to function parameters - [https://github.com/scikit-learn-contrib/boruta_py](https://github.com/scikit-learn-contrib/boruta_py)
    borutashap = BorutaShap(model = estimator_borutashap,
                            importance_measure = 'shap',
                            classification = True)
    borutashap.fit(X = data, y = train_target, 
                   n_trials = borutashap_trials)

    ## print results
    %matplotlib inline
    borutashap.plot(which_features = 'all')## print results
    green_area = borutashap.accepted
    blue_area = borutashap.tentative
    print('features in the green area:', green_area)
    print('features in the blue area:', blue_area) if borutashap_green_blue == "both":
        borutashap_top_features = green_area + blue_area
    else:
        borutashap_top_features = green_area

    borutashap_top_features_df=pd.DataFrame(borutashap_top_features,
                                            columns = ['Feature'])
    borutashap_top_features_df['Method'] = 'Borutashap' return borutashap_top_features_df,borutashap################################ Calculate borutashap #############################################################borutashap_top_features_df,borutashap = borutashap_feature_selection(train_features_v2, train_target,borutashap_estimator,borutashap_trials,borutashap_green_blue)
borutashap_top_features_df.head(n=20)

作者图片

关于 python 包的更多细节可以在这里找到。

C)汇集一切

永远不要把所有的鸡蛋放在一个篮子里

这句至理名言非常适合数据科学的大多数应用。大多数情况下,没有一个 ML 模型是没有缺陷的,但是每个模型都有自己的优点。根据我的经验,不要只依赖一种方法,如果可能的话,使用多种方法的组合总是一个好策略。这样,我们就不会受到任何方法的主要缺点的影响,并利用多种方法的优势。相同的逻辑可以应用于特征选择技术。现在我们已经学习了 8 种不同的方法,我建议读者使用它们的组合(或全部),并选择大多数方法选择的特征。下面是一个快速的 python 代码

# Methods Selectedselected_method = [corr_top_features_df, woe_top_features_df,beta_top_features_df,lasso_top_features_df,
                   rfe_top_features_df,sfs_top_features_df,borutapy_top_features_df,borutashap_top_features_df]# Combining features from all the models
master_df_feature_selection = pd.concat(selected_method, axis =0)
number_of_methods = len(selected_method)
selection_threshold = int(len(selected_method)/2)
print('Selecting features which are picked by more than ', selection_threshold, ' methods')
master_df_feature_selection_v2 = pd.DataFrame(master_df_feature_selection.groupby('Feature').size()).reset_index()
master_df_feature_selection_v2.columns = ['Features', 'Count_Method']
master_df_feature_selection_v3 = master_df_feature_selection_v2[master_df_feature_selection_v2['Count_Method']>selection_threshold]
final_features = master_df_feature_selection_v3['Features'].tolist()
print('Final Features Selected: ',final_features)
train_features_v2[final_features].hist(figsize = (14,14), xrot =  45)
plt.show()
master_df_feature_selection_v3.head(n=30)

作者图片

最后的话

恭喜你!

我们已经完成了基于 ML 的特征选择技术系列!我们深入探讨了 8 种主要方法,它们分布在不同的类别(过滤器、包装器和嵌入式)、计算难度和易于理解的程度。我们不仅学习了它背后的理论,还能够用 python 实现它们。我们还讨论了使用它们时的最佳实践。这个博客系列以及参考资料应该足以让任何人开始使用这些方法。

这里的最后一块拼图是要记住,我们在这里只涉及了科学,但是特征选择也是一门艺术。通过实践,我们会做得更好,但我想用一条建议来结束这个系列:尽可能多地使用方法,并始终根据业务逻辑来验证结果。有时,即使模型不能挑选一些关键特性,但业务逻辑要求我们使用它,我也会建议添加它。

参考材料

  1. Borutapy python 包
  2. Borutshap python 包

我们连线吧!

如果你和我一样,对 AI、数据科学或经济学充满热情,请随时在 LinkedInGithubMedium 上添加/关注我。

照片由威廉·冈克尔Unsplash 上拍摄

基于神经微分方程的深度平衡模型

原文:https://towardsdatascience.com/deep-equilibrium-models-via-neural-odes-c25a3ac8d004

结合神经网络、不动点和常微分方程的艺术

来源:https://pix abay . com/photos/sea-waves-nature-light-ripples-7484743/

介绍

最近,聪明的研究人员已经意识到,我们可以将看似无关的想法结合起来,重新解释神经网络如何工作以及我们如何训练它们。首先,如果我们将神经网络的隐藏层数增加到无穷大,我们可以将神经网络的输出视为一个不动点问题。第二,神经网络和常微分方程 (ODEs)之间有很深的联系。我们可以用微分方程求解器来训练神经网络。那么,如果我们把这两个想法结合在一起呢?也就是说,我们通过找到 ODE 的稳态来训练一个神经网络。事实证明,这种方法非常有效,这也是这篇博文的目的。

演职员表:本帖基于这篇优秀的博文,第一次看的时候完全炸了我的心。

不动点

假设我们有一个由关系式 x'=f(x)定义的动力系统。当左侧等于左侧时,达到系统的固定点:x=f(x)。为了找到一个固定点,可以从随机猜测开始,然后应用函数 f 和一定次数。

下面用固定点 cos(x)=x 来说明这个过程。我从最初的猜测 x0 = -1 开始。然后,我使用规则 x1 = cos(x0)更新我的猜测。然后我重复这个过程。经过几次迭代,xn 真的接近红线(45°线),这表明

计算定点 cos(x)=x .来源:作者的计算。

细心的读者请注意:这里一切正常的原因是因为函数 x- > cos(x)是感兴趣区间上的收缩映射。参见:https://en.wikipedia.org/wiki/Banach_fixed-point_theorem

跟神经网络有什么关系?好吧,让我们考虑一个单层 x'=NN(x)的神经网络。现在,假设我们添加另一层,使用相同的架构:x'' = NN(NN(x))。我们再做一遍那个运算:x''' = NN(NN(NN(x)))。等等…这个过程和我们上面做的真的很像,简单的不动点问题 cos(x)=x。

常微分方程

到目前为止,一切顺利。现在,让我们假设我们正在考虑一个物理系统(一个球,一个火箭,等等。)与位置 x,我们假设 f 给我们系统的速度:f(x) = dx/dt。现在 dx ≈ x_{n+1}-x_{n},所以

速度为零时物理系统不动:f(x) = 0。物理系统在 g(x)=x 时也不动,其中就是上述类型的不动点问题。重点是不动点问题和寻找颂歌的稳态之间存在联系。

定点+ ODE +神经网络

有时,我们可以找到常微分方程的精确解。大多数时候,我们不能,所以我们必须找到数字近似值。一个(绝妙的)想法是使用神经网络来近似求解。更具体地说,对于上面的函数 g,我们使用神经网络。

对于给定的 g,我们可以使用一个 ODE 求解器,它给出了 ODE 的固定点。更进一步,我们可以训练神经网络,这样对于给定的输入,ODE 的固定点就是我们想要预测的量。简而言之,这就是深度均衡模型 (DEM)的全部内容。

应用 1:学习 y=2x

首先,我们可以检查这种方法是否适用于一个非常简单的案例。这里,给定 x,我们希望 DEM 预测值 2x。下面的代码使用了 Julia,它被描述为“快速 Python”:

它应该输出类似于下图的图形:

来源:作者基于上述代码的计算。基于https://julialang.org/blog/2021/10/DEQ/的代码

事情按预期运行。然而,使用 DEM 学习函数 y=2x 感觉就像用火箭筒打死一只苍蝇。在下一个应用程序中,我们要处理一个稍微有点雄心勃勃的目标:MNIST 数据集和从图像中预测数字。

应用 2: MNIST

代码有点复杂,但主要思想保持不变。对于给定的输入,输出是 ODE 的固定点,其中 ODE 依赖于神经网络。

在这里,我们必须做更多的工作,因为我们首先要把图像转换成矢量。然后,我们使用 softmax 层将 ODE 的稳态转换为数字预测(已经包含在损失函数logitcrossentropy).中)。还要注意,ODE 层夹在另外两层之间。请随意尝试其他架构。

训练之后,您应该会看到类似这样的内容

来源:作者基于上述代码的计算。基于https://julialang.org/blog/2021/10/DEQ/的代码

DEM prediction: 2
True digit: 2

结论

这篇博客文章概述了什么是深度均衡模型。我们可以创建一个结合了神经网络、ode 和固定点的预测机器,这看起来几乎是不可思议的。读完这篇博客后,我希望你能多理解一点这个魔术背后的机制。

参考

白,,科尔特和弗拉德连科尔顿。“深度平衡模型。”神经信息处理系统进展 32 (2019)。

陈,李瑞奇,等。神经常微分方程。神经信息处理系统进展 31 (2018)。

Julia 中的可组合性:通过神经微分方程实现深度平衡模型。网址:【https://julialang.org/blog/2021/10/DEQ/

深度特征合成与遗传特征生成

原文:https://towardsdatascience.com/deep-feature-synthesis-vs-genetic-feature-generation-6ba4d05a6ca5

机器学习的两种自动特征生成策略的逐步比较

视觉效果Unsplash 上拍照

介绍

功能工程是从现有功能创建新功能的过程,目的是获取第一组功能本身没有的与目标列的关系。这个过程对于提高机器学习算法的性能非常重要。尽管当数据科学家应用特定于用例的转换时,特性工程工作得最好,但是有一些方法可以自动地完成这项工作,而不需要预先的领域知识。

在这个故事中,我们将通过一个例子来解释如何使用 ATOM 包来快速比较两种自动特征生成算法:深度特征合成(DFS)和遗传特征生成(GFG)。ATOM 是一个开源的 Python 包,旨在帮助数据科学家加快机器学习管道的探索。如果你想对图书馆有一个温和的介绍,读读这个故事。声明:这个故事的作者也是这个包的作者。

基线

让我们开始仅使用初始特征来训练模型。我们将要使用的数据是来自 Kaggle 的澳大利亚天气数据集的变体。这个数据集的目标是预测明天是否会下雨,在目标列RainTomorrow上训练一个二元分类器。

import pandas as pd
from atom import ATOMClassifier# Load the data and have a look
X **=** pd**.**read_csv("./datasets/weatherAUS.csv")
X**.**head()

初始化 atom 实例并为建模准备数据。出于解释目的,我们仅使用数据集的一个子集(1000 行)。下面几行估算缺失值并对分类列进行编码。

atom = ATOMClassifier(X, y="RainTomorrow", n_rows=1e3, verbose=2)
atom**.**impute()
atom**.**encode()

输出如下所示。

我们可以使用dataset属性快速检查数据在转换后的样子。

atom.dataset.head()

数据现在可以被模型接收了。在这个故事中,我们将使用 LightGBM 模型进行预测。使用 atom 训练和评估模型非常简单:

atom.run(models="LGB", metric="accuracy")

我们在测试集上实现了 0.8471 的精度。还不错。让我们看看自动化特征生成是否能在这方面有所改进。

深度特征合成

DFS 对现有特征应用标准的数学运算符(加、减、乘等),进行组合。例如,在我们的数据集上,DFS 可以创建新的特征MinTemp + MaxTempWindDir9am x WindDir3pm

为了能够比较这些模型,我们为 DFS 管道创建了一个新的分支。如果你不熟悉 ATOM 的分支系统,看看这个的故事。

atom.branch = "dfs"

使用 atom 的 特征生成 方法在新分支上运行 DFS。为了简化,我们只使用加法和乘法来创建新的特征(使用divlogsqrt操作符可以返回带有infnan值的特征,这些值必须再次估算)。

atom.feature_generation(
    strategy="dfs",
    n_features=10,
    operators=["add", "mul"],
)

注意: ATOM 使用头罩下的 featuretools 包来运行 DFS。

由于我们使用 n_features=10,从所有可能的组合中随机选择的 10 个要素被添加到数据集中。

atom.dataset.head()

使用以下内容再次训练模型:

atom.run(models="LGB_dfs")

注意两件事:

  • 我们在模型的首字母缩略词后添加标签_dfs,以避免覆盖基线模型。
  • 不再需要指定用于验证的指标。atom 实例将自动使用与之前模型相同的标准。在我们的例子中,准确性。

看起来 DFS 并没有改进模型。结果甚至变得更糟。让我们看看 GFG 表现如何。

遗传特征生成

GFG 使用遗传编程(进化编程的一个分支)来确定哪些特征是成功的,并在此基础上创造新的特征。DFS 盲目地尝试各种功能的组合,而 GFG 则试图通过每一代算法来改进其功能。GFG 使用与 DFS 相同的操作符,但它不是只应用一次转换,而是进一步发展它们,创建功能组合的嵌套结构。仅使用运算符 add (+)和 mul (x),一个示例特征可能是:

add(add(mul(MinTemp, WindDir3pm), Pressure3pm), mul(MaxTemp, MinTemp))

与 DFS 一样,我们首先创建一个新的分支(从最初的master分支中,将 DFS 排除在管道之外),然后我们训练和评估模型。同样,我们创建了 10 个新功能。

注意: ATOM 使用头罩下的gp learn包运行 GFG。

*atom.branch = "gfg_from_master"atom.feature_generation(
    strategy="GFG",
    n_features=10,
    operators=["add", "mul"],
)*

可以通过genetic_features属性访问新生成特征的概述、它们的名称以及它们的适应性(在遗传算法中获得的分数)。

atom.genetic_features

请注意,由于要素的描述可能会变得很长,新要素被命名为feature n,其中 n 代表数据集中的第 n 个要素。

atom.dataset.head()

再次运行该模型会产生:

atom.run(models="LGB_gfg")

这一次我们获得了 0.8824 的准确度分数,比基线模型获得的 0.8471 好得多!

结果

现在这三个模型都训练好了,是时候分析结果了。使用results属性获得所有模型在训练集和测试集上的分数。

atom.results

使用 atom 的绘图方法进一步比较模型的特性和性能。

atom.plot_roc()

让我们来看看哪些特征对模型的预测贡献最大。使用 atom 的画布绘制多个相邻的地块。

*with atom.canvas(1, 3, figsize=(20, 8)):
    atom.lgb.plot_feature_importance(show=10, title="LGB")
    atom.lgb_dfs.plot_feature_importance(show=10, title="LGB + DFS")
    atom.lgb_gfg.plot_feature_importance(show=10, title="LGB + GFG")*

对于两个非基线模型来说,生成的特征似乎是最重要的,这表明新特征与目标列相关,并且它们对模型的预测起着重要作用。

使用决策图,还可以查看数据集中各个行的特性的影响。

atom.lgb_dfs.decision_plot(index=0, show=15)

结论

我们比较了使用两种自动特征生成技术生成的新特征丰富的数据集上训练的分类器的性能。结果表明,使用这些技术可以大大提高模型的性能。当然,请注意,这只是一个用于解释目的的模拟示例。在真实用例中改进模型可能要复杂得多。

关于 ATOM 的更多信息,请看软件包的文档。对于 bug 或功能请求,请不要犹豫,在 GitHub 上提出问题或给我发电子邮件。

相关故事:

参考资料:

  • 天气数据由澳大利亚政府根据知识共享属性许可发布,并从 Kaggle 收集。
  • 这个故事中所有没有说明的图片都是作者创作的。

深度前馈神经网络及其再激活函数的优势

原文:https://towardsdatascience.com/deep-feed-forward-neural-networks-and-the-advantage-of-relu-activation-function-ff881e58a635

深刻的俳句:教 GPT J 用音节模式作曲

原文:https://towardsdatascience.com/deep-haiku-teaching-gpt-j-to-compose-with-syllable-patterns-5234bca9701

如何用音位微调一个大变压器后生成有节奏的散文

作者图片,图片来源于 Unsplash 上的 Diana Polekhina

在这篇文章中,我将向你展示我是如何微调一个名为 GPT J 的人工智能系统来创造新的俳句,这是一种源于日本的短诗。关键是让我的模型,Deep Haiku,看到并理解诗中的音节数。

我通常不会在我的文章中显示目录,但我认为这是值得的,因为我在这个项目中使用了各种各样的技术。如果您对这些主题感兴趣,请随意跳到下面的任何部分:

  • 用 FastPunct 给文本添加标点和大写;
  • 使用 GRUEN 评估生成文本的质量;
  • 用 KeyBERT 从文本中抽取主题:
  • 用 Phonemizer 将文本拆分成音节和音素;
  • 对变形金刚进行多任务训练;
  • 微调 GPT-J 8 位谷歌 Colab(免费!);
  • 使用解毒功能标记淫秽和威胁性文本;

如果你想为某个特定的主题创作俳句,你可以在这里使用 T4。请务必查看附录中生成的俳句。

背景

正如我们在波士顿所说,OpenAI 的 GPT-3 语言生成模型非常棒。你可以问任何问题,它会给出一个合理的、往往很有见地的答案。如果你让它产生创造性的散文,比如诗歌,它会做得出奇的好。

但是 GPT-3 和大多数其他语言模型似乎不能使用格律(诗歌中常用的节奏结构)来写散文。这是因为单词在模型中的表现方式。大多数语言模型使用单词部分,而不是字母或音节作为数据类型。语言生成系统根本不知道单词中有多少音节,所以它们无法提取和复制任何节奏模式。

例如,这里有一个与 OpenAI 的 GPT-3 的交互,我询问音节,并提示它创建一个俳句。请注意,我的提示是粗体的,响应文本来自使用默认参数的 GPT-3 达芬奇模型。

GPT 三号游乐场,图片作者

好了,它清楚地知道什么是音节,以及俳句常用的音节数[5,7,5]。但是当我让它写一首关于秋天的俳句时,它想出了一首可爱的诗,音节数是[5,6,7]。

这里有一些由 GPT-3 写的四季生成的俳句和我的新模型,深度俳句。仪表显示为灰色。

GPT-3 的四季俳句和作者的深俳句

正如你所看到的,两个系统都创造了一套很好的诗歌。我会让你来判断散文的质量,但很明显,GPT-3 不知道如何遵循标准的米。相比之下,深度俳句在所有四种俳句中都使用了[5,7,5]音步。

先前的工作

显然,我不是唯一一个希望获得一个转换器来生成带有计量散文的文本的人。例如,在他的论文“俳句生成,一种基于变压器的方法,有很多控制,”贾科莫·米塞利指出,典型的俳句格律模式并没有严格遵循[1]。

现代的,尤其是英语的俳句并不严格遵循 5-7-5 的模式,而是通常坚持大约 10/12 个单词的短-长-短形式。—贾科莫·米塞利

米塞利的海辜系统也创造了一些优秀的散文,但在本文的 12 个例子中,只有 1 个遵循[5,7,5]模式。请注意,我借用了第一行,看看深俳句会把它带到哪里。他们来了。

样本俳句,由俳句和深俳句组成,由作者列表

米塞利引用了另一篇论文,直接论述了生成散文的韵律。不是用来写俳句的;是用来写打油诗的。在他们的论文“曾经有一个非常糟糕的诗人,它是自动化的,但你不知道它,”王建友等人讨论了他们使用 open ais 2 模型的早期实验。

一个 GPT-2 的简单实现不能产生原始的和有效的打油诗。GPT-2 倾向于生成超过打油诗音节限制的长句。为了满足音节限制,我们需要截断生成的句子,这就产生了结尾不正确的行。—王健友,等。

他们接着描述了一个用于生成五行打油诗的自定义转换器 LimGen,它根据词性、音节数和重读音节的位置来选择单词。

对于深度俳句,我构建了一个系统,通过微调一个通用转换器,根据用户指定的提示生成俳句,并遵循[5,7,5]节拍。

概观

下面是我用来训练和运行 Deep Haiku 的组件和流程图。在快速讨论了每个部分的作用之后,我将更详细地讨论这些内容。

深俳句组件,作者配图

我首先从用户 hjhalani30 和 bfbarry 那里下载了两个 Kaggle.com 俳句数据集。数据集分别在 CC0 和 CC-BY 许可下发布。组合数据集中的俳句数量超过 140,000。我使用 FastPunct 为俳句添加标点和大小写,并运行 KeyBERT 模型[3]来提取用作提示的短语。

然后,我通过使用 GRUEN metric 来衡量文本的质量[4]和 phoenemizer 库[5]来过滤数据,以计算音节并将提示和俳句转换为音素。过滤产生了超过 26K 的相对高质量的俳句,它们都有[5,7,5]音步。

我使用了 Eluther [6]的 GPT-J 6B 模型作为深层俳句的基础。在将模型量化为 8 位以在谷歌 Colab 上运行后,我使用过滤后的俳句作为十个时代的训练数据对其进行了微调,这在谷歌 Colab 上用了 11 个小时,使用了特斯拉 V100 GPU。

生成新的俳句从选择一个词或短语作为提示开始,比如“秋天”我用微调过的模型创造了 20 首候选俳句。对结果进行过滤以符合量表,并使用解毒库选择性地过滤以移除包含明确语言的候选者。是的,深俳句懂得骂人。剩下的候选人将与分数一起显示。

例如,我生成了 20 个带有提示“秋天”的俳句,11 个使用了[5,7,5]格律。这是过滤后的结果和分数。

深俳句的输出示例,作者列表

好吧,前三名看起来不错,虽然有点老土。但他们都没有使用脏话,所以毒性几乎为零。唯一轻微的亮点是第六个提到了“世界末日”和“死亡的尖端”对于一首关于秋天的俳句来说有点暗,但也不算冒犯。

克里斯·劳顿Unsplash 上拍摄

系统详细信息

培训用数据

和我的许多项目一样,这个项目从获取训练数据开始。如果你没有好的训练数据,很难得到一个好的工作的 AI 模型。

幸运的是,Kaggle 上至少有两个合适的俳句数据集。bfbarry 的第一个包含了超过 11K 的收集和清理的俳句。他在知识共享 CC0 许可下发布了数据集。第二个俳句数据集来自 Kaggle 上的 hjhalani30。这是一个从不同地方聚集起来的更大的数据集。收藏中有超过 14 万首俳句。超过 110,000 条的绝大多数来自与#twaiku 标签相关的 Twitter。这个数据集是在知识共享 CC 4.0 许可下发布的。

使用 FastPunct 为文本添加标点符号和大写字母

我注意到第一个数据集中的俳句都是小写的,没有标点符号。我知道以这种方式写诗是一种风格选择,但我决定添加大写字母和标点符号,因为这有助于下一步的文本质量分析。为此,我使用了 FastPunct 模块。

这里有一些 Python 中的示例代码,它为数据集中的一个俳句添加了标点符号和大小写。

from fastpunct import FastPunct

fastpunct = FastPunct()
print(fastpunct.punct(["""**was it all a dream
                          i most certainly hope not
                          that was happiness**"""]))
# Output:
# **Was it all a dream?** # **I most certainly hope not.** # **That was happiness.**

请注意,将每个句子的第一个单词大写也会将第一行识别为疑问句,并相应地对其进行标点。下面是数据集中的几个俳句来说明 FastPunct 的功能。

使用 FastPunct 前后来自 Bfbarry 数据集 的俳句,表由作者提供

请注意 FastPunct 如何对这些样本做了更多的工作。最后一个例子在“hide”后面加了一个逗号,在缩写的“cause”前面加了一个撇号。

第二个数据集中的一些俳句有大小写和标点,但很多没有。因此,为了保持一致性,我去掉了标点符号和大小写,并将其放回 FastPunct 中。以下是该数据集中的一些示例。

使用 FastPunct 前后来自 Hjhalani30 数据集 的俳句,表由作者提供

综合这些数据集,我得到了超过 15 万首俳句。然后,我使用以下步骤中描述的方法对它们进行过滤。

使用 GRUEN 评估生成文本的质量

如果你最近一直在阅读自然语言处理(NLP),你可能听说过 BLEU 的度量标准及其变体。BLEU 代表双语评估 Understudy,是一种使用计算机过程评估从一种自然语言翻译成另一种自然语言的文本质量的算法。您有翻译任务的前后文本,BLEU 让您知道生成的文本是否与人类编写的预期翻译相匹配。你可以在 Renu Khandelwal 关于 TDS 的文章中阅读 BLEU 算法是如何工作的。

尽管 BLEU 在根据预期结果评估文本质量方面表现良好,但它并不能帮助确定创造性写作的质量。因此,我使用了一个名为 GRUEN 的自动化系统来评估俳句数据集中使用的散文的质量。

由朱和 Suma Bha 开发的 GRUEN 系统[4]试图使用 Hoa Trang Dang 在 2006 年文档理解会议(DUC) [9]上提出的三种质量来自动评估文本。

Q1:语法- 文本不应该有大写错误或明显不合语法的句子(例如,片段,缺少组件),使文本难以阅读。

Q2:无冗余——不应该有不必要的重复。

Q3:焦点-文字要有焦点;句子应该只包含与整个文本相关的信息。

下面是一些展示如何使用 GRUEN 的示例代码。

import GRUEN.Main as gruen
doc =["**Dendelion blooms. In dapples of sunshine. The first brushstrokes of spring.**"]
print(gruen.get_gruen(doc)[0])# Output **0.72511**

GRUEN 度量的结果是一个从 0.0 到 1.0 的单一数字,它是三种文本质量的总和,其中越大越好。这是上面八首俳句的格林评分。

来自数据集 的俳句,带有 GRUEN 质量评分,作者列表

你可以看到系统是如何给含有三个独立句子或从句的俳句评分的,而不是那些由一个句子任意分成三部分的俳句。我只过滤了 0.5 分以上的俳句,得到了 45K 个样本。

用 KeyBERT 从文本中提取主题

因为我想对模型进行微调,以在给定主题的情况下创建新的俳句,所以我需要从训练集中的俳句中提取一个关键字或短语来调节系统。为此,我求助于 KeyBERT 系统,它已经被训练成从文本中提取关键词。这是我使用的源代码。

from keybert import KeyBERT
kw_model = KeyBERT()
doc = """**An old silent pond.
         A frog jumps into the pond.
         Splash! Silence again.**"""keywords = kw_model.extract_keywords(doc, keyphrase_ngram_range=(1, 2), stop_words=None)
print(keywords[0][0])# Output: **silent pond**

KeyBERT 系统是“提取的”,这意味着它总是选择包含在源文本中的单词或短语。以下是系统从一组俳句样本中提取的主题。

数据集 中的俳句,主题来自 KeyBERT ,作者列表

正如你所看到的,它从每首俳句中找到了一个重要的单词或短语。我用这段摘录的文字作为主题,让 GPT-J 在训练中根据提示写俳句。

用 Phonemizer 将文本分成音节和音素

正如我上面提到的,Transformer 模型使用单词部分进行文本编码,因此不能“看到”音素。这使得模型几乎不可能在文本中找到和复制音节模式。为了解决这个问题,我将样本俳句翻译成带有音节中断的音素,以教模型如何在文本中看到节拍。

在尝试了几种将文本从英语转换成音素的技术后,我发现 Phonemizer 项目效果最好。

Phonemizer 可以使用以下后端:ESpeak、ESpeak-Mbrola、Festival 和 Segments [5]。其中,我发现节日最适合我手头的任务。Festival 是一个文本到语音的引擎,只支持美国英语。我没有用它来从文本中创建声音文件,因为它是被设计来做的。然而,我确实用它将语音转换成音素,并在音节级别进行标记化。下面是一些示例代码,展示了如何使用这个包。

from phonemizer import phonemize
from phonemizer.separator import Separatordoc = """**Awaken before dawn.
         I hear the city rising.
         The new day begins.**"""phn = phonemize(doc, language='en-us', backend='festival',
                with_stress=False, separator=Separator(phone=None,
                word=' ', syllable="|"), strip=True)
print(phn)# Output:
# **ax|wey|kaxn biy|faor daon**
# **ay hhihr dhax sih|tiy ray|zaxng**
# **dhax nuw dey bax|gihnz**

阅读 Festival 的音素输出有点困难,但您最终可以找到窍门。请注意,我使用|字符分隔音节,因为这将有助于 GPT-J 计算出音节数。

例如,单词wake在带有音节标记的节日注音中被写成 ax|wey|kaxn ,而在标准的国际音标中则被写成 əˈwākən

这里有一些明文和节日注音的俳句样本。

来自数据集 的样本俳句,由音素化器生成,表格由作者生成

在获得文本和音素形式的数据集后,我使用音节计数来只使用[5,7,5]格律的俳句进行训练。然后,我使用俳句的文本和音素版本进行多任务学习,以微调 GPT-J。这将训练数据集筛选到 26K 个样本。

执行变形金刚的多任务训练

多任务学习是由卡内基梅隆大学的 Rich Caruna 开发的一种训练机器模型的方法。在他的论文“多任务学习”中,他指出,该技术通过使用相关任务的训练数据中包含的领域信息来提高泛化能力。它通过使用共享表示并行学习任务来做到这一点;每个任务学到的东西可以帮助其他任务学得更好[7]。

对于深度俳句,我对 GPT J 进行了微调,教它执行以下四项任务:

  1. 使用文本为给定主题生成俳句;
  2. 使用音素为给定主题生成俳句;
  3. 将俳句从文本翻译成音素;
  4. 将俳句从音素翻译成文本;

注:我用括号将第一个任务的文本括起来,用尖括号将第二个任务括起来,用方括号将第三个任务括起来,用花括号将第四个任务括起来。我这样做是为了提示文本生成器知道哪个任务是哪个任务。我用等号来分隔所有任务的输入和输出。例如,这里有一个样本俳句的四行训练数据。

(encouragement = Need encouragement. / Making myself positive. / I want happiness.)<axn|ker|axjh|maxnt = niyd axn|ker|axjh|maxnt / mey|kaxng may|sehlf paa|zax|tihv / ay waant hhae|piy|naxsy>[need encouragement / making myself positive / i want happiness = niyd axn|ker|axjh|maxnt / mey|kaxng may|sehlf paa|zax|tihv / ay waant hhae|piy|naxs]{niyd axn|ker|axjh|maxnt / mey|kaxng may|sehlf paa|zax|tihv / ay waant hhae|piy|naxs = need encouragement / making myself positive / i want happiness}

注意,用于生成明文中的俳句的主题被指定为文本,并且用于生成音素中的俳句的主题被指定为音素。

我希望训练系统同时学习这四项任务能帮助它写出有趣且连贯的[5,7,5]格律的俳句。

微调 GPT-J 8 位谷歌 Colab(免费!)

为了让 GPT-J 理解、学习和执行所有四项任务,我使用了一个有 60 亿个参数的变压器模型。据 eluther.ai 报道,GPT-J 6B 模型的大小与 OpenAI 的居里模型相当,后者是他们的第二大 GPT-3 模型。他们最大的模型 davinci 拥有高达 175B 的参数。

目前,谷歌 Colab 只使用 16g 内存的 GPU,32 位版本的 GPT-J 6-B 将耗尽内存。为了使用 Google Colab 进行微调,我使用了他们的 8 位版本的模型。关于它如何工作的详细解释可以在这张模型卡中找到。

我的训练系统是基于的作品。它使用了微软的 Edward Hu 等人提出的低秩自适应技术[8]。

当我们预训练更大的模型时,重新训练所有模型参数的完全微调变得不太可行。以 GPT-3 175B 为例,部署微调模型的独立实例,每个实例都有 175B 参数,这是非常昂贵的。我们提出了低秩自适应,即 LoRA,它冻结了预训练的模型权重,并将可训练的秩分解矩阵注入到变压器架构的每一层中,从而大大减少了下游任务的可训练参数的数量。—爱德华·胡等。

我在 Google Colab 上对系统进行了 11 个小时的训练。以下是我用于训练的参数,基于尼基塔·施耐德的文章

num_train_epochs = 10
batch_size = 2
warmup_steps = 100
weight_decay = 0.01

这是训练好的模型对 rain 主题的输出:

在雨中行走,
人行道闪耀着古老的记忆

-深刻的俳句

所以它看起来起作用了!经过微调的模型可以生成更多情况下遵循[5,7,5]模式的俳句,这比没有通过音素进行多任务学习的模型好得多。它似乎有能力从最初的训练中挖掘背景。例如,这里有三个关于人工智能和人工智能的俳句:

AI 和 ML /能够预测/你在想什么
AI 和 ML /不能发展一个灵魂/它们只是数字
AI 和 ML /和我们一样是种族主义者/我们才是问题所在

注意,在训练数据集中没有任何关于人工智能和人工智能的俳句。出于风格的考虑,我去掉了所有的尾随句点。

使用解毒功能标记淫秽和威胁性文本

正如我前面提到的,Deep Haiku 知道如何使用淫秽内容,因为我没有过滤训练数据以删除明确的内容。即使我这样做了,它可能还是会偶尔使用脏话,因为它最初是在一个大型的、未经过滤的文本语料库上训练的。

为了标记或过滤显式文本,我使用解毒模块来检查深层俳句的输出。解毒寻找以下类型的言论:有毒,严重中毒,淫秽,威胁,侮辱,身份攻击[9]。

以下是该系统如何对维基百科上的言论页面上发现的有毒评论进行评级。

维基百科对话页面上的有毒评论,来源: Kaggle ,作者列表

由于各种原因,所有这些评论在毒性方面得分很高。就我的目的而言,我过滤掉所有有毒的俳句,让家人安全地观看。

这里有更多关于雨的俳句,有质量和毒性的分数。

深俳句输出样本,作者列表

请务必在附录中查看更多生成的俳句,或在此创建自己的

讨论

如上图,你可以用多任务学习来教大型变形金刚数音节。注意不是 100%完美;许多生成的俳句不遵循[5,7,5]模式。但它确实极大地增加了遵循它的可能性。它似乎保留了许多不同科目的原始语言训练。

对于未来的工作,可能有一种方法可以为 Transformer 模型使用基于音素和音节而不是单词部分的定制标记器。该系统本来就能够看到和复制散文的韵律。这样做可以消除多任务学习的需要。棘手的部分将是保留在 word 部件上执行的初始训练的知识。

源代码和 Colabs

这个项目的所有源代码都可以在 GitHub 上获得。我在 CC BY-SA 许可下发布了源代码。

知识共享署名共享

感谢

我要感谢詹尼弗·林和奥利弗·斯特瑞普对这个项目的帮助。

参考

[1] 俳句,g .米塞利,俳句生成,一种基于变压器的方法,具有大量控制 (2021)

2 林根,j .王等人曾经有一个很烂的诗人,是自动化的但是你不知道 (2021)

[3] M. Grootendorst, KeyBERT :用 BERT 进行最小关键词提取(2020)

[4] W. Zhu 和 S. Bhat, GRUEN 用于评估生成文本的语言质量 (2020)

[5] M. Bernard,Phonemizer:Python 中多种语言的文本到电话转录 (2016)

[6] GPT-J ,网格-变压器-JAX:模型-与 JAX (2021)并行实现变压器语言模型

[7] R .卡鲁阿纳,http://www.cs.cornell.edu/~caruana/mlj97.pdf(1997)

[8] E .胡等, LoRA :大型语言模型的低秩适应 (2021)

[9] L. Hanu 与《酉队》,https://github.com/unitaryai/detoxify(2020)

附录

以下是针对以下主题的深度俳句的输出示例。这些是我认为每批 20 个中最好的。

COVID

冠状病毒
用面罩封锁
垂死挣扎

理发

刚剪了头发
我的头发不再浓密了
现在更精致了

我仍在努力寻找欢笑和泪水的最佳组合

早晨

早晨带给我们新的一天,让我们重新开始。让我们看看我感觉如何

音乐

鬼魂从来没有去过音乐区是有原因的

计算机编程语言

精通 Python 语言是一个很大的优势

为了无限制地访问 Medium 上的所有文章,成为会员,每月支付 5 美元。非会员每月只能看三个锁定的故事。

PyTorch 中的深层图像先验

原文:https://towardsdatascience.com/deep-image-prior-in-pytorch-e6edf666a480

无数据随机网络图像去噪

图一。浸渍管道。单个图像用于训练,目的是从噪声中重建图像。最终,网络学会重建图像的去噪版本。作者创建的图像。

深度学习和神经网络一直与大数据紧密相关。无论是语言翻译的图像分类,您几乎总是需要大量的数据来提高模型适用于真实世界数据集的任务准确性。即使在少量或一次性场景下,初步的情况是你仍然需要大量的数据来训练网络。但是,如果我告诉你,你不需要任何数据或任何预先训练的网络,但你可以执行图像恢复甚至超分辨率呢?

在本文中,我们将深入到深度网络的一个完全不同的领域,即深度图像先验(DIP),它不需要任何数据集进行训练,但学会分离噪声和图像来执行图像恢复。我们将详细讨论 PyTorch 教程,展示 DIP 的强大功能。

什么是深度图像先验?

图 1 简单展示了 DIP 的工作原理。出乎意料的简单。你首先有一个随机初始化的网络,目的是从纯噪声中重建目标图像。然后将网络的输出重建与原始图像进行比较,以计算损失函数,随后更新网络。经过一些迭代,你会惊讶地发现,网络将开始输出原始图像的“去噪”版本。

本质上,我们的整个训练过程是优化网络以保留图像的先验信息,因此命名为“深度图像先验”。

那么,为什么会这样呢?

理论上,一个网络应该能够拾取图像的所有方面,从粗糙到细节,其中也包括固有的噪声。然而,实际上,在最终拾取噪声并因此“过度拟合”整个图像之前,网络更可能拾取图像内的连贯和一致的特征。因此,如果我们在过度拟合之前在中间停止训练,网络输出变成原始图像的干净版本,服务于我们的图像恢复目的。

计算环境

库和硬件要求

这个实现是建立在 PyTorch 和 OpenCV 之上的。通常情况下,神经网络与 GPU 一起进行并行计算会更好。然而,由于仅使用我们正在去噪的单个图像的 DIPs 的特殊性质,CPU 是足够的。

下面是导入库和引入 GPU(如果有的话)的代码:

履行

网络体系结构

根据最初的 DIP 文件,不同的网络架构工作方式不同。我们按照论文建议的设置创建了一个带有跳跃连接的沙漏网络。

以下是网络的实现:

培养

训练是相当非正统的,因为我们只有一个图像,必须顺序优化它。这意味着我们完全忽略了 PyTorch 的批量训练能力。请注意,网络确实会考虑批次维度,因此我们必须在计算损失之前取消图像排队。

以下是培训的实施情况:

结果

图二。DIP 结果。作者创建的图像。

我们提供 100、500、1000 和 2000 年后的结果如下。从图 2 中可以看出,DIP 网络首先学习整个图像的干净特征,在中间产生图像的干净版本。然而,随着训练的进行,在分辨率增加的同时,一些噪声也被带入图像中。

“二次探底”

有趣的是,在 DIP 论文之后,Gandelsman 等人提出了一种称为双 DIP 的变体,他们发现同时优化两个先验可以鼓励网络分别学习特征,从而导致有意义的图像分解,甚至前景和背景分离。

全文可以在这里找到https://arxiv.org/abs/1812.00467

结束注释

现在你知道了!一个网络,一个图像,没有任何数据,你可以从头开始执行图像去噪和恢复。DIP 的完整实现可在此处找到:

**

感谢您坚持到现在🙏!我会在计算机视觉/深度学习的不同领域发布更多信息,所以如果你有兴趣了解更多信息,请加入并订阅*https://taying-cheng.medium.com/membership!一定要看看我关于计算机视觉方法的其他文章!***

Deep Lake —大规模管理深度学习数据的架构蓝图—第一部分

原文:https://towardsdatascience.com/deep-lake-an-architectural-blueprint-for-managing-deep-learning-data-at-scale-part-i-effafd488e7c

图片由作者使用 vqgan + clip (“水下机器人世界| art station 上的趋势”,1000 iter)。)

介绍

在过去的几年里,机器学习数据管理实践发生了巨大的变化,引入了新的设计模式和工具,如特征库、数据和模型监控实践以及特征生成框架

机器学习的数据管理方面的大多数进展都集中在经典(基于特征的)数据上,不能按原样应用于非结构化数据,留下了深度学习数据管理实践

本系列的目标是描述 Deep Lake — 一个用于管理深度学习数据的架构蓝图,它可以被视为数据湖、特征库和深度学习评估库之间的交叉。

本系列的第一部分将强调管理深度学习应用数据的独特挑战,并在高层次上介绍 Deep Lake

本系列的第二部分将深入探讨该蓝图支持的特定设计模式,以及关于实现和技术堆栈选择的注意事项。

范围

这篇文章涵盖了深度学习的用例,其中原始数据包括图像、视频、音频或其他信号,通常表示为大型二进制数据(相对于表格数据的深度学习)。

这篇文章中的例子指的是深度学习任务,这些任务可以用结构化目标来表示(例如分类、对象检测、音素预测等),但也可以应用于输出非结构化数据的生成模型。

警告:重点是那些至少部分依赖于带注释数据集的任务(相对于大规模自我监督方法)。

观众

这篇文章假设读者熟悉机器学习和深度学习的实践和术语,以及数据工程设计模式和实践的工作知识。

深度学习数据生命周期 101

本节的目标是从高层次上描述在深度学习模型/系统的生命周期中产生和消费数据的方式。

作者图片

数据采集

团队经常使用多个数据源来训练深度学习模型,其中数据以不同的节奏和不同的格式到达。

  • 从外部数据源获取数据集是很常见的——比如开放数据集或从第三方一次性提取的数据。
  • 在某些情况下,数据也是使用模拟 生成算法合成生成的,或者通过主动活动(例如,准备并捕捉携带武器的人的图像)或搜索数据(例如,谷歌街景或特斯拉的车队)。
  • 只要有可能,生产数据就被用来创造一个模型改进的良性循环——这通常只能在初始模型发布后才能创造。

数据管道

在数据可以使用之前,我们经常需要对其进行转换、清理和丰富。

这个过程需要处理大量的二进制数据,这使得它很有挑战性。

  • 非结构化数据的转换可以简单到缩放和裁剪、采样编码、或者在更复杂的情况下使用预训练模型(可能需要也可能不需要定期训练和验证)将数据嵌入到向量空间中。
  • 清理通常意味着标记损坏的数据,比如空的数据文件或者不合理的注释。
  • 丰富可以包括通过编程从原始字节中提取感兴趣的数据(例如每幅图像的“照明条件”)。
  • 另一种不同类型的丰富是使用人工标注者创建标签——人工标注任务可能像使用众包平台对猫和狗进行分类一样简单,或者需要严格的、受监管的、多领域专家标签,并带有解决专家间分歧的强制协议。
  • 最后,丰富可能包括在训练过程之前对数据的扩充,尤其是如果扩充是一个繁重的过程。

构建数据集

通常,团队可以访问不能全部用于建模的数据池——特别是当任务需要标记时。

  • 随机选择数据通常是不够的——因为我们需要确保数据集代表足够广泛的****分布(例如,照明条件、背景、性别等可变性。).
  • 数据集通常还必须包含罕见且困难的边缘案例的表示(对于关键任务应用程序尤其常见)。
  • 因此,一个常见的工作流程是缩小数据范围,并使用某种形式的分层技术对其进行采样,然后由人工注释者创建标签。
  • 有时,数据集创建是一个迭代循环(例如,当需要一些标签来引导数据集时),并且通常遵循一个复杂且特定于领域的工作流(在第二部分中有更多关于这个的内容)。
  • 最后,在训练和评估中经常使用多个数据集来从不同方面测试模型的行为。

培训、验证和测试

当然,这些过程消耗数据集,但是也产生数据。

  • 两种型号性能的指标报告
  • ****模型预测数据,包括可解释数据、置信度得分等。
  • 实验谱系、训练模型、指标等。通常存储在一个实验跟踪系统中。

生产数据捕获和监控

一旦将模型部署到生产环境中,就需要监控原始数据和预测,以确保模型正确运行。

  • 通常监控标签的分布或它们的属性(类别分布、边界框大小)以及预测置信度等。
  • 很多时候,我们可以从生产系统中捕捉信号,这些信号可能表明我们的算法执行得有多好——例如,通过产品本身从用户那里收集明确或隐含的反馈。
  • 如果可能的话,获取这些生产数据是很有用的,因为我们可以用它来改进模型的下一个版本。

深度学习数据的独特挑战

在高层次上,深度学习模型的生命周期与经典的 ML 模型没有太大的不同。然而,深度学习有一些独特的差异,当涉及到数据管理时,这些差异会带来相当大的挑战:

大型非结构化有效载荷

在传统的 ML 中,单个有效载荷由特征构成,并且对于单个模型示例,通常可以用 1K 以下的数据来表示,这使得利用专门为结构化数据构建的各种存储、查询和可视化引擎变得相对简单。

另一方面,非结构化数据:

  • 通常不能或不应该存储在数据库引擎中
  • 不附带声明性查询语言(如 SQL)
  • 没有专门的观众是无法“目测”的

这需要设计用于存储、查询和可视化非结构化数据的定制解决方案

数据量

在经典的 ML 中,我们通常可以完全依赖系统本身产生的数据(例如,用户点击或购买、交易等)。)—并无限期存储几乎所有相关数据。

在 DL 中,完全收集生产数据非常昂贵的情况并不少见(想想无限期存储特斯拉所有汽车拍摄的所有视频)。

此外,一段时间后,来自生产系统的 99.9%的数据对改进模型没有什么价值,剩余的 0.1%变得很重要,但很难获得(例如,与安全相关的罕见边缘情况)。

在一些深度学习系统中,数据采集或数据处理管道需要能够识别和过滤感兴趣的数据,以便长期保留

数据量的另一个挑战是,训练深度学习模型需要高端、快速的存储(和独特的数据格式),以便优化从磁盘到 GPU 内存的数据加载路径。

由于这种存储级别非常昂贵,并且数据量很大,我们被迫在更便宜的长期存储中管理“冷”数据,该存储针对吞吐量、成本进行了优化,并支持对单个示例的随机访问(以便能够标记和关注示例)。

深度学习数据通常需要具有不同性价比的多个存储层,并针对不同的消费者以不同的存储格式存储

数据标记

在经典 ML 中,我们经常“免费”获得所有数据示例的标签,或者可以通过编程从其余数据中获得它们。

在深度学习中,我们经常需要执行一些注释,通常遵循复杂、昂贵和漫长的过程——包括利用人工注释器。

管理深度学习数据的反模式

管理用于深度学习的庞大且不断增长的数据语料库可能是一项艰巨的任务。下面是 DL 团队有时会遵循的一些常见的反模式:

将数据准备视为临时任务 团队执行从原始数据到为 GPU 准备的训练样本的整个数据准备过程,作为单个模型开发或改进工作的一部分,这并不罕见。

这相当于从生产数据库的副本开始开发经典的 ML 模型版本(这是 5-10 年前的行业)。

存储丛林

这使得项目和团队之间的数据共享成为一个巨大的挑战,因为大多数数据都不容易访问,更不用说发现了。

格式面条 即使数据可以在一个中心位置访问,每个数据源甚至每个数据集都以不同的格式存储也并不罕见——无论是在如何将非结构化数据组织到文件夹中方面,还是在如何表示标签方面。

这增加了组合来自多个来源和项目的数据的工作量。

维度忽略 管理包含领域数据的良好表示的数据集需要团队了解他们的(二进制)数据中有什么——例如,收集白天和夜晚场景的图像。

但是对于万亿字节的数据,你怎么知道哪张图像包含白天的场景还是夜晚的场景呢?

一种手工方式是观察足够多的示例,并以特定的方式收集它们。然而,这既费时又重复,而且对于不常见的情况(如“夜间冰雹风暴”)效果不佳

每次您想要改进您的模型时,手动进行这项工作的成本是令人望而却步的,因此团队通常在他们的数据集中没有足够好的分布。

技术发展水平

领先的科技巨头如何应对这些挑战?不幸的是,关于这个话题的文章并不多。

优步·ATG

一篇来自优步的博客帖子描述了https://eng.uber.com/petastorm/****—一个由自动驾驶小组创建的库,它有助于以不同格式存储和表示数据集,对查询和训练都有用。****

作者图片

帖子还提到了优步的自动驾驶“数据湖”,它统一了来自多个来源的数据,对其进行预处理,并以优化的方式存储,以便在数据集生成过程之前进行查询和检索。

参考上面的数据生命周期 101 图,优步的内部平台似乎包括:

  1. 从原始数据到通用/有用格式的数据管道
  2. 一个带有数据模式的数据湖,可以方便地访问数据(通过 HDFS 和拼花地板)
  3. 数据之上的查询引擎(Apache Spark ),它通过将一些信息提取到可查询的列中来支持对数据的高效查询,并对存储进行建模以简化时序数据的检索。
  4. 能够轻松地将数据转换为高效的培训和特定于框架的数据格式(使用 PetaStorm 格式)

一个没有提到的问题是,人类是否以及如何访问数据进行目测或标记——因为提到的格式不允许非常容易地进行点查询。

特斯拉

在最近特斯拉的人工智能日的谈话中,特斯拉的人工智能团队似乎建立了:

  1. 具有角色和场景库的合成数据生成器
  2. 一个自动预处理工具,可以为交叉点和其他区域创建注释(可能需要人工标注)

所有迹象表明,这些任务是一个更大的公共基础设施的一部分,该基础设施在任何具体的建模任务之前执行大量繁重的工作

相关数据管理领域

  • 一些标记工具提供可视化和搜索非结构化数据的功能(例如,使用元数据或向量相似度)
  • 矢量数据库帮助团队管理大规模嵌入数据——尽管通常用于矢量相似性搜索期间的点查询,而不是数据探索和分析
  • 在更下游的地方,数据版本控制工具帮助团队在数据选择之后存储和管理他们的数据集——尽管数据存储的方式并不适合探索或分析。

将数据工程的首要原则应用于数字图书馆数据

执行上游起重

大多数数据工程架构模式都是围绕在业务任务之前提取常见的、繁重的数据工作而构建的,从而使下游团队能够专注于解决他们独特的问题。

数据湖和数据仓库、ETL 管道和特性存储都是基于这个原理运行的。

注意:当有足够多的下游消费者需要类似的数据处理来创建一次完成这项工作的效率时,这种方法是可行的。

根据数据的处理级别在层中存储数据

由于并非所有下游用例都可以提前预测,并且可能需要从头开始重新处理数据,因此数据湖通常将数据存储在不同的层中:

  • 生的
  • 轻度加工和清洁
  • 为常用案例(有时在商场中)做好准备。

这通常被称为“青铜、白银、黄金”数据层设计模式。

创建显式数据模型

数据建模有助于理解数据,并围绕数据进行思考。

首先,我们需要设计组织二进制数据的结构,以便我们能够管理它—应用保留策略、清单、在存储层之间移动等。

更重要的是,正如我们所看到的,建立数据集需要按“维度”过滤我们的数据,例如,按图像分辨率、光照条件、个人资料照片的性别等。

明确定义数据模型是有意义的,例如:

  • 识别“域实体”
  • 确定这些实体上的“尺寸”
  • 识别描述“发生在我们实体上的事情”的“事件”。

一旦可以描述显式数据模型,根据定义,它具有结构,并且可以存储在结构化数据的存储引擎(例如,数据库)中

如果访问模式不同,则创建单独的“物化视图”

一般来说,我们试图预先计算并以有用的格式存储数据。

如果我们有多个消费者,他们有非常不同的数据访问模式,有时将数据具体化为不同的存储设备和格式会很有用,以方便消费。

在我们的例子中,GPU 数据加载需要非常优化的存储设备和格式,这对于目测数据或长期存储通常是没有用的。

维护更改日志与覆盖数据

如果可能的话,最好添加新版本的数据(原始数据、标签等)。)vs .覆盖旧版本。

深湖建筑模式—概述

通常,这种模式遵循现代数据湖和数据仓库模式。

作者图片

*编号参考上图。

(1)收购

我们从数据源中捕获数据,或者以批处理方式,或者以流方式。

从外部数据源来看,这可能意味着非结构化数据和一些类似标签的结构化数据。

如果来源是生产系统,我们通常可以获得非结构化的原始数据、预测、元数据、附加的相关事件或以上所有内容。

如果(非结构化)数据需要积极过滤(想想物联网设备),采集逻辑需要处理至少部分过滤。

(2)土地

“着陆区”中原始形式的结构化和非结构化信息。通常在这个阶段,它们都可以作为文件或对象存储。

(3)摄入管道(ETL/ELT)

从原始数据到常见的有用结构。
“ETL/ELT”中的“T”包括将非结构化数据转换成有用的格式,提取公共维度清理破碎数据,组织数据模型中的数据。

这包括查询、过滤和转换结构化和非结构化数据。

一些或所有被认为保留起来不太有用的数据过滤通常发生在 ETL 管道内部。

为了处理来自多个不同数据源的数据,在继续处理之前,我们将首先使用每个数据源的逻辑对数据进行规范化。

(4) 存储在公共层

有用形式的数据存储在一个中央数据湖中,可以为多个项目服务。

数据是根据一个很好理解的模式建模的,包括非结构化数据的布局(例如分区),尤其是结构化数据的数据模型。

可以统一用于结构化数据和非结构化数据的存储技术(例如使用 HDFS),或者对结构化数据和非结构化数据使用不同的存储技术(权衡利弊)。

此时,我们可以对数据执行查询,以了解和描述原始数据属性以及标签、预测和数据的其他方面。

(5)领域工作流和(6)仓库

如果数据用于不同的目的,那么每个领域通常都需要更丰富的数据模型——比如数据的额外维度。

(7)创建数据集

通常,创建数据集的任务需要一个工作流:

  • 使用分层通过分布选择数据池
  • 仅对所选数据执行附加标注
  • 执行验证和拆分
  • 以 GPU 培训友好的格式准备和存储

对于具有边缘情况和复杂分布的复杂数据集,我们可能需要多次重复这个循环,直到得到我们需要的数据集。

用于准备大型数据池的相同管道工具可用于生成特定数据集。**

(8)评估

可能需要通过对(多个)数据集进行推断并应用通过/失败标准来评估通过了初始有用性标准的模型。

将预测反馈到湖中是很有用的,并且能够将它们传递给注释系统中的人类审阅者,以便进行更严格的验证以及积极的学习机会。

执行大规模推理、将数据存储在仓库中以及与注释系统进行交互可以被表达为使用管道工具的工作流。

建筑视图

数据模型

数据模型通常在某种程度上是特定于领域和模态的。然而,构建它的方法通常是相似的:

  • 原始数据(例如图像或音频文件)—作为一个“实体”,我们希望“了解一些东西”
  • 派生的非结构化数据(如嵌入)—也是一个实体,指向“父项”(原始数据)
  • 标签和预测是“事件”

标签和预测通常共享一个共同的表示,并且可以被认为是“某个参与者关于主题(原始数据)的某个问题的意见”。

每个实体可以有任意数量的这种“意见”,包括来自多个参与者的意见(例如,标注者之间的共识)

  • 尺寸:
    -技术尺寸(分辨率、FPS、编码等。)
    -领域维度——帮助描述原始数据内容但不是模型主要目标的东西(如照明条件、物体数量、人的性别等)。)
  • 在一些更高级的情况下,较低级别的数据可用于获得关于其他较高级别的实体的见解。例如,我们可以执行聚合,并将汇总的模型性能数据存储在仓库中。
  • 一个更高级的用例是具体化我们对其执行预测的对象的信息,例如,从那里的多个预测导出一个十字路口的真实世界高分辨率地图。

组件视图

非结构化存储引擎 如集中式文件系统或对象存储,以逻辑方式分区。
用作数据的真实来源,包括来自观众/标签系统的访问。

结构化存储如云数据仓库或适度的关系数据库。

注意:根据优步的博客文章,您可能能够将这些引擎和查询引擎结合在一个基础设施中(具有重要的权衡)

逻辑数据层 —原始、公共和领域仓库
通常作为结构化存储中的模式或名称空间来实施,因为非结构化数据是不可变的,所以复制它没有意义。

管道工具 通常是 DAG 编制器,支持长时间运行的步骤(人工标记)和触发数据处理基础设施的能力(见下文)

非结构化数据处理基础设施

  • 用命令式代码表示的逻辑
  • 计算在集群上执行(例如,无服务器、k8s 作业、Spark 作业等。)
  • 进程从非结构化存储中读取并将输出写入某个(可能是单独的)存储设备(包括消息总线、对象存储等)。)
  • 下游管道步骤和工程师可以访问输出。

结构化数据处理和查询工具
理想情况下是一种
声明式查询语言
(存储解决方案自带),用于检索数据以及执行 DML 语句,如 CTAS。****

应用程序接口

读取 API

  • 低级—分别针对结构化数据和非结构化数据
  • 高级—用于查询符合标准的所有原始数据,并将其具体化为指定的格式(适用于标记/目测或训练数据准备)

编写 API ,重点关注从不同来源(标签系统、外部数据和模型预测)获取新的标签和预测****

标签格式翻译 API

  • 到/从注释系统标签表达
  • 从模型预测数据格式

总结和后续步骤

由于数据的非结构化、大容量和劳动密集型性质,深度学习的数据管理是一项挑战。

在这篇文章中,我们介绍了“Deep Lake”——一个利用数据工程最佳实践和模式的架构蓝图,并将其应用于管理深度学习数据的领域。

在本系列的下一部分,我们将描述 Deep Lake 系统中使用的设计模式,并回顾潜在的技术栈实现替代方案。

基于深度学习的惯性导航:一种混合导航滤波器

原文:https://towardsdatascience.com/deep-learning-based-inertial-navigation-a-hybrid-navigation-filter-ecabd547456b

惯性导航领域被认为是一个非常经典的领域。它旨在利用(低级)惯性传感器和精确(低频)传感器,如 GPS 接收器,为我们提供导航解决方案(位置、速度和方向)。一个问题出现了,强大的深度学习工具可以如何推动这个经典领域。

个人动机介绍:我为什么要开始?

三年前,我开始问自己,将机器学习和深度学习(DL)结合起来是否有价值,特别是在经典的惯性导航系统 (INS)中。在这个行业工作了 3 年后,我在海法大学开始了我的博士研究,包括为高通和 Autotalks 工作。在这些站点中,我思考了 DL 加速的许多迷人的解决方案。例如,我研究过高通的指纹传感器,这种传感器目前存在于许多智能手机设备中。

作为这场革命的一部分,我对自己以前的教育进行了反思:我毕业于以色列理工学院,获得了 3 个学位:航空航天工程硕士学位和学士学位,以及经济和管理学士学位(成绩优异)。我过去主要关注实时导航和跟踪目的的最优控制和估计。留在这个领域,把 DL 整合成我博士的一部分,题目是“基于机器学习的惯性导航”,似乎是很自然的事情。

背景

经典的 INS加速度计和陀螺仪的测量值组合成运动学方程。最终实现导航解算。但是,这两个传感器噪声很大,使得解决方案很差,并且会出现快速发散。因此,最有可能将经典的 INS 与精确的传感器融合。例如全球定位系统(GPS)接收器。这种融合方案被称为 INS/GPS。最大的问题是,“如何将所有传感器集成在一起,以实现最精确的导航解决方案?”答案涉及到著名的信号处理算法,卡尔曼滤波器 (KF)。KF 是惯性导航传感器融合任务中最著名的算法。大多数时候,由于导航方程涉及非线性,所以使用扩展的 KF (EKF)版本。

我提出了一个问题和一个解决方案——那么 DL 组合在哪里呢?答案很简单。导航滤波器,或通常任何 EKF,必须有自己的参数,以最佳工作。

卡尔曼滤波器真的是最优滤波器吗?

在大多数估算书中,你可能会发现 KF 是一个最佳滤波器,它可以最小化状态和这些状态的预测之间的平方根误差(MSE )(状态是一个可以由 15 个状态组成的向量:3 维的位置和速度加上身体方向角(另外 3 个)。加速度计和陀螺仪偏置还有另外六种状态。但是我们希望最小化真实状态向量和预测状态向量之间的误差。在现实生活中,除非使用非常精确的设备(即实时运动设备),否则我们无法获得这种真实状态。

作为负责整个导航场景的动态模型的过滤器设计者,他可能在状态及其方程(转移矩阵)上出错。记住,这只是一个运动学模型 而不是真正的运动学本身!为了达到最优,设计者必须设计一个精确的动态模型(稍后,我们还将讨论测量模型)。由于没有人知道未来,伟大的 KF 允许我们在建模阶段为我们的不确定性增加一些权重。在系统模型噪声协方差矩阵中考虑了这种不确定性。对于每个状态,我们可以将一些代表高斯白噪声方差的值放入以帮助 KF 处理它将在实时导航期间遇到的一些动态

因此,KF 在模型状态和估计状态之间的 MSE 意义上是最优的。就真实状态和估计状态之间的 MSE 而言,KF 并不是最优的……我们需要根据我们所处的环境,继续实时调整其参数。

K F 在模型状态和估计状态之间的 MSE 意义上是最优的就真实状态和估计状态之间的 MSE 而言,KF 不是最佳的…我们需要在实时设置中不断调整其参数

混合方法

所以,这是 DL 参与的部分。让我们看看下图:基于 EKF 的导航滤波器由 GPS 和 INS 输入(分别为绿色和紫色)提供,并最终提供精确的导航解决方案。DL 用于从惯性测量(加速度计和陀螺仪)中学习 EKF 的调谐参数。这些测量是在实时设置中提供的,因此 DL 模型也以实时方式调整这些参数。例如,在 INS 的动态模型中存在一定程度的不确定性。这种不确定性是由加速计和陀螺仪中的噪声、干扰和附加因素造成的。这些因素在车辆导航过程中会发生变化,并导致导航性能下降。为了解决这个问题,需要相应地调整动态模型的不确定性。

一种方法是调整动态模型(Q)的噪声协方差矩阵。矩阵值会影响我们注入到模型中的高斯白噪声的大小。因此,随着值的增加,设计者的不确定性增加,反之亦然。有许多经典的自适应方法,考虑 EKF 的实体(如创新属性)来调整它。在这个建议的混合方法中,我们依赖于来自模拟数据集的训练模型,该数据集具有许多噪声信号及其相关方差(标签)的例子。

INS/GPS 的混合模型(图片由作者提供)

一旦设计了这样的混合导航滤波器模型,就需要考虑关于 DL 模型的许多技术细节。部分列表包括信号的长度(示例)、决定是否包括某些特征工程、更新调谐参数的频率。在我们最近的论文“基于混合模型和学习的自适应导航滤波器”中,我们考虑了这些细节,该论文由 Barak Or 和 Itzik Klein 撰写,发表在IEEE Transactions on Instrumentation and Measurement(预印本可在这里获得)。

摘要

这项工作是混合导航模型中的第一项工作,除了深度学习模型之外,还考虑了经典的卡尔曼滤波器,用于调整滤波器参数。在我们测试的一些案例研究中,这种组合产生了很大的改进。其中一个包括四轴飞行器导航,我们的混合方法使定位精度提高了 27%。

参考

如果想看原文,可以在 IEEE 网站 :

【1】Or,Barak,和 Itzik Klein。"一种基于混合模型和学习的自适应导航滤波器." IEEE 仪器与测量汇刊71(2022):1–11。

如果你想继续阅读关于识别噪声协方差参数的问题,你可以参考 IEEE 网站上的这篇文章 :

【2】张、凌毅、David Sidoti、Adam Bienkowski、Krishna R. Pattipati、Yaakov Bar-Shalom 和 David L. Kleinman。"关于噪声协方差和自适应卡尔曼滤波的识别:一个 50 年老问题的新观点."IEEE 访问权限 8(2020):59362–59388。

如果你想继续阅读关于惯性传感器的深度学习,我推荐我在 Medium : 上的前一篇文章<http://Deep Learning for Inertial Navigation A short review of cutting edge deep learning-based solutions for inertial navigation.>

巴拉或。“惯性导航的深度学习:对惯性导航基于深度学习的前沿解决方案的简短回顾。”2020.

关于作者

巴拉克是人工智能和传感器融合领域的研究人员。Barak 拥有多项专利,并在专业杂志上发表了多篇文章。他是 ALMA Tech 的创始人。一家人工智能和高级导航公司。他曾在高通 2019-2020 工作,主要研究 DSP 和机器学习算法。在此之前,他负责 Autotalks 的本地化项目。他获得了以色列理工学院 Technion 的航空航天工程硕士学位(2018 年)和理学学士学位(2016 年),以及经济和管理学士学位(2016 年,成绩优异)。巴拉克目前正在以色列海法大学完成博士学位。

www.Barakor.com|https://www.linkedin.com/in/barakor/

深度学习:更快、更好、免费,只需三个简单的步骤

原文:https://towardsdatascience.com/deep-learning-faster-better-and-free-in-3-easy-steps-8ae2e1b932d5

实践的 RL 课程间奏曲

厌倦了在你的笔记本电脑上以…一只乌龟的速度训练深度学习模型?🐢

对购买昂贵的 GPU 或优化云服务账单不感兴趣?💸

希望有更好的方法来做这件事?

幸运的是,最后一个问题的答案是肯定的。这个珍贵的 GPU 会更快的训练你的深度学习模型。快多了。而且完全免费。

这篇文章既面向动手 RL 课程的学生,也面向任何正在寻找更快训练循环的深度学习开发者。

让我们进入正题。

凯,海洋(图片由作者提供)

深度学习中的 GPU

GPU(图形处理单元)是深度学习取得巨大成功背后的关键硬件组件。GPU 加速神经网络训练循环,以适应合理的人类时间跨度。没有它们,深度学习是不可能的。

如果你想训练大型深度神经网络,你需要使用 GPU。但是,GPU 也💰。这意味着我们大多数人家里都没有。

当然,你可以使用像 GCP 或 AWS 这样的云服务来租用 GPU,但要冒着每月支付高额账单的风险。

那么问题是…

有没有办法让这个珍贵的 GPU…免费得到

是的,有。让我告诉你怎么做。

解决方案:深度学习的谷歌实验室

Google Colab 是一项服务

👉🏽免费提供间隔时间长达 12 小时的 GPU,以及
👉🏽与 GitHub 库无缝集成。

考虑到这两个特性,我们可以用免费的 GPU 轻松地增强 GitHub 中任何 Jupyter 笔记本的性能。

我们将校准深度 Q 代理的超参数,这是一个相当昂贵的计算。

如果你想知道更多关于这个例子的细节,查看我的 强化学习课程 。这个笔记本是基于 课程的 第 6 部分,在这里我们训练一个深度 Q 智能体去解决来自 OpenAI 健身房的 大车杆子 环境。

我们开始吧!

来吧,打开这个笔记本:

👉🏽笔记本/11 _ hyperparameter _ search _ in _ Google _ colab . ipynb

并跟随这些 3 个简单的步骤

第一步。在 Google Colab 中加载 Jupyter 笔记本

你可以从一个公共的 GitHub 库加载任何一个 Jupyter 笔记本到 Colab,只需点击一下。

如何?

一旦您提交并将笔记本推送到远程 GitHub 分支,您可以在如下 URL 下找到它:

https://github.com/GITHUB_USER/REPO_NAME/path/to/file.ipynb

现在,如果你在浏览器上输入另一个网址

https://colab . research . Google . com/GITHUB/GITHUB _ USER/REPO _ NAME/path/to/file . ipynb

你将会神奇地在这个 Jupyter 上安装 Google Colab。

手动键入 URL 不是最好的用户体验。但是,我们可以通过在我们最初的 Jupyter 笔记本上添加一个链接来轻松解决这个问题。

点击 此处 打开 GitHub 中的笔记本。

如果你向下滚动一点,你会看到我添加了一个这样的按钮

点击这里在 Google Colab 中打开笔记本(图片由作者提供)

点击它。链接的网址是我刚刚告诉你的格式,☝🏽。

将会打开一个浏览器选项卡,您将会看到一个与您的本地 Jupyter 服务器非常相似的界面。这是谷歌 Colab。实际上是前端应用。在后端,你连接到谷歌服务器上的一个运行时,让你执行 Python 代码。

现在,默认情况下,Google Colab 运行时只使用 CPU,而不使用 GPU。但是你可以很容易地改变它。让我们开始吧。

第二步。启用免费 GPU 加速

转到运行时>>更改运行时类型> > GPU。瞧啊。

将运行时改为 GPU(图片由作者提供)

您的运行时现在使用 GPU。甜食🍭。

第三步。安装 Python 依赖项

Google Colab 运行时安装了一些流行的 Python 包,如 PyTorch 或 TensorFlow。然而,要运行您的代码,您需要确保安装了所有正确的依赖项。

在笔记本顶部添加一个单元格,以便在 Colab 的环境中安装所有必需的包。

这就是这个设置的样子。

我们从 GitHub 获取代码,并从我们的文件中安装依赖关系。在这一步之后,Google Colab 可能会要求我们重新启动运行时。我们只需进入顶部菜单> >运行时> >重启运行时。

运行这个并重新启动运行时

然后执行第二个单元,将我们的本地文件夹src添加为本地 Python 包。

然后运行这个

就是这样。您有一个免费的 GPU 供您使用!

随意运行整个笔记本,并(可能)解决你的第一个深度强化学习问题😎。

奖金黑客集成 MLflow 与 Colab 🥷

这个主要是给我的学生看的,因为我们在 RL 实践课程中使用 MLflow 来跟踪所有的实验结果,但是请随意阅读。你永远不知道它什么时候能派上用场。

当我训练深度学习模型时,我喜欢使用开源的 MLflow 来跟踪所有的实验结果。但是,如果我在 Colab 上运行笔记本,如何登录到我的本地 MLflow 服务器?

有一个窍门。相当粗糙,但我喜欢它❤️.

它的名字是 ngrok 一个让你以一种安全的方式将你的本地电脑暴露给外部互联网的工具。

首先,从命令行本地启动 MLflow 跟踪服务器(如果已经使用了 5000,请调整端口号)

$ (venv) mlflow server --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./artifacts --host 0.0.0.0 --port 5000

然后,安装 pyngrok ,这是一个用于 ngrok 的方便的 Python 包装器。

$ (venv) pip install pyngrok

并安全地公开 MLflow 服务器端口,我们在同一个端口上运行 MLflow 服务器

$ (venv) ngrok http 5000

将出现一个屏幕,显示 ngrok 为您的隧道生成的 URL。复制 URL 地址。

复制你在屏幕上看到的网址(图片由作者提供)

现在,如果你去 Colab 的笔记本,你可以把这个 URL 粘贴到那里:

运行笔记本的其余部分。

看到 MLflow 仪表盘再次接收实验数据,你会很惊讶。

下一步是什么?❤️

在下一讲中,我们将介绍一个新的深度 RL 算法家族,我们将解决一个新的环境。

敬请关注。

想支持我吗?

你喜欢阅读和学习关于 ML、AI 和数据科学的知识吗?

无限制地访问我在 Medium 上发布的所有内容,并支持我的写作。

👉🏽今天使用我的 推荐链接 成为会员。

https://pau-labarta-bajo.medium.com/membership

👉🏽订阅 datamachines 简讯

👉🏽 跟着我 上媒。

祝你愉快🤗

避寒胜地

动力学的深度学习——直觉

原文:https://towardsdatascience.com/deep-learning-for-dynamics-the-intuitions-20a67942dfbc

深度学习在学习动力学方面已经显示出有希望的结果,这里是两种流行方法背后的直觉,用 PyTorch 编码的例子进行说明。

这篇博文假设了深度学习的一些基础知识。这篇文章中使用的所有代码都可以在我的 GitHub 中找到:https://github.com/TomJZ/DeepLearningForDynamics-Intuition

微分方程

大多数物理模型都是用微分方程(DEs)编写的,具体来说就是常微分方程(ODEs)和偏微分方程(PDEs)。牛顿万有引力定律写道 F = Gm1* m2/r = ma ,其中 a 为加速度,位置的二阶导数。爱因斯坦著名的广义相对论使用偏微分方程系统将时空、能量和动量联系起来。薛定谔方程使用线性 PDE 描述了量子力学系统的波函数,在量子尺度上模拟了概率分布的流动。毫不夸张地说,经典和现代物理学的成功都归功于 DEs 令人难以置信的表现力。

DEs 背后的假设很简单:时间和空间是连续的,因此它们可以被分成无限小的部分。通过恰当地描述这些部分在最小尺度下如何相互作用,观察到的宏观行为应该会浮现出来。

情商。1:颂歌的一般形式

上面的方程是 ODEs 的一般形式,它与大多数其他函数的明显区别在于 f(。,.)设为 x_dot ,状态的导数。这个方程非常简单——它只是状态和时间的函数。这种简单的形式可以模拟从新冠肺炎传播到机器人动力学的各种各样的现象。

假设系统从点 x0t=0 开始,其在 t=c 的状态由下式给出

情商。2:给定初始条件时 c 时刻的状态

通过评估上面的等式,可以获得在 t=c 的状态。关键是积分的计算。如果积分可以解析地计算出来,则称常微分方程有解析解。否则,积分可能需要使用数值方法进行评估,如欧拉法龙格-库塔法或多步解算器。用这些方法我们得到了给定初始条件的常微分方程的数值解。著名的三体没有解析解,并且本质上是混沌的,这一点我们将在后面讨论。

在我们深入研究机器学习之前,让我们先弄清楚一些术语。在这篇博文中,我将提到 f(。,.)作为动力学,它是支配一个系统如何进化的函数。然后是前面段落中描述的 ODE 的解决方案的概念。一种解决方案是使用 ODE 在初始条件下生成的状态序列(想象将时间 c 设置为等式中的时间戳序列)。2,你就可以解出一系列的状态)。由微分方程表示的动力学有时更明确地表述为矢量场,相应地,微分方程的解可以称为轨迹。为了一致和清晰,我将成对使用这些术语: DEs 和它们的解;矢量场和这些场中的轨迹。

给定系统演化的观测值,或其轨迹 Z = [x(t1),x(t2),x(t3),…,x(tn)],其中采样时间戳是 T = [t1,t2,…,tn],因此机器学习任务是找到一个模型来拟合这些观测值。让我们在这里暂停一下,想一想到底如何处理这个问题。这就是哲学二分法的由来。一些人认为这个问题仅仅是学习一个时间序列轨迹,而另一些人认为学习潜在的向量场是重要的。

这些系统

在我们深入到不同的思想流派之前,这里有两个经典的动力系统我们将用来作为说明性的例子:(1)一个稳定的螺旋,和(2)洛伦兹系统。

2D 稳定螺旋,顾名思义,是一个稳定的系统。它的轨迹,随着时间的推移,收敛到一个固定点。

# Stable Spiral system dynamics
class Spiral(nn.Module):
    def __init__(self):
        super(Spiral, self).__init__()
        self.lin = nn.Linear(2, 2, bias=False)
        W = torch.tensor([[-0.1, -2.0],
                         [2.0, -0.1]])
        self.lin.weight = nn.Parameter(W) def forward(self, t, y):
        return self.lin(y**3)

图 1:稳定的螺旋。轨迹最终收敛到一个固定点

另一方面,2D 混沌的洛伦兹系统要有趣得多。它是由数学家和气象学家爱德华·洛伦茨在 1963 年发现的。它以 混沌 而闻名,这意味着给定一个参考初始条件和由此产生的轨迹,对这个初始条件的任何微小扰动都会导致轨迹以指数速度发散。然而,这种分歧并没有使轨迹走向无穷:轨迹最终停留在一个称为吸引子的有界集合上。术语蝴蝶效应是洛伦兹吸引子的真实含义。

# Lorenz system dynamics
class Lorenz(nn.Module):
    def __init__(self):
        super(Lorenz, self).__init__()
        self.lin = nn.Linear(5, 3, bias=False)
        W = torch.tensor([[-10., 10., 0., 0., 0.],
                          [28., -1., 0., -1., 0.],
                          [0., 0., -8\. / 3., 0., 1.]])
        self.lin.weight = nn.Parameter(W) def forward(self, t, x):
        y = y = torch.ones([1, 5])
        y[0][0] = x[0][0]
        y[0][1] = x[0][1]
        y[0][2] = x[0][2]
        y[0][3] = x[0][0] * x[0][2]
        y[0][4] = x[0][0] * x[0][1]
        return self.lin(y)

图 2:洛伦兹系统。两个“翅膀”停留在一个吸引子上

在上面洛伦兹系统的图中,我根据时间对轨迹进行了颜色编码。轨迹从深蓝色开始,以黄色结束。可以观察到,在蝴蝶的两个“翅膀”上,颜色是混合的——这表明了轨迹的“随机性”。简单来说,你无法预测它会在未来的某个时间落脚在哪一翼。另一个有趣的事实是,混乱只可能在 3D 或更高的维度,这让我想起了道家的智慧:道产生了一个;一个产生了两个;两个产生三个;三生万物。

请注意,许多人将洛伦兹系统误认为非确定性系统。洛伦兹系统,像任何常微分方程一样,事实上完全是确定性的。它的“随机性”来源于指数级快速发散的轨迹,而不是任何随机性。混沌的本质使得洛伦兹系统更难学习,我们将在后面的学习示例中展示。

学习轨迹

对于不熟悉动力系统的人来说,当一个类似 Z = [x(t1),x(t2),x(t3),…,x(tn)]的轨迹出现时,他们可能会把这看成一个时间序列建模问题。任务是直接模拟轨迹 Z 本身。像 RNNs 和 transformers 这样的经典时间序列建模工具自然就派上了用场。这些模型有许多变种,如油藏计算机、LSTMs、时空器等等。

我们将用最简单的 RNN 作为例证。不熟悉 RNNs 的可以在这里看一下

class fcRNN(nn.Module):
    def __init__(self, input_size, hidden_dim, output_size, n_layers):
        super(fcRNN, self).__init__()
        self.hidden_dim = hidden_dim
        self.n_layers = n_layers
        self.rnn = nn.RNN(input_size, 
                          hidden_dim, 
                          n_layers,
                          nonlinearity='relu',
                          batch_first=True) # RNN hidden units self.fc = nn.Linear(hidden_dim, output_size) # output layer def forward(self, x):
        bs, _, _ = x.shape
        h0 = torch.zeros(self.n_layers, bs,
                        self.hidden_dim).requires_grad_().to(device)
        out, hidden = self.rnn(x, h0.detach())
        out = out.view(bs, -1, self.hidden_dim)
        out = self.fc(out)
        return out[:, -1, :]

这个 RNN 没有什么特别的。输入大小和输出大小与系统的维数相同(螺旋为 2,洛伦兹为 3)。

情商。3:用 RNN 生成轨迹,假设训练时间窗口为 3

训练 RNN 的方法如下:首先为输入定义一个时间窗口,然后 RNN 预测下一步,由此可以计算损失。更具体地说,给定轨迹 Z = [x(t1),x(t2),x(t3),…,x(tn)],以及比如说 3 的时间窗,我们处理 Z 以给出训练数据 S = {([x(t1),x(t2),x(t3)],x(t4),([x(t2),x(t3),x(t4)],x(t5),…,([x(tn-3),x(tn-2),x(tn-1)],x(tn))}。以第一个训练样本为例,RNN 取[x(t1),x(t2),x(t3)]并输出其预测。在预测和 x(t4)之间计算损失。

图 3:和 RNN 一起学习稳定螺旋

图 4:稳定螺旋的 RNN 预测

如图 4 所示,得到的模型在预测稳定螺旋的轨迹方面做得相当不错。然而,从图 3 中可以看出,训练过程并不那么美好。在训练过程中,轨迹的某些部分经常变得不平滑。

接下来让我们看看学习洛伦兹系统。在我下面的洛伦兹系统图中,深灰色散点图是训练数据,而渐变颜色线是模型预测。

图 5:用 RNN 学习洛伦兹系统

图 6:洛伦兹系统的 RNN 预测

在训练过程中我们仍然可以观察到轨迹并不是那么平滑。更糟糕的是,轨迹似乎在跳来跳去。得到的模型预测很难捕捉到系统的吸引子。根据颜色,系统错误地收敛到右“翼”中心的一个固定点。虽然我没有做大量的调整,但这种趋向于收敛到一个固定点或极限环的趋势经常被观察到。

学习向量场

对于那些有一些动力系统背景的人来说,学习基础向量场是最自然的方法。毕竟,对动力系统的研究本身就是数学的一个分支,它本身就应该告知学习是如何进行的。换句话说,我们希望为未知的 f(.,.)在 Eq。1 利用对其状态序列的观察。

情商。4:生成带节点的轨迹。fNN(。,.)是描述向量场的神经网络

许多最近的作品[1,2,6]采用了这一观点,并展示了学习动力学的令人印象深刻的结果。这类神经网络称为神经常微分方程(节点)[1]。在加速和改善其优化收敛方面取得了很大进展。还有关于这些学习技巧的好教程

在这篇文章中,我将使用 TorchDiffEq 库,它几乎是当今 NODE 的标准工具箱。它不仅提供了各种解算器,还集成了 py torch伴随灵敏度方法,这是一种内存高效的反向传播替代方法。稳定螺旋和 Lorenz 系统的模型是具有 ReLU 激活功能的黑盒全连接神经网络。

与 RNNs 不同,我们不需要处理训练数据——观察到的轨迹可以直接用于学习。虽然大多数演示使用随机梯度下降(SGD)从训练数据中随机抽样批次进行训练,但我使用梯度下降并同时从所有批次中学习。我稍后会解释这样做的原因。

节点“批处理”的概念很重要。它是沿着训练轨迹的一批点,您可以将这些点用作生成神经网络模型预测的初始条件。还有一个至关重要的超参数,我称之为前瞻。前瞻是神经网络根据一批初始条件预测未来的步数。然后计算这些批预测和地面实况之间的损失。

前瞻与你试图学习的系统动力学有很大关系,必须小心选择。对于大多数稳定的系统,更大的前瞻会起作用。然而,对于混乱和不稳定的系统,前瞻必须保持较小(您可以始终将其保持为 1)。对于混沌/不稳定动力学保持较小的原因直接来自于这些动力学的定义。如果系统的轨迹随着不同的权重以指数方式快速发散,计算该轨迹上的损失将是没有意义的。简单来说,你不能强迫两个注定有分歧的东西变得一样。

# A 2D system for fixed point
class FixedPointTrain(nn.Module):
    """
    neural network for learning the stable spiral
    """
    def __init__(self):
        super(FixedPointTrain, self).__init__()
        self.lin = nn.Linear(2, 128)
        self.lin3 = nn.Linear(128, 2)
        self.relu = nn.ReLU()def forward(self, t, x):
        x = self.relu(self.lin(x))
        x = self.lin3(x)
        return x# A 3D system for Lorenz system
class LorenzTrain(nn.Module):
    """
    neural network for learning the chaotic lorenz system
    """
    def __init__(self):
        super(LorenzTrain, self).__init__()
        self.lin = nn.Linear(3, 256)
        self.lin3 = nn.Linear(256, 3)
        self.relu = nn.ReLU()def forward(self, t, x):
        x = self.relu(self.lin(x))
        x = self.lin3(x)
        return x

图 7:学习带有节点的稳定螺旋

图 8:稳定螺旋的节点预测

稳定螺旋的学习结果如图 8 所示,其中我也画出了矢量场。图 7 所示的学习过程在训练期间的任何时候都显示出平滑得多的轨迹。

图 9:学习带有节点的洛伦兹系统

图 10:洛伦兹系统的节点预测

对于 Lorenz 系统,如图 9 所示,在训练期间,轨迹似乎仍在跳跃。然而,在整个训练过程中,轨迹要平滑得多。图 10 中得到的模型预测也很好地捕捉了系统的吸引子。虽然神经网络仍然表现出收敛到一个固定点或极限环的趋势,但 NODE 已经显示出产生真正的混沌轨迹2

有些人可能想知道为什么学习轨迹与训练数据不完全匹配。但任务首先不是要匹配这些轨迹,因为这些系统的混沌本质会让任何努力都变得徒劳。相反,神经网络正在学习矢量场,尽管预测轨迹存在抖动,但矢量场会随着时间的推移而收敛。如果向量场学习得足够好,混沌系统的吸引子将被很好地捕获,如图 10 所示。

最后一点是关于我使用梯度下降而不是 SGD 的原因。由于系统的混沌特性,使用神经网络生成的轨迹一旦被学习,不仅对初始条件敏感,而且对其自身的权重敏感。这意味着对神经网络权重的轻微更新将彻底改变所得系统的稳定性(例如稳定或混沌),并进而改变其轨迹。SGD 是随机的,除非学习率降到极低,否则它会放大这种效应。如果除了基础向量场之外,我们还想捕捉吸引子,那么减少训练中的随机性是很重要的。由于学习低维动态系统的大多数任务只涉及小数据集,使用梯度下降比 SGD 快得多。

那么有什么区别呢?

时间嵌入

RNNs 可以被认为是时间延迟系统,其中系统的动态被嵌入到状态的时间历史中。这在我们处理用于训练 RNNs 的数据的方式中是明显的。还有诸如图克嵌入定理的理论,给出了混沌动力学可以从嵌入式系统中重构的条件。

另一方面,NODE 可以直接学习向量场。它不需要对系统进行任何变换,如时间嵌入。不过 NODE 也可以用来学习嵌入式系统,只要可以参数化为 DEs。

隐式积分器

另一个重要的区别是,RNNs 可以被认为内部有一个隐式积分器。它直接从先前的状态预测下一个状态。情商。2 显示了从动力系统生成轨迹的方式,并且存在积分。当学习动态系统时,RNNs 隐式地执行这种集成。然而,这使 RNNs 处于不利地位,因为它只能以规定的时间间隔输出状态。对于以不规则时间间隔采样的观察值,RNNs 可能难以应用。

相比之下,NODE 输出状态的导数,并依靠外部积分器/解算器使用学习的场来生成轨迹。这不仅允许 NODE 使用不规则采样点对观察值进行训练,还允许它利用任何现有的求解器来满足我们的需求。

平滑

正如从前面的例子中观察到的,一个显著的区别是,在训练期间,NODE 的轨迹比 RNNs 的轨迹平滑得多。

这是在节点外部执行集成的结果,这使得节点成为真正的动态系统。用数学术语来说,它构成了微分同胚的一个单参数群,这里的“单参数”就是时间。微分同胚给了轨迹许多有趣的性质,我将遵从外部来源。因此,轨迹在时间上的平滑度可以被认为是这些属性之一。然而,对于 RNNs,没有这样的理论性质。

在先的;在前的

在建模方面,自上而下的深度学习方法和自下而上的第一原理方法之间经常存在争论。实际上,这两种方法都有各自的优点和缺点。自上而下的方法可以从数据中提取复杂的隐藏模式,但缺乏可解释性,而且通常缺乏可推广性;自下而上的方法产生可概括和可解释的模型,但需要对过程进行艰苦的分解,而且常常需要一点运气。作为一名机器人专家,在诉诸黑盒方法之前,我倾向于拥有尽可能多的第一原理知识。两者的有机结合当然是最理想的。

节点更“暴露”的性质使得合并物理先验更容易。毕竟我们大部分的物理模型都是常微分方程或者偏微分方程。当我们想在 n ODE 中加入一些已知的 ODE 结构时,这使得 NODE 成为一个更兼容的选择。其中一个例子可以在我以前的工作中找到,我试图结合两个世界的优点。然而,RNNs 更具挑战性,因为动力学隐藏在其隐式积分中。

当学习动力系统时,一些显示良好结果的工作假设完全函数形式。注意,这使得它成为一个参数估计问题,而不是深度学习问题。参数估计问题比仅用黑盒神经网络学习要简单得多,因此这种工作的结果应该仔细评估。

挑战和机遇

测试真正的混乱

图 11:混沌洛伦兹系统与极限环的 0–1 测试。(a) Lorenz 系统轨迹(b)Lorenz 系统 0–1 测试的“pq”图,应类似于无界布朗运动(c)极限环轨迹(d)极限环的 0–1 测试的“pq”图,应是有界的

目前还不知道神经网络是否能够“真正”代表混沌动力学。一些测试,如0–1 测试可以根据系统足够长的轨迹将系统归类为混沌系统。但是多长时间才算足够长呢?有人可能会说,总有一个时间点,轨迹收敛到一个固定点或极限环,特别是我们已经根据经验表明了他们的这种收敛趋势。

学习僵硬的系统

图 12:范德波尔振荡器。刚性系统的一个例子

僵硬的系统在化学和生物系统中普遍存在,并且是出了名的难以处理。它们生成的轨迹在时间尺度上有如此巨大的差异,以至于学习它们需要大量的调整。

学习高维动力系统

图 13: 1D 仓本-西瓦辛斯基方程。时空混沌的偏微分方程

虽然本文中的例子都是低维的常微分方程,但偏微分方程才是真正的数学瑰宝。海洋预报、气象学、传热学、量子力学等等,都可以用偏微分方程很好地建模。学习偏微分方程的一个巨大挑战在于它们的高空间维度,这通常需要离散化。学习和求解偏微分方程是一个活跃的研究领域,近年来产生了许多引人入胜的作品。

应用程序

学习过的颂歌在各个领域的应用越来越多。我很幸运在计算机视觉[3]和机器人学[4,5]中应用了学习动力学的框架。在每一个项目之后,我都有了更多的直觉,对这个领域更加兴奋。与此同时,我也意识到我们还有很长的路要走,才能真正理解动力学和深度学习的相互作用。

我的一些代码片段是根据这个演示修改的。

参考文献

[1] T. Q. Chen、Y. Rubanova、J. Bettencourt 和 D. Duvenaud,“神经常微分方程”载于 NeurIPS,2018 年,第 6572–6583 页。

2 T. Z .贾浩,M. A. Hsieh,E. Forgoston,“基于知识的非线性动力学和混沌的学习”,混沌,第 31 卷,第 11 期,第 111101 页,2021 年。

[3]吴、贾浩、王、谢美华、谢志伟,“基于神经常微分方程的可变形图像配准优化框架”,计算机视觉与模式识别(CVPR)IEEE 会议,2022 .

[4] K. Y. Chee、T. Z. Jiahao和 M. A. Hsieh,“Knode-mpc:基于知识的数据驱动的空中机器人预测控制框架”,IEEE 机器人与自动化快报(RA-L),第 7 卷,第 2 期,第 2819–2826 页,2022 年。

[5]贾浩,潘立群,谢美华,“用基于知识的神经常微分方程学习群集”,IEEE 机器人与自动化国际会议(ICRA),2022 年。

[6] S. Brunton,J. Proctor 和 J. Kutz,“从数据中发现控制方程:非线性动力系统的稀疏识别”,《美国国家科学院院刊》113,3932–3937(2015)。

用于仇恨言论检测的深度学习:大规模实证评估

原文:https://towardsdatascience.com/deep-learning-for-hate-speech-detection-a-large-scale-empirical-evaluation-92831ded6bb6

为仇恨言论检测在实践中的使用提供指导,量化最新水平,并确定未来的研究方向

A 自动化的仇恨言论检测是打击仇恨言论传播的重要工具,尤其是在社交媒体中。已经为该任务开发了许多方法,包括最近激增的基于深度学习的方法。还开发了各种数据集,举例说明仇恨言论检测问题的各种表现。在这里,我们通过三个最常用的数据集,对深度和浅层仇恨言论检测方法进行了大规模的实证比较。我们的目标是阐明该领域的进展,并确定当前最先进技术的优势和弱点。我们特别关注对实际性能的测量,包括检测准确性、计算效率、使用预训练模型的能力和领域泛化。我们这样做的目的是为仇恨言论检测在实践中的使用提供指导,量化最新技术水平,并确定未来的研究方向。arXiv [1]提供了完整的研究报告。在这里,我们介绍了这项研究的概况和一些有趣的结果。查看下面的完整文件了解更多细节。

https://arxiv.org/abs/2202.09517

本研究中使用的代码和数据集可从下面的存储库中获得。

https://github.com/jmjmalik22/Hate-Speech-Detection

动机

在过去的十年里,社交媒体经历了令人难以置信的增长,无论是其规模还是作为一种沟通形式的重要性。社交媒体的本质意味着任何人都可以发布任何他们想要的东西,提出任何立场,无论是启发性的,令人反感的还是介于两者之间的。根据论坛的不同,这些帖子可能会被数百万人看到。不同的论坛对不当内容有不同的定义,识别不当内容的过程也不同,但媒体的规模意味着自动化方法是这项任务的重要组成部分。仇恨言论是这种不当内容的一个重要方面。

然而,仇恨言论是一个主观和复杂的术语,没有单一的定义。无论术语的定义或问题如何,很明显,在某些情况下,自动检测仇恨言论的方法是必要的。在这种情况下,采用准确、有效和高效的方法至关重要。

已经探索了用于仇恨语音检测任务的各种方法,包括传统分类器、基于深度学习的分类器或者两种方法的组合。另一方面,已经引入并发布了许多数据集基准来评估这些方法的性能,如 Davidson 2、Founta [3]和 Twitter 情感分析(TSA) [4]。

为了提供有见地的应用指南,本研究旨在对这些数据集上不同类型的仇恨言论检测方法进行全面的实证评估和比较。通过这项评估研究,我们回答了仇恨言论检测中的以下四个关键问题。I)不同流行检测模型对不同仇恨言论数据集的有效性如何?这很重要,因为拥有不同应用环境的从业者经常需要从大量的检测器中进行选择。ii)是否有任何特定的模型可以实现比其他模型更理想的性能(在检测精度和效率方面)?准确性和效率都是至关重要的,因为及时处理这些大量的在线文本数据需要计算上可扩展的和准确的检测器。iii)流行的预训练方法对检测模型的效果如何?预训练方法在推动许多机器学习和自然语言处理领域的发展方面一直发挥着重要作用,包括仇恨言论检测。因此,重要的是评估不同的预训练和仇恨言论检测器相结合的有效性。iv)检测模型在处理域转换时的可推广性如何?包括这个问题是因为,由于仇恨言论的种类、表达仇恨文本的方式以及不同语言之间的差异的多样性,跨域仇恨言论检测已经成为最重要的问题之一。

实证结果

在我们的实验中,采用了三个广泛使用的数据集,包括 Davidson、Founta 和 Twit-ter 情感分析(TSA ),它们的关键统计数据如下表 1 所示。

在这三个数据集上评估了 14 个浅、深分类器及其组合。

Q1:不同模型对流行基准的有效性

表 2 显示了所有 14 个仇恨言论检测模型在三个流行基准上的宏观和加权平均结果。ELECTRA 在三个数据集上表现出优异的性能;有趣的是,使用 TF-IDF 的 XGB 也获得了令人印象深刻的性能。

Q2:特定模型在准确性和效率上的优势

如表 2 所示,Glove 和 TF-IDF 模型并不总是具有优于彼此的性能。有趣的是,与基于手套的神经网络模型获得的结果相比,基于 TF-IDF 的 XGB 能够表现得更好或相当。在一些现有的研究中也可以发现类似的结果。就变压器嵌入而言,使用 BERT、ELECTRA 和 Al-BERT 在所有三个数据集中提供了最佳结果,表现出比竞争模型相当一致的优异性能。模型的计算时间如下图所示。据观察,虽然由转换器提供的准确性高于其他嵌入,但是它确实需要更多的时间来在数据集上训练。

问题 3:深度仇恨言论检测模型的预培训

如表 2 所示,预训练模型(例如,基于手套的 MLP 和基于变形金刚的 MLP)在三个数据集上的表现通常优于普通模型(例如,基于 TF-IDF 的 MLP)。基于变形金刚的预训练通常比基于 TF-IDF 的 MLP 模型好得多。

与基于手套的预训练相比,在宏 F1 和加权平均 F1 测量中,不同的基于变压器的预训练方法在所有三个数据集上都表现得一致更好。这适用于 CNN 和 MLP 的分类器。

Q4。跨域仇恨言论检测

然后,我们执行三组跨域实验,每个模型在三个数据集之一的训练数据上进行训练,并在另外两个数据集的测试数据上进行评估,即,一个数据集用作源域,而另外两个数据集用作目标域。此外,我们还展示了源域数据集测试数据的结果,作为基准性能。下图说明了三种跨域场景。

实验场景 1 是在 Davidson 上训练模型,在另外两个数据集上测试模型。结果如表 4 所示。与源域数据集 Davidson 的测试数据结果相比,Founta 和 TSA 上的跨域性能显著下降,尤其是在 Founta 上,所有手套和变形金刚模型在 macroF1and 和加权平均 1 得分上都获得了大约 0.5 到 0.6 的 1 得分。大多数模型在 TSA 上的表现变得更好,特别是在加权平均结果上。这表明 TSA 中的非仇恨推文与 Davidson 中的非仇恨推文具有一些共同的特征,导致在 nen-仇恨推文分类上的良好性能,从而导致良好的加权平均 F1 性能。

查看我们下面的预印本全文,了解更多跨领域的实验结果

https://arxiv.org/abs/2202.09517

结论

我们在三个不同数据特征的常用基准上,对 14 个用于仇恨言论检测的浅层和深层模型进行了大规模的经验评估。这将为其检测准确性、计算效率、使用预训练模型的能力以及在现实应用中部署的领域可推广性提供重要的见解。

模型实现和数据集在以下 GitHub 上公开发布。

https://github.com/jmjmalik22/Hate-Speech-Detection

参考文献

[1] Singh Malik,Jitendra,Guansong Pang 和 Anton van den Hengel .“仇恨言论检测的深度学习:一项比较研究。” arXiv 电子版 (2022): arXiv-2202。
2戴维森、托马斯、达纳·沃姆斯利、迈克尔·梅西和英格玛·韦伯。"自动仇恨言论检测和攻击性语言的问题."在网络和社交媒体国际 AAAI 会议记录中,第 11 卷,第 1 期,第 512-515 页。2017.
[3]丰塔、安提戈尼·玛丽亚、康斯坦丁诺斯·朱瓦斯、德斯波纳·查察库、伊利亚斯·莱昂蒂亚迪斯、杰里米·布莱克本、吉安卢卡·斯特林希尼、雅典娜·瓦卡利、迈克尔·西里瓦诺斯和尼古拉斯·库尔特利斯。" twitter 滥用行为的大规模众包和特征描述."在第十二届国际网络和社交媒体 AAAI 会议上。2018.
【4】https://data hack . analyticsvidhya . com/contest/practice-problem-Twitter-情操-分析/?UTM _ source = av _ blog&UTM _ medium = practice _ blog _ text _ class ification # # discuss tab

深度学习并不比表格数据上的树“差”

原文:https://towardsdatascience.com/deep-learning-is-not-worse-than-trees-on-tabular-data-1de25ed31d2

阿啦 pixabay

超越表格数据的经典概念

深度学习已经成为人工智能的公共和私人面孔。当一个人在聚会上与朋友、街上的陌生人和工作中的同事随意谈论人工智能时,几乎总是谈论令人兴奋的生成语言、创造艺术、合成音乐等模型。大规模和复杂设计的深度学习模型为这些令人兴奋的机器能力提供了动力。

然而,许多从业者正理直气壮地反对深度学习的技术轰动效应。虽然深度学习“很酷”,但它肯定不是建模的最终目的。

尽管深度学习无疑主导了图像、文本和音频等专业的高维数据形式,但普遍的共识是,它在表格数据中的表现相对较差。

因此,那些对深度学习有些厌恶甚至怨恨的人用表格数据表明了他们的观点。(发表公认的深度学习论文,进行看似琐碎甚至科学上可疑的修改,这在过去和现在都很流行。这是对深度学习研究文化的抱怨之一,我在这个领域的许多思想更为传统的同事都表达了这一点,这是公平的。)

现在,在这个少数群体中,抨击“新晋新一代数据科学家”过于迷恋深度学习也很流行,相反,他们吹捧相对更经典的基于树的方法,认为这是表格数据的“最佳”模型。你会发现这种观点无处不在——在大胆的研究论文、面向人工智能的社交媒体、研究论坛和博客帖子中。的确,反文化往往和主流文化一样时髦,无论是和嬉皮士还是深度学习。

这并不是说没有好的研究表明基于树的方法优于深度学习——当然有。但是,这种细致入微和语境化的研究往往被错误地认为是一种普遍规律,那些厌恶深度学习的人往往会像许多推进深度学习状态的人一样,信奉同样有问题的学说:在一组通常定义良好的限制内获得结果,并故意以不考虑所述语境界限的方式外推它们。

那些提倡基于树的模型而不是深度学习模型的人最明显的短视是在问题域空间。对表格深度学习方法的一个常见批评是,它们看起来像是“技巧”,是零星工作的错综复杂的一次性方法,而不是可靠的高性能树方法。智力问题 Wolpert 和 Macready 的经典的没有免费的午餐定理让我们思考每当我们遇到普遍(或甚至接近普遍)优势的主张,无论这是在性能、一致性或另一个度量上的优势,是:(near) 在问题空间的哪个子集上是普遍的?

广为人知的研究调查和更非正式的调查使用的数据集显示了深度学习在表格数据上的成功,这些数据集是常见的基准数据集-森林覆盖数据集,希格斯玻色子数据集,加州住房数据集,葡萄酒质量数据集,等等。这些数据集,即使以几十个为单位进行评估,无疑也是有限的。当然,我们必须承认,与这些更同质的基准数据集相比,使用表现不佳的多样化数据集进行评估性调查研究要困难得多。

然而,那些吹捧此类研究结果对神经网络在表格数据上的能力做出广泛判断的人,忽视了机器学习模型应用于表格数据领域的广度。

在所有数据形式中,表格数据在结构质量和领域上是最多样化的,这种说法不无道理。从这个意义上说,“表格数据”更多的是一个“大他者”,甚至是“大全部”,而不是一个特定类型或结构的数据。

让我们考虑一些超出这个狭窄基准集的表格数据问题的真实例子。

随着可从生物系统获得的数据信号的增加,生物数据集的特征丰富性从仅仅一二十年前的状态显著增加。这些表格数据集的丰富性揭示了生物现象的巨大复杂性——从局部到全球的多种尺度的复杂模式,以无数种方式相互作用。深度神经网络几乎总是被用于对表示复杂生物现象的现代表格数据集进行建模。

或者,内容推荐,一个需要仔细和高容量建模能力的复杂领域,或多或少普遍采用深度学习解决方案。例如,网飞报告称,在实施深度学习时,“通过线下和线上指标衡量,我们的建议有了很大改善”。类似地,谷歌的一篇论文展示了深度学习作为支持 YouTube 建议的范式的重组,该论文写道,“与谷歌的其他产品领域相结合,YouTube 经历了一个基本的范式转变,即使用深度学习作为几乎所有学习问题的通用解决方案。”

只要我们看一看,我们可以找到更多的例子。许多表格数据集包含文本属性,例如包含文本评论以及以表格形式表示的用户和产品信息的在线产品评论数据集。最近的房屋列表数据集包含与标准表格信息(如平方英尺、浴室数量等)相关联的图像。或者,考虑股票价格数据,该数据除了以表格形式获取公司信息之外,还获取时间序列数据。如果除了表格数据和时间序列数据之外,我们还想添加十大财经头条来预测股票价格,该怎么办?据我所知,基于树的模型不能有效地解决这些多模态问题。而深度学习则可以用来解决所有的问题。

事实是,自 2000 年代和 2010 年代初以来,数据已经发生了变化,当时许多基准数据集用于研究深度学习和基于树的模型之间的性能差异。表格数据比以往任何时候都更加精细和复杂,捕捉了广泛的难以置信的复杂现象。在表格数据的环境中,深度学习作为一种非结构化、稀疏和随机的成功方法显然是不正确的。

此外,原始监督学习不仅仅是对表格数据建模的单一问题。

  • 表格数据通常是有噪声的,我们需要一些方法来消除噪声,或者开发一些对噪声具有鲁棒性的方法。
  • 表格数据也经常不断变化,因此我们需要能够在结构上轻松适应新数据的模型。
  • 我们还经常遇到许多不同的数据集,它们共享一个基本相似的结构,因此我们希望能够将知识从一个模型转移到另一个模型。
  • 有时缺少表格数据,我们需要生成现实的新数据。
  • 我们也希望能够用非常有限的数据集开发非常健壮和通用的模型。

同样,就我们所知,基于树的模型要么不能做到以上所述,要么很难做到以上所述。另一方面,神经网络在适应来自计算机视觉和自然语言处理领域的表格数据后,可以成功地完成以下所有工作。

当然,对神经网络有一些重要的合理的普遍反对意见。

其中一个反对意见是可解释性——认为深度神经网络比树模型更难解释。可解释性是一个特别有趣的想法,因为它更多的是人类观察者的属性,而不是模型本身的属性。对数百个特征进行操作的梯度推进模型真的比在同一数据集上训练的多层感知器更具内在可解释性吗?树模型确实建立了容易理解的单一特征分割条件,但是这本身并没有什么价值。此外,像梯度增强系统这样的流行树集成中的许多或大多数模型不直接对目标建模,而是对残差建模,这使得直接可解释性更加困难。我们更关心的是特性之间的交互。为了有效地理解这一点,树模型和神经网络都需要外部可解释性方法来将决策的复杂性分解为关键的方向和力量。因此,尚不清楚在复杂数据集上,树模型是否比神经网络模型更具有内在的可解释性。

第二个主要的反对意见是调整神经网络的元参数的麻烦。这是神经网络不可调和的属性。鉴于架构、培训课程、优化过程等的可能配置和方法的纯粹多样性,神经网络基本上更多地作为理想而不是真正的具体算法存在。应当注意,对于计算机视觉和自然语言处理领域,这是一个比表格数据更突出的问题,并且已经提出了减轻这种影响的方法。此外,应该注意,基于树的模型也具有大量元参数,这通常需要系统的元优化。

第三个反对意见是神经网络和树模型之间的训练时间差异(树模型比神经网络快)。任何以表格数据为代表提出这种异议的人都表明,他们对表格数据有一个非常具体的概念——即小而简单的数据集。在大型和复杂的数据集上,基于树的模型在时间和空间方面的训练成本都很高。(无论如何,在小而简单的表格数据集上,树模型往往做得更好。这是无可争议的。这里的问题是表格数据的错误限制空间。)

另一个反对意见是神经网络不能以反映特征的有效含义的方式有效地预处理数据。流行的基于树的模型能够以一种被认为对异构数据更有效的方式解释特征。然而,这并不妨碍深度学习与预处理方案的联合应用,预处理方案可以在访问表达特征方面将神经网络置于与基于树的模型平等的基础上。

所有这些都是为了挑战基于树的模型优于深度学习模型的概念,甚至是基于树的模型一贯或普遍优于深度学习模型的概念。这也不是说深度学习总体上优于基于树的模型。相反,我们的目标是全面和公平。

为了清楚起见,提出了以下主张:

  • 深度学习在某个明确定义的问题领域上成功工作,就像基于树的方法在另一个明确定义的问题领域上成功工作一样。
  • 深度学习可以解决基于树的方法无法解决的原始表格数据到标签监督学习之外的许多问题,例如对多模态数据(图像、文本和/或音频等)进行建模。表格数据之外的数据)、去噪数据、跨数据集传递知识、在有限的数据集上成功训练以及生成数据。
  • 深度学习确实容易出现困难,比如可解释性和元优化。然而,在大多数情况下,基于树的模型在某种程度上遭受相同的问题(在深度学习首先至少会取得一定成功的足够复杂的情况下——我们必须在这里进行比较)。

希望这能引起一些思考,并标志着深度学习至少是大范围表格数据问题的一个有意义的候选。

当然,我知道这可能是一个不受欢迎的立场。我欢迎回复中的任何讨论——让我们开始吧!

以上是从表格数据的现代深度学习中修改的摘录,这是我与人合著的一本书,将于今年年底发行。这本书的目的是通过提供理论和工具将深度学习应用于表格数据问题,进一步证实这些说法。在超过 1000 页的好材料中,我们调查了几十篇研究论文和许多有趣的想法。这是其中的一个小例子:

  • 使用元优化为大型异构数据集选择最佳经典分类编码策略
  • 将表格数据转换成图像,然后应用卷积神经网络
  • 理解注意力是处理表格数据中异质性的自然方式
  • 构建多模态模型,根据图像、时间序列、文本和表格输入执行监督预测
  • 探索使用软模拟来模拟决策树结构的架构
  • 在隐私和数据集大小约束下为模型训练生成表格数据
  • 使用自动编码器架构在标签稀缺的环境中执行自监督预训练

(拿起这本书后,你会了解所有这些,甚至更多!)

我们也给出了树算法和它们适用的问题类型的一个相当广泛的概述。

再说一遍,这本书的目的不是要“站在”深度学习的“一边”。取而代之的是展示现有的各种各样的技术,以及它们可以成功应用于的表格数据问题的类型。

有兴趣的话,在这里预购!我们很感激。

而且,如果你对最新的文章感兴趣,可以考虑订阅。如果你想支持我的写作,通过我的推荐链接加入 Medium 是一个很好的方式。干杯!

深度学习模型优化变得容易(或至少更容易)

原文:https://towardsdatascience.com/deep-learning-model-optimizations-made-easy-or-at-least-easier-a0255182f427

可持续人工智能,一次优化一个模型

照片由 DeepMindUnsplash 上拍摄

介绍

深度学习人工智能模型在过去十年中发展迅速,伴随着这种快速增长的是计算资源需求的爆炸式增长。每一个更大的模型都需要更多的计算资源和更多的位移动,包括进出不同的内存层次和跨系统。

可持续人工智能以及为什么深度学习优化对我很重要

2020 年 1 月,连线杂志发表了这篇文章,人工智能可以做伟大的事情——如果它不烧毁地球。最近,MIT Technology Review 撰写了一篇文章,这些简单的变化可以使人工智能研究更加节能,讲述了艾伦人工智能研究所、微软、拥抱脸和几所大学如何合作,以了解如何通过基于可再生能源何时可用来运行工作负载来减少排放。

在我的上一篇文章中,我花了一些时间思考可持续人工智能,并讨论了传统深度学习神经网络的一些软件/硬件替代方案。虽然我没有围绕可持续发展来撰写这篇文章,但所有这些技术都有机会解决与特定领域的深度学习模型类似的问题,同时显著减少用于实现这些解决方案的计算能力。

模型性能优化的奇妙之处在于,它不仅提高了性能,还降低了成本和能耗。通过利用下面的一些技术,我们可以更快、更便宜、更可持续地解决有趣的问题。

常见的深度学习优化

知识蒸馏

顾名思义,知识提炼的目标是从一个模型中获取功能,并将其转移到另一个模型中。通过利用已经是问题的工作解决方案的模型,我们能够创建一个相似的、不太复杂的模型来执行相同的任务。显然,较小的模型必须具有相似的精度才能成功地进行蒸馏。

在许多关于该主题的现代出版物中,教师/学生类比被用来描述知识提炼学习模型如何工作。使用较大的教师模型来帮助训练较小的学生模型有三种不同的方式:基于响应的知识、基于特征的知识和基于关系的知识。下图很好地帮助我们理解了教师模型如何通知学生模型。

我们的知识从哪里来?来源:知识提炼:一项调查

基于响应的知识通过查看教师模型的输出来帮助训练学生模型。这可能是创建较小模型的最常见的方式。我们采用较大的模型输出,并基于相同或相似的输入,尝试在较小的模型上获得相同的输出行为。

基于特征的知识通过尝试让中间层模仿教师模型的行为来帮助训练学生模型。这可能很困难,因为捕捉模型的中间特征激活并不总是容易的。然而,在这个领域中已经做了各种工作来捕获中间特征的行为,这使得这种基于特征的知识提取成为可能。

基于关系的知识转移是基于这样一种思想,即在教师网络中,网络中显著不同部分的输出可以一起工作来帮助推动输出。定义算法来帮助训练有点不太直观,但基本思想是采用各种节点组,通常称为特征图,并训练学生节点来提供与父节点中的特征图相似的输出。

通过这三种技术的不同组合,已经表明一些非常大的模型可以被移植到较小的表示中。可能其中最著名的是 DistilBERT ,它能够保持“与 BERT 相比 97%的语言理解能力,同时拥有 40%的小模型和 60%的快速度。”

量化

也许最知名的深度学习优化类型是量化。量化包括采用使用更高精度数字格式(如 32 位或 64 位浮点表示)训练的模型,以及使用较小精度数字格式(通常为 8 位整数(INT8)格式)的神经网络来再现功能。

有几种量化的方法。可以在初始模型被训练之后执行量化。然后,可以通过缩放原始模型内的权重来计算后续 INT8 模型,以生成新模型。这样做的好处是,可以在对现有模型进行微调后,对它们进行优化。

另一种选择是将量化技术作为初始训练过程的一部分。与上述训练后计算的 INT8 模型方法相比,此过程通常会创建更准确的 INT8 模型,但这是以创建模型训练系统时的前期复杂性为代价的。

在这两种情况下,使用 INT8 表示的结果在模型大小方面提供了显著的节省,这转化为更低的内存和计算需求。正如官方 TensorFlow 量子化感知培训网站上记录的那样,这通常可以在很少或没有准确度损失的情况下完成。

使优化更容易

可以想象,这些关于如何创建更小但仍然有效的模型的简单描述需要各种复杂的现实世界的解决方案来正确执行它们。有大量的研究论文致力于这些主题,并且大量的研究已经进入可以概括这些解决方案的方法。

TensorFlow 和 PyTorch 都提供了一些量化 API 来简化量化过程。我还打算在网上找一些知识提炼过程的例子,这无疑是相当复杂的。Keras 有一个很好的张量流例子:

https://keras.io/examples/vision/knowledge_distillation/

对于 PyTorch 来说,这是一个很好的知识提炼的例子,尽管示例代码有点老:

可以想象,结合这些技术来生成一个优化的模型并不一定是一项简单的任务。为了帮助提供简化的模型优化工作流程,英特尔最近发布了英特尔神经压缩器,作为英特尔人工智能分析工具包的一部分。这个用于 CPU 和 GPU 部署的开源 python 库简化并自动化了执行这些优化的大量设置和过程。由于它支持 TensorFlow、PyTorch、MXNet 和 ONNX,这个库应该能够帮助快速地将许多较大的模型迁移到更小、更优化的模型中,这些模型需要更少的硬件资源来运行。关于如何在 PyTorch 中利用这个库的更多细节,请查看这篇文章:

https://medium.com/pytorch/pytorch-inference-acceleration-with-intel-neural-compressor-842ef4210d7d

根据您的用例以及您已经在使用的框架,还有其他的选择。例如,如果你碰巧在使用类似 openVINO 的东西,你可以利用框架的相关解决方案,神经网络压缩框架(NNCF)训练后优化工具(POT) 。显然,您的最佳选择是尝试使用一个与您已经利用的框架或 SDK 相关联的工具。

结论

深度学习模型是许多行业解决方案的重要组成部分。随着这种趋势的继续,模型压缩和优化对于减少模型的大小,使它们比以前运行得更快更有效是至关重要的。这些技术提供了能量使用量的标量减少,但是在其核心,最终解决方案仍然是神经网络。

作为一个社区,这既是一个难以置信的挑战,也是我们在推动创新的同时找到更多减少能源使用的方法的当务之急。展望未来,我希望看到范式是否以及如何转变,以使我们能够继续利用人工智能,但计算和能源使用量将呈指数级下降。

**Want to Connect?**If you want to see what random tech news I’m reading, you can [follow me](https://twitter.com/tonymongkolsmai) on Twitter.Tony is a Software Architect and Technical Evangelist at Intel. He has worked on several software developer tools and most recently led the software engineering team that built the data center platform which enabled Habana’s scalable MLPerf solution.Intel, the Intel logo and other Intel marks are trademarks of Intel Corporation or its subsidiaries. Other names and brands may be claimed as the property of others.

参考

  1. 提取神经网络中的知识。辛顿 G,维尼亚尔斯 O,迪安 J(2015)https://arxiv.org/abs/1503.02531
  2. 知识升华:一个综述。苟 J,于 B,梅班克 SJ,陶 D(2021)
  3. 蒸馏伯特,伯特的蒸馏版本:更小,更快,更便宜,更轻。桑五,出道 L,肖蒙德 J,狼 T(2019)https://arxiv.org/abs/1910.01108

深度学习模型可视化工具:哪个最好?

原文:https://towardsdatascience.com/deep-learning-model-visualization-tools-which-is-best-83ecbe14fa7

为您的数据科学项目可视化您的下一个深度学习模型的不同方式。

  • 注意:我与今天讨论的公司或 API 没有任何关系。

图片来自作者

介绍

可视化很重要,原因有很多。它们有助于支持和巩固正文中的信息。你知道吗,人类不仅通过阅读和讲课来学习,他们还通过观察来学习。深度学习模型,即。神经网络很难用语言来描述。生成您创建的神经网络的可视化可以帮助读者完全理解和检查开发的模型。

我们今天要看的可视化深度学习模型的 5 个工具是:

  1. Microsoft Powerpoint
  2. 人工神经网络可视化工具
  3. 喀拉斯总结()【方法
  4. Keras Utils 模型绘图功能
  5. Keras 视觉

Microsoft Powerpoint

是的,第一个可视化深度学习模型的工具是微软 Powerpoint。我认为 Powerpoint 是创建深度学习模型可视化的一种选择,因为它的可访问性、易用性、灵活性便于用户操纵和控制视觉效果。

在 PowerPoint 中创建的可视化效果(图片来自作者)

赞成的意见

如前所述,微软 Powerpoint 的优点在于其可访问性、易用性、灵活性。

  1. 可访问性 -任何在电脑上运行微软产品的人都可以访问 Powerpoint。
  2. 易用性 -该应用程序也非常“尖尖的”。基本上,要创建可视化效果,您需要做的就是添加形状和文本框。在那里,你可以定义它们的物理特性,制作出独一无二的最终产品。这与 Powerpoint 的灵活性紧密相关。
  3. 灵活性-Powerpoint 中有如此多不同的特性,允许你创建你想要的模型的几乎任何表示。

骗局

  1. 创建一个非常详细的可视化效果可能需要很长时间。
  2. Powerpoint 仍然受限于你想要创建的可视化的抽象程度。
  3. Powerpoint 不像我们今天看到的其他包那样与 Python 中创建的模型交互。

人工神经网络可视化工具

下一个可视化工具是 ANN Visualizer API。这个工具你需要做的就是运行一行代码来打印你的模型,然后你就可以访问它创建的图像了。

# !pip install graphviz
import graphviz
model = Sequential()
model.add(layers.Convolution2D(32,3,3,input_shape=(28,28,1),activation= layers.LeakyReLU()))
model.add(layers.MaxPooling2D(pool_size=(2, 2)))
model.add(layers.Convolution2D(64,3,3,activation=layers.LeakyReLU()))
model.add(layers.MaxPooling2D(pool_size=(1, 1)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(Dense(units=10, activation='softmax'))

ann_viz(model , view=True, title= 'My Model', filename = 'output.gv')

运行这段代码后,API 输出下图供您个人使用。

人工神经网络 API 图像(图像来自作者)

赞成的意见

  1. 创建模型的高分辨率图像。
  2. 构建图形结构的模型,使用户很容易看到所有不同的连接。
  3. 易于理解的视觉效果。
  4. 提供深度学习模型每一层的参数数量。

骗局

  1. 我不得不将我的模型的格式更改为 blocked layers(它不接受我在所有其他示例中使用的其他格式)
  2. 不容易访问(在 Colab 中),它将文件保存为. gv 文件

喀拉斯。summary()方法

如果你在 Keras 中创建一个模型,你可以使用。summary() 方法打印出您的模型,并获得它所包含内容的概要。

model = keras.Sequential(
    [
        keras.Input(shape=input_shape),
        layers.Conv2D(32, kernel_size=(3, 3)),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Conv2D(64, kernel_size=(3, 3)),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Flatten(),
        layers.Dropout(0.5),
        layers.Dense(num_classes, activation="softmax"),
    ]
)

model.summary()

喀拉斯。summary()方法(图片来自作者)

赞成的意见

  1. 如果您正在 Keras 中创建模型,将为您提供一个快速摘要。
  2. 明确显示每个模型层中不同数量的参数。(甚至列出了可训练参数的总数)。
  3. 清楚地定义了每一层。

骗局

  1. 仅适用于使用 Keras 开发的车型。
  2. 对于书面报告来说,不会产生视觉上吸引人的输出。
  3. 输出中没有交互组件。
  4. 没有颜色。

Keras Utils 模型绘图功能

Keras Utils 模型绘图功能比高了一步。方法总结()。它用一行代码创建了一个更具视觉吸引力的模型概述。

from keras.utils.vis_utils import plot_model

#Your Model code here use the Keras Sequential Architecture

plot_model(model, to_file='model_plot.png', show_shapes=True, show_layer_names=True)

Keras Util 模型绘图功能(图片来自作者)

赞成的意见

  1. 比 Keras 更视觉化。总结()方法。
  2. 清楚地说明输入和输出尺寸大小。
  3. 可以在一行代码中完成。

骗局

  1. 不能产生最具视觉吸引力的图像(没有颜色!).
  2. 必须用于 Keras 模型。

视觉角膜

在今天讨论的三种不同的 Keras 方法中,可视化 Keras 方法在视觉上是最吸引人的!就像其他方法一样,这可以在一行代码中完成。

model = keras.Sequential(
    [
        keras.Input(shape=input_shape),
        layers.Conv2D(32, kernel_size=(3, 3)),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Conv2D(64, kernel_size=(3, 3)),
        layers.LeakyReLU(),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Flatten(),
        layers.Dropout(0.5),
        layers.Dense(num_classes, activation="softmax"),
    ]
)

visualkeras.layered_view(model, legend=True,to_file = '/content/gdrive/MyDrive/output.png').show()

如果您不想要下图所示的图例,也可以说 legend=False

视觉 Keras 模型(图片来自作者)

赞成的意见

  1. 为 Keras 模型创建视觉上吸引人的图像。API 详细描述了每个层,并根据它们在模型中的大小以不同的长度显示它们。
  2. 与其他两个 Keras 函数不同,该 API 为视觉效果添加了颜色。

骗局

  1. 仅适用于 Keras 车型。
  2. 没有给出与每层相关的各种超参数(即每层的输入和输出尺寸)。
  3. 它使不同类型的层颜色相同,这可能是非常混乱的(卷积层与上面图像中的致密层颜色相同)。

结论

哪种可视化工具最好的最终结论是…?

看情况!

如果你想控制可视化效果的所有方面,就用微软的 PowerPoint 。代价是你将花费更多的时间和精力来创建可视化。 ANN Visualizer 是一个伟大的工具,用于显示深度学习模型的流程,同时使图形视觉上具有娱乐性。我在 visualizer API 上遇到的最大问题是,我必须在 Keras 中更改我的神经网络的设置。

如果深度学习模型是用 Keras 和开发的,你想快速总结一下模型,就用 Keras。方法总结()。这个工具的缺点是摘要在视觉上没有吸引力,也不是书面报告的最佳选择。 Keras 的 utils 模型绘图功能可能比更优。summary() 方法,但请记住,它仍然不是一个直观的图像,也没有列出可训练的参数。

如果您想用 Keras 创建一个读者更容易注意到的图像,请使用 Visual Keras。V isual Keras 为深度学习模型增加了大小和颜色,这将获得用户的注意,并清楚地定义了模型的架构。一个警告是,Visual Keras 没有列出与深度学习模型的每一层相关联的值。

如果你喜欢今天的阅读,请关注我,让我知道你是否还有其他想让我探讨的话题!如果你还没有介质账号,就通过我的链接 这里 报名吧!另外,在LinkedIn上加我,或者随时联系!感谢阅读!

深度学习:不,LSTMs 没有死!

原文:https://towardsdatascience.com/deep-learning-no-lstms-are-not-dead-20217553b87a

如果他们都死了,为什么他们还能赢得 Kaggle 比赛?

里卡多·L 在 Unsplash 上的照片

在过去十年中,谁一直在密切关注机器学习领域?

这样做的人见证了前所未有的科学的革命性进步。这就像 20 世纪初,爱因斯坦的论文成为量子力学的基础。只是这一次,是 AlexNet 论文【1】,一个挑战计算机视觉的架构,重新点燃了人们对机器学习(后来转化为深度学习)的兴趣。

这种无情增长的警告是很难正确评估每一个突破:在一个新功能被引入并开始普及之前,另一个功能会出现——更强大、更快或更便宜。巨大的增长带来了太多的宣传,吸引了许多新来者,他们往往热情高涨,但经验不足。

深度学习领域中一个被误解的突破是递归神经网络家族。如果你在谷歌上搜索诸如“lstm 死了”“RNNs 已经死了”这样的短语,你会发现大量的结果,其中大部分都是不正确的或者没有给出完整的画面。本文将向您展示递归网络仍然是相关的,并在许多实际场景中找到用途。

但是首先,我会给你历史背景来理解为什么大多数人相信相反的事情。还有,这篇文章并不是只说lstm变形金刚。您还将学习如何不带偏见地评估数据科学中的一个概念。

输入 LSTMs

每个大型科技公司都采用 LSTMs 没有 LSTMs 就没有 NLP 研究。

长短期记忆网络—lst ms【2】于 2014 年开始起飞,尽管它们早在 1997 年就被引入。它们属于 递归神经网络家族——RNNs【3】,以及门控递归单元——GRU【4】。

随着 GPU 的可访问性和第一个深度学习框架的出现, LSTMs 成为主导 NLP 领域的最先进模型。2013 年单词嵌入的发现也有助于建立迁移学习的机制。事实上,当时几乎任何一个 NLP 任务的标准组件都是:a) 预训练单词嵌入,b)lstm和 c)序列到序列架构【5】。

那个时期的每个数据科学家都会同意lstm 统治了 NLP 领域:它们被用于语音识别、文本到语音合成、语言建模和机器翻译。每一家大型科技公司都欣然接受了它们;没有lstm就没有 NLP。

谷歌为机器翻译创造的最佳模型之一如图图 1 所示:

图 1 :谷歌神经机器翻译——GNMT 架构(来源)

这个在[6]中引入的复杂模型是谷歌翻译服务的幕后推手。与之前的产品相比,它减少了 60%的翻译错误。如你所见,它大量使用了lstm,形成了著名的编码器-解码器拓扑(包括一个双向 LSTM )。

这种实现还利用了的机制,允许模型根据需要关注输入序列的相关部分。这显示在图 1 的中,其中使用注意力分数对编码器的顶部向量进行加权。换句话说,每个时间步的每个单词都用一个可学习的分数加权,以最小化错误。更多信息,请查看这篇文章,或者更好地阅读原文【5】。**

然而,LSTMs 有两个主要缺点。它们在训练期间不容易并行化。
2。由于它们的循环性质,它们可以模拟的序列长度是有限的。

但稍后会详细介绍。

输入变压器

rnn 是顺序模型,这意味着单词是按顺序处理的。但是转换器并行处理所有的单词。

2017 年,谷歌推出了Transformer【7】架构,这是 NLP 生态系统的一个里程碑。这款新机型通过提出多头注意力机制,对注意力进行了更深入的研究,该机制:****

  • 充分利用的自我关注,从而实现优越的性能****
  • 采用模块化结构,使繁重的矩阵运算更加并行化。也就是说,运行速度更快,可扩展性更好。****

然而,在转换后的模型中没有使用lstm。即使在上下文信息很重要的第一层(并且lstm可能有用), Transformer 论文提出了一种不同的机制,称为位置编码。这也揭示了两类模型的主要区别:rnn是顺序模型,意思是单词按顺序处理。但是 变压器并行处理所有字。这大大减少了培训时间。****

从那以后, Transformer 的核心理念一直是语言处理进一步研究的基础,催生了新的变种。这些如图 2 所示。****

图 2 :开源变压器家族(来源

别忘了时间序列!

lstm变形金刚都非常擅长模拟序列信息。因此,它们也可以应用于时间序列预测的情况。****

如果你对时间序列预测感兴趣,可以查看我的 最佳深度学习预测模型列表

传统统计赢得第一轮

然而,实验结果表明,在准确性方面,他们并不能决定性地胜过传统的统计方法(如 ARIMA )。另一方面,统计方法和基于 rnn 的方法的结合更加有效。一个这样的例子是由优步建造的 ES-RNN 模型,它最终赢得了 M4 竞赛:它是一个混合模型,在扩张 LSTM 的基础上使用指数平滑****

自然地,变压器受到了考验。对于时间序列预测,最常见的方法如下:使用原始的转换器,并用 Time2vec 层替换位置编码层*。但是变压器模型都无法超越统计方法*****

此外,我想澄清几件事:

  • 这并不意味着统计方法总是更好。例如,如果有大量数据,LSTMs 可能会比 ARIMA 表现得更好。
  • 统计方法需要更多的数据预处理:这可能包括使时间序列平稳(如果不是),消除季节性,波动性,等等。 LSTMs 可以更容易地捕捉到序列的自然特征,至少是通过使用更简单的技术。
  • 此外,统计方法不太通用:例如,自回归方法不能处理未来未知的额外特征。

底线是 ML 方法在预测能力方面并不总是比统计方法更好。

深度学习赢得第二轮

直到 2018-2019 年,研究才有了回报,深度学习模型开始在时间序列预测任务中变得更具竞争力。关于时间序列预测和深度学习的更全面的分析,请查看这篇文章:

图 3和图 4中显示了两种最先进的模型。分别描绘了 谷歌的 时态融合变压器亚马逊的 DeepAR 的架构。注意到什么有趣的事了吗?****

*****

图 3: 颞融合变压器(来源)*****

图 4: DeepAR 模型架构(来源)

嗯,这些模型有很多有趣的地方,但最重要的一点是与文章主题产生共鸣的是:****

两种型号都采用了 LSTMs!但是怎么做呢?

DeepAR 是一个结合了自回归和深度学习特性的复杂时间序列模型。图 4 显示的h_i,t向量实际上是 LSTM 单元的隐藏状态。这些隐藏状态然后被用于计算高斯分布的μσ参数。从该分布中,选择n个样本,其中值代表预测值。****

时间融合转换器——TFT是一个多层的、纯深度学习的时间序列模型。这个模型既有一个 LSTM 编码器-解码器也有一个新颖的注意力机制提供可解释的预测。我们不会在这里深究这个模型的细节——查看这篇惊人的文章,它提供了一个彻底的解释。****

底线是,这两种深度学习模型都优于传统的统计方法。此外,这两种模型都更加通用,因为它们可以处理多个时间序列,并接受更丰富的功能集(TFT 略胜一筹)。

复发和注意力是如何相关的

为了说明这一点,我们将强调 TFT 白皮书中的一段摘录:

为了学习不同尺度下的时间关系,TFT 使用递归层进行本地处理,使用可解释的自我关注层进行长期依赖****

考虑到目前为止我们所知道的,以及上面的摘录,我们现在可以将这些点联系起来:

递归网络擅长捕捉序列的局部时间特征,而注意力更擅长学习长期动态。

这不是一个武断的结论。 TFT 论文的作者通过执行所谓的消融分析来证明这一点:在这种类型的分析中,我们移除或替换复杂机器学习系统的某些组件,以了解每个组件的贡献。****

TFT 的作者测试了 LSTM 编码器-解码器层,以及其他组件:他们通过用原始变压器的标准位置编码层来替换它。他们发现了两件事:****

  1. 利用序列到序列层有助于模型的性能。**
  2. 在执行基准测试的 5 个数据集的 4 个中, LSTM 层实现了更高的性能。

因此,我们可以有把握地得出结论, LSTM 层仍然是时间序列深度学习模型中非常宝贵的组件。而且,它们不会对抗注意力机制。相反,它们仍然可以与基于注意力的组件相结合,以进一步提高模型的效率。****

lst ms 的隐藏宝石:条件输出

这是 LSTMs 最容易被忽视的优点之一,许多数据科学从业者仍然没有意识到这一点。

如果您一直在使用普通的递归网络,您会知道这种类型的网络只能处理时态数据——这些数据被表示为相互之间有各种依赖关系的序列。但是,它们不能直接对静态元数据或时不变数据建模。

在自然语言处理中,静态元数据是不相关的。相反,NLP 模型专注于单词的词汇表,其中每个单词都由一个嵌入表示,这是整个模型中的一个统一概念。每个单词来自哪种类型的文档并不重要,只要 NLP 模型能够学习每个单词的正确的上下文感知表示 。记住,一个特定的单词可以有不同的嵌入,这取决于它的意思和它在句子中的位置。

然而,在时间序列模型中,时不变数据具有更大的影响。例如,假设我们有一个涉及商店产品的销售预测场景。产品的销售量可以建模为时间序列,但它也会受到节假日等外部因素的影响。所以,一个好的预测模型也应该考虑那些变量。这就是 TFT 的作用(见图 5* )。但是 TFT 是如何实现的呢?*****

*****

图 5: 外部静态变量对预测的影响(来源)*****

TFT 被专业地设计来集成静态元数据。它使用了各种技术,在原始文件中有描述。然而,最重要的一点与 LSTMs 有关。

LSTMs 可以无缝地执行这项任务,使用的是【11】中首次介绍的技巧:不是将初始h_0隐藏状态和 LSTM 的单元格状态c_0设置为 0(或随机),而是用我们选择的指定向量/嵌入来初始化它们。或者我们可以在拟合期间使这些向量可训练(实际上, TFT 就是这么做的)。以这种方式,LSTM 单元的输出适当地取决于外部变量,而不影响其时间相关性。****

LSTMs vs TCNS

关注变形金刚出现之前,又出现了一个承诺改变风景的新颖模型。这些就是所谓的时间卷积网络——TCN。****

TCN使用扩张卷积,在不同长度的输入序列上应用填充——使它们能够检测不仅彼此接近而且位置完全不同的项目之间的依赖关系。

TCNs 于 2016 年[12]首次推出,并于 2018 年[13]正式化,利用卷积网络对基于序列的数据进行建模。自然,他们也是时间序列预测任务的理想候选人。

图 6: 滤波器大小 k = 3,膨胀因子 d = 1,2,4 的膨胀卷积。感受野可以覆盖来自输入序列的所有数据点x_0...x_T。(来源)

TCNs 的“秘密武器”就是膨胀卷积,如图6 所示。标准 CNN 使用固定大小的核/过滤器,因此它们只能覆盖紧邻的数据元素。相比之下,TCN使用扩展卷积,在不同长度的输入序列上应用填充——使它们能够检测不仅彼此接近而且位置完全不同的项目之间的依赖关系。****

除此之外,他们还使用其他技术,如剩余连接,这现在是深度网络的标准。同样,我们不会深究细节(在一篇新文章中会有更多的内容)。相反,我们将关注与lstm的不同之处:

  • 速度:**一般来说,TCNlstm快,因为它们使用卷积,可以并行完成。在实践中,通过使用大量的扩展,并考虑到剩余连接, TCNs 可能会变得更慢。**
  • 输入长度:**两个TCNlstm都能够接受可变长度的输入。**
  • ****内存:平均而言,TCN 比 LSTMs 需要更多内存,因为每个序列都由多个扩展层处理。同样,这取决于定义每个模型复杂程度的超参数。
  • 性能:**最初的论文显示 TCNs 优于 LSTMs 。然而,在实践中,情况并非总是如此。[13]中更详尽的研究表明,在某些任务中TCN更好,而在其他任务中lstm更有效。**

****换句话说,这里没有明确的赢家。两种模式各有利弊。最好的方法是对两者进行评估,看看哪一个最适合你的情况。

但是请注意,这种方法现在已经过时了。除非你的案例非常琐碎,否则你不会通过应用单一的 TCNLSTM 模型来实现最先进的性能。现代案例考虑了更多的外部参数,这需要一种更具挑战性的方法。这又意味着必须使用不止一个组件/模型。这将在下一节更好地解释。

深度学习和 Kaggle 中的时间序列

到目前为止,我们一直从学术的角度来评估单个模型。然而,如果我们要形成一个更详细的观点,就不能忽视实际方面。

一个很好的评估基础是 Kaggle ,它间接提供了关于数据科学领域状态的经验证据。我们将关注最近的一场 Kaggle 比赛, 呼吸机压力预测 任务是在给定控制输入序列的情况下,预测机械肺内的压力序列。每个训练实例可以被认为是一个自己的时间序列,因此使任务成为一个多时间序列的问题。

本次比赛极具挑战性,原因有三:

  1. 竞争问题可以表述为回归和分类任务。
  2. 数据集为创造性特征工程敞开了大门。
  3. 鉴于每个受试者/数据点由不同的序列表示,利用统计模型是不可行的。

现在,对于这篇文章的主题,关于这个完成有 2 件有趣的事情:

  • 前三名团队以及许多其他团队在其最终解决方案**中至少使用了一个LSTM组件(例如堆叠 lstm双向 lstm)。****
  • 获胜团队提交了一个 多级深度架构 **,其中包括一个 LSTM 网络和一个变压器模块。该架构如图图 7: 所示****

图 7: 第一名解决方案的架构(来源)

当然,这支队伍运用了许多其他的技巧来帮助他们获胜。这里重要的是,非平凡数据集可以从许多不同的方面进行分析,因此需要更复杂的解决方案。由于每个模型都有其独特的优势和劣势,您不能将自己局限于单一模型或单一方法。

卷积神经网络的命运

我希望这篇文章对 LSTMs 的价值做了很好的论证。然而,毫无疑问,《变形金刚》是机器学习领域的一个惊人突破。这种程度的成功必然会导致将来更高的采用水平。

2020 年,变形金刚被改编为计算机视觉,诞生了 【视觉变形金刚(ViT) 。那篇论文引发了进一步的研究,最终这个经过额外修改的新模型能够在许多图像分类任务中胜过 CNN。更好的是,研究人员发现将两种成分结合起来会有更好的效果。此外,我们将来会看到更多的 ViTs。

所以,我希望这一次我们避免诸如“CNN之死”或者“CNN之陨落”之类的豪言壮语的快感。****

结束语

简而言之,本案例研究讨论了以下几点:

  • 要正确评估机器学习领域的突破所带来的影响几乎是不可能的。
  • 变形金刚的出现重塑了景观:lstm,尤其是在 NLP 不再是关注的焦点。**
  • 关于时间序列,lstm 更有用。它们的好处是相当可观的。
  • 数据科学中的现代(且有趣)挑战涉及多个领域,例如音频、文本、图形等。这反过来需要结合各种方法/模式来应对这些挑战。

感谢您的阅读!

参考

  1. Alex 等人,“使用深度卷积神经网络的 ImageNet 分类”(NIPS,2012 年)
  2. Hochreiter 等人,“长短期记忆”(《神经计算》,1997)
  3. Rumelhart 等人通过错误传播学习内部表征(1985 年 9 月)
  4. Kyunghyun 等人关于神经机器翻译的性质:编码器-解码器方法
  5. Sutskever 等人 2014 年,用神经网络进行序列对序列学习 (2014 年)
  6. 吴永辉等谷歌的神经机器翻译系统:弥合人与机器翻译的鸿沟 (2016)
  7. A.瓦斯瓦尼等人关注是你所需要的全部,2017 年 6 月
  8. 赛义德·迈赫兰·卡泽米等人 Time2Vec:学习时间的一种矢量表示 2019 年 7 月
  9. Bryan Lim 等人,用于可解释的多时域时间序列预测的时间融合转换器(国际预测杂志,2021 年 12 月)
  10. D.Salinas 等人, DeepAR:用自回归递归网络进行概率预测,国际预测杂志(2019)。
  11. 安德烈·卡帕西,李菲菲,用于生成图像描述的深度视觉语义对齐
  12. Lea 等 用于动作分割和检测的时态卷积网络 ( CVPR 2017)
  13. 白等, 序列建模通用卷积和递归网络的实证评估 (2018)
  14. Dosovitskiy 等著, 一幅图像抵得上 16x16 个字:变形金刚在尺度上的图像识别 (2020)

视频深度学习(第三部分):深入研究 3D CNNs

原文:https://towardsdatascience.com/deep-learning-on-video-part-three-diving-deeper-into-3d-cnns-cb3c0daa471e

分解的 3D 卷积运算(由作者创建)

这篇文章是我的视频深度学习方法系列的第三篇,是我在 Alegion 作为一名研究科学家的工作的一部分。这一系列博客文章的目标是概述视频深度学习的历史,并为希望涉足该领域的研究人员或从业人员提供相关背景。鉴于视频数据在实际场景中的大规模增长(如物联网视频、自动驾驶汽车和社交媒体应用),分析具有时间结构的视觉数据(如视频)变得越来越重要——简单地从单个图像或帧中提取有用的信息往往是不够的[1]。

在本系列的前两篇文章中,我概述了最初基于深度学习处理视频数据的努力,以及广泛使用的双流网络架构,它彻底改变了视频深度学习的使用。在这篇文章中,我将进一步深入 3D 卷积神经网络(CNN)——基于图像的 2D CNN 在视频领域的直接延伸。这些网络最初未能引起研究界的兴趣,因为与之前概述的双流网络相比,它们的性能很差。然而,随后的研究大大提高了他们的数据效率(即网络需要多少数据才能良好运行)和整体性能,使他们更值得考虑。

这篇文章将从一些初步信息开始,这些信息激发了这篇文章中概述的改进 3D CNNs 的各种想法。在建立了必要的背景之后,我将概述与改进 3D CNNs 性能相关的主要研究进展,包括分解的 3D 卷积、膨胀的 3D 卷积和增加的时间范围。这些想法中的每一个都在缓解一些主要问题方面发挥了关键作用,这些问题阻碍了 3D CNNs 实现最先进的性能,这将在本文中进行更详细的解释。

预赛

正如在的上一篇文章中所概述的,双流网络架构【2】是首批基于 CNN 的视频架构之一,与手工制作的基于视频的学习任务技术相比,其性能得到了持续改善。双流方法在社区中被广泛使用,并在随后几年的几个体系结构变体的开发中被利用[3,4],导致替代体系结构方法的探索较少。因此,双流网络架构主导了几年,但仍然存在需要解决的问题,最终导致了对 3D CNNs 等替代深度学习方法的探索。

什么是 3D 卷积?

3D CNN 是一种深度学习架构,由几个连续的 3D 卷积层组成。正如本系列的初始帖子中所述,3D 卷积通过在空间和时间上对四维数据输入上的四维内核进行卷积来进行操作。数据输入和内核的这四个维度来自两个空间维度、通道维度(例如,RGB 图像具有三个通道)和时间维度(即,视频帧的数量)。有关基本描述,请参见下图。

连续帧体积上的 3D 卷积(由作者创建)

上述卷积运算采用一个2x3x3x3卷积核(即,跨越两个连续帧、三个通道,并且具有空间维度3x3)并且在三个连续 RGB 帧上卷积该核以产生输出表示。这里,应该注意的是,在给定足够的存储容量的情况下,内核的时间维度(在这种情况下为 2)可以增加到跨越任意数量的连续帧。这样,3D 卷积的输出表示本质上是时空的(即,捕捉每个帧内的空间信息和相邻帧之间的时间信息)。

没有 AlexNet 时刻…

随着视频深度学习新方法的开发,研究人员不断将深度学习多年前对图像处理的巨大影响进行类比。也就是说,2012 年开发的用于图像分类的 AlexNet 架构[5]比基于手工制作的特征的方法产生了巨大的改进(即,5–10%的绝对改进)。因此,尽管常见视频架构(如双流架构)的性能很强,但其影响无法与图像处理中深度学习引起的范式转变相提并论[1,6],导致研究人员想知道如何才能在视频领域产生这样的“AlexNet 时刻”。

是什么阻碍了更好的性能?

深度学习在视频领域取得更有限成功的原因通常归因于两个主要问题:

  • 缺乏大规模、带注释的视频数据
  • 基于视频的深度学习架构的高度复杂性

与图像数据相比,密集注释的视频数据更难找到和/或产生。因此,用于人体动作识别(HAR)(即,最常见的基于视频的学习任务之一)的许多数据集在相当长的时间内都很小(例如, HMDB51UCF101 )。

在这些小数据集上训练的模型的性能是有限的,许多研究人员假设,创建更大规模的数据集可以帮助催化基于视频的学习方法的 AlexNet 级别的性能改善[5,6]。因此,随后开发了许多更大规模的基于视频的数据集(例如,动力学活动网络动态场景等)。),从而在未来几年缓解与视频数据集的局限性相关的问题。

除了缺乏大规模数据集,天真地将常见的深度学习模型扩展到视频领域会导致计算和参数复杂性的惊人增加。例如,与基于图像的 CNN 相比,3D CNNs 有更多的参数,因为每个卷积核都增加了额外的时间维度。即,每个卷积运算在计算其输出时同时考虑多个视频帧(与考虑单个帧/图像的 2D 卷积相反),并且卷积参数的数量与所考虑的帧的数量成线性比例;见下文的描述。

3D 卷积参数相对于所考虑的帧数的缩放(由作者创建)

这种增加的时间维度导致 3D CNNs 具有更高的计算成本和更重的数据要求,以实现可接受的训练和泛化性能。因此,3D CNNs 最初优于更简单、低参数的架构,可以用有限的数据学习有意义的表示。

我们怎样才能前进?

“AlexNet 时刻”的缺乏表明,更简单的架构(例如,双流网络)不再足以用于视频深度学习——需要更强大的东西。与这些更简单的架构相比,3D CNNs 具有更高的表示能力——它们包含许多参数,并且能够将时间推理纳入其卷积运算中[1]。因此,如果 3D CNNss 的有限数据和高参数复杂性问题能够得到缓解,3D CNN 有可能表现良好。

收集了更大的基于视频的数据集,从而缓解了与缺乏足够数据相关的性能问题。然而,与图像识别数据集相比,高质量的视频数据仍然有限。此外,视频深度学习社区普遍认为,与图像识别相关的数据稀缺将是一个挥之不去的问题,因为与单个帧或图像相比,视频数据基本上更难注释。因此,为了补充大规模训练数据集的开发,研究人员开始研究更有效的 3D CNN 变体,尽管数据有限,但它们仍能实现更好的性能。

缓解 3D 卷积的问题

研究人员研究了几种以更高效和更合理的方式利用 3D 卷积的方法。两种主要方法包括:

  • 仅在少量网络层中使用 3D 卷积,并允许其余层执行 2D 卷积运算[9,10]。
  • 将 3D 卷积分解成单独的 2D 空间和 1D 时间卷积运算,并按顺序应用[8,9,10]。

虽然仅在部分网络层中使用 3D 卷积是一个简单的概念,但是分解 3D 卷积可能需要更多的解释。考虑具有大小为Fx3x3x3的内核的 3D 卷积,其代表在F个连续帧上的标准大小的内核。因式分解的 3D 卷积背后的主要论点是,这一单个操作可以分成按顺序执行的两个卷积操作:分别应用于每一帧的空间卷积和聚合每一帧的输出特征的时间卷积。在实践中,这种分解卷积运算被实现为大小为1x3x3x3Fx1x1x1的两个 3D 卷积,从而将可训练参数的数量从Fx3x3x3减少到F + 3x3x3;有关示意图,请参见下图。

分解的 3D 卷积的描述(由作者创建)

这里,我们使用空间分辨率2x2来节省空间,但是3x3卷积在实践中是标准的。与全 3D 卷积相比,这种分解方法有几个好处。首先,它大大减少了卷积运算中的参数数量——这是一种对其标准对应物的低阶近似。尽管这意味着所得到的网络具有更少的表示能力(即,它可以学习的变换的数量更有限),但是训练所需的数据更少(即,由于参数的减少),并且计算开销减少。

除了减少可训练参数的数量,这种方法增加了网络内应用的非线性的数量(即,可以在每个卷积分量之后而不是在整个 3D 卷积之后应用元素式非线性),这对整个网络的性能有积极的好处[10]。此外,因子分解卷积的 2D 分量现在可以用预训练的图像分类权重(例如,来自 ImageNet)来初始化,从而使得能够利用更大规模的图像识别数据集来获得视频深度学习的改进性能。

利用 3D CNNs 改善视频理解

通过探索更有效的架构变体,研究人员能够大幅提高 3D CNNs 的性能,并超越其他流行的架构(如双流网络)。这种改进的性能通常是通过利用因式分解的 3D 卷积、仔细选择使用 3D 卷积(与 2D 卷积相反)的层、以及开发利用大型图像识别数据集来改进视频理解的方法来实现的。

在这一部分中,我将首先总结一些基于视频的学习任务的分解式 3D 卷积的早期工作,这些工作是使用较旧、功能较弱的视频深度学习架构执行的。然后,我将概述最近的 3D CNN 架构,这些架构重新利用预训练的 3D CNN 来实现性能的显著提高。最后,我将解释这种高性能的最新架构是如何与高效的 3D CNN 架构相结合的,从而使 3D CNN(当与一些用于改进训练的通用技巧相结合时)能够以更简单的架构超越之前观察到的性能。

因式分解法

分解的时空 CNN。分解 3D 卷积的想法最初是在【5】中探索的。在这项工作中,作者声称 3D CNNs 的成功是有限的,因为 i) 大规模的监督视频数据不可用, ii) 3D 卷积需要大量的训练数据才能很好地执行,因为它们包含许多参数。因此,必须开发一些方法来减少 3D 卷积使用的可训练参数的数量,并能够在有限的数据范围内学习高质量的时空特征。

这种被称为 FstCN 的方法将整个网络体系结构分为独立的空间和时间部分。特别地,网络的初始层仅包含学习单帧表示的 2D 空间卷积。然后,后面的网络层包含 1D 时间卷积,它捕获相邻帧之间的关系。这种方法类似于前面讨论的 3D 卷积的因式分解,但是因式分解操作的空间和时间分量不是以交替的方式应用,而是被分成不同的网络区域(即,首先应用所有空间卷积层,然后应用所有时间卷积层)。

这种方法偏离了前一节所述的基本思想,即 3D 卷积可以用顺序应用的独立空间和时间卷积来近似。下面描述了这两种方法之间的区别,其中[5]中采用的方法被称为“分解式架构”。

3D CNN 的不同因子分解(由作者创建)

有趣的是,[5]中的方法是由将 3D 卷积分解成一系列空间和时间卷积的想法激发的。然而,最终的架构没有遵循这种方法,而是选择在网络的不同区域应用空间和时间卷积。因为作者没有为这一选择提供具体的推理,所以后来的工作研究了更类似于先前讨论的原始因式分解的架构。

伪 3D ResNets。在【8】中探索了将每个 3D 卷积层分解为按顺序应用的单独的空间和时间卷积的想法。与之前的工作类似,作者认为 3D CNN 架构(如 C3D [11])性能不佳,因为卷积运算扩展到多个帧时会产生大量参数。尽管这种 3D 卷积具有很高的表示能力(即,从数据中学习许多不同特征的能力),但是没有足够的训练数据可用于学习有用的表示,并且执行完全 3D 卷积的计算成本很高。

幸运的是,3D 卷积的分解方法可以降低计算复杂度和可训练参数的数量。在[8]中,作者从 3D ResNet 架构[13]开始,用一对 2D 空间(即,与1x3x3x3内核的卷积)和 1D 时间(即,与Fx1x1x1内核的卷积,其中F是所考虑的相邻帧的总数)卷积替换所有 3D 卷积运算。除了顺序应用这些操作之外,作者还试图以并行和混合并行/顺序的方式应用它们;请参见下图,了解所考虑选项的描述。

顺序、并行和混合因子分解 3D 卷积的描述(由作者创建)

在研究了这些变化之后,作者发现最高性能的架构在整个网络中使用这些不同的分解卷积的混合,声称这种变化有助于网络性能。通过采用这种改进的因式分解架构,并利用若干最新进展来改进神经网络训练(例如,批量标准化、残差连接等)。),这个网络能够胜过以前的 3D CNN 变体(例如,C3D [11])和其他最先进的方法(例如,双流网络2)。甚至在更大、更高级的数据集(如 ActivityNet 和 Dynamic Scene)上也观察到了这样的改进。然而,该网络因其复杂性而受到批评,因为使用了各种不同的卷积类型,产生了有点奇特的非同质架构。

重新规划 2D 有线电视新闻网

膨胀的 2D 网络。如前所述,有效利用 3D CNNs 的最大问题之一是缺乏足够的训练数据。在图像识别领域,这种大规模数据集(如 ImageNet)随处可见,使 2D CNN 的性能远远超过了替代方法(如手工制作或基于机器学习的方法)。因此,人们可能开始想知道在这种基于图像的数据集上学习的表示是否可以转移到视频领域。这个问题在[6]中得到了回答,其中开发了双流膨胀 3D CNN 架构(I3D)。

I3D 架构背后的主要思想是从预先训练的图像识别架构开始,并随着时间的推移“膨胀”其参数。实际上,这是通过采用预先训练的大小为3x3x3的 2D 卷积核,并在时间上将其复制F次来创建大小为Fx3x3x3的 3D 卷积核来实现的,该 3D 卷积核考虑了F个相邻帧。然后,该 3D 内核中的权重除以F,以确保卷积输出的预期幅度被保留(即,新内核的输出值不应比之前大F倍)。膨胀 2D 卷积核的想法描述如下。

2D 卷积核被膨胀成 3D 卷积核(由作者创建)

利用这种想法,[6]的作者可以采用高性能的图像识别架构(即,在这种情况下使用 inception v1 架构[12]),膨胀卷积核,并将所得架构应用于基于视频的学习任务。这种方法非常有效,因为它允许在学习有用的视频表示时利用大型图像数据集(即,因为网络参数是用来自图像识别任务的预训练权重初始化的)。因此,缺乏大规模视频数据集变得不那么有害,因为人们可以用现有的大规模图像数据集来补充视频数据。

在提出 I3D 架构后,作者进一步探索了仅膨胀网络中的某些层,发现并非所有网络层都必须是 3D 的——仅通过在需要的地方利用 3D 卷积可以实现计算节省。此外,作者发现,利用双流方法(即,训练在 RGB 和光流输入上训练的两个单独的模型,然后在测试时合并它们的预测)可以提高人类动作识别性能。这一发现在后来的工作中得到巩固,导致双流方法甚至在后来的 3D CNN 架构中仍然被大量使用。发现[6]中的最终方案(即,具有两个单独的光流和 RGB 流的 I3D 架构)表现非常好,远远超过之前常见架构的性能(例如,3D CNN、因式分解的 3D CNN、普通双流架构等)。).

分解膨胀的网络。I3D 建筑因其令人印象深刻的表现而在后期作品中被大量使用。然而,值得注意的是,这种架构使用了全 3D 卷积,如前所述,计算量很大,并且包含许多参数。因此,人们有理由怀疑分解的 3D 卷积是否能提高 I3D 体系结构的性能和效率。这种想法在两部几乎同时出版的独立著作中被同时探讨[9,10]。

在[9]中,作者从 I3D 网络体系结构开始,探索分解其每个 3D 卷积的不同可能性。特别是,作者研究了 i) 哪些网络层应该具有 3D 与 2D 卷积,以及 ii) 这些 3D 卷积应该如何实现。在探索不同选项的过程中,作者发现在网络的早期层中使用分解的 3D 卷积,在后期层中使用 2D 卷积(即“底部重型”架构)会产生最佳性能,从而产生优于 2D 和全 3D 对应物的网络。这样的结果意味着运动建模(即,学习相邻帧之间的关系)是应该在网络的早期层内处理的低/中级操作。

同样,[10]的作者研究了 I3D 架构的可能变化,但是他们得出了完全不同的结果。也就是说,作者发现 3D 卷积仅在后面的网络层中需要。因此,他们提出的架构在早期网络层中使用 2D 卷积,随后在后期层中使用分解的 3D 卷积(即,“头重脚轻”的架构)。发现这种方法产生了更好的速度-精度折衷,因为 3D 卷积仅在特征图已经被下采样的后面的层中被利用。然而,与之前的工作类似,[10]的作者发现,给定这种改进的 I3D 架构,最好的性能仍然可以通过具有用于 RGB 和光流输入的独立网络流的双流方法来实现。

在高层次上,[9,10]中的建议表明,尽管 I3D 架构的性能令人印象深刻,但通过 I)仔细选择使用 3D 卷积的层和 ii)用具有更少参数的因子分解变量替换完整的 3D 卷积,可以实现更好的性能。这些变化进一步提高了 I3D 的性能,产生了一种 3D CNN 架构,其性能超过了当时用于人体动作识别(以及更复杂的定位任务,如人体动作检测)的众多先进方法。

长期三维卷积

除了因数分解和膨胀的 3D 卷积的建议之外,同时进行的工作还研究了 3D 卷积的一个可以用来提高其性能的最终属性:它们的时间范围。简而言之,3D 卷积的时间范围是卷积运算中考虑的帧数。如前所述,3D 卷积的核的大小为Fx3x3x3,其中F是在计算卷积的输出时考虑的时间范围或帧数。

虽然时间范围通常设置为固定值(通常约为 16 帧[2,5]),但[1]的作者广泛研究了 3D CNNs 的不同F设置,发现在 3D 卷积中考虑更多帧(例如,100 帧而不是 16 帧)可以显著提高网络性能。直觉上,这样的概念是有意义的,因为更复杂的基于视频的任务可能依赖于在某些视频中出现的长期时间关系。尽管这种方法会产生更高的计算成本(即,由于更大的 3D 卷积核),但[1]的作者通过简单地降低输入视频的空间分辨率来缓解这种担忧,并发现由此产生的架构优于具有更短时间范围的可比架构。

[1]中的方法由于使用了过时的网络架构,其性能优于更简单的视频架构(例如,双流网络)。然而,在[9]中,作者再次研究了在因子分解 I3D 网络的环境中使用更长的时间范围,表明这种增加的时间范围再次提高了网络的性能。这一发现表明,使用较长的输入片段有助于更好地完成包含长期时间关系的学习任务。作者声称,对这种长期时间关系的正确理解在以前是避免的,因为早期的人类动作识别数据集可以通过从一个或几个相邻帧中提取的特征来解决。

结论

在这篇文章中,我们概述了 3D CNNs 的发展,这些发展使它们成为视频深度学习的可行架构。虽然早期的 3D CNN 变体由于缺乏足够的数据来训练它们的许多参数而表现不佳,但是通过 i) 开发具有更少参数的 3D 卷积的因子分解变体, ii) 仅在必要的层中使用 3D 卷积,以及 iii) 尽可能利用在 2D 图像识别数据集上训练的参数,它们的性能得到了改善。利用所有这些技巧的模型[9,10]明显优于之前的 2D、3D 和双流方法,尤其是当使用较长的剪辑作为输入时。

尽管它们很有效,但这些高效的 3D CNN 架构在学习大量视频数据时,仍然隐式地对称处理时间和空间。不幸的是,时间常常不应该被等同于空间对待——建模运动高度依赖于帧内对象的速度,对象更有可能移动缓慢或根本不移动。这种认识导致了视频深度学习的改进慢播网络的发展[14],这将在下一篇文章中讨论。

非常感谢你阅读这篇文章!希望对你有帮助。如果您有任何反馈或担忧,请随时对帖子发表评论或通过 twitter 联系我。如果你想关注我未来的工作,你可以在媒体上关注我,或者在我的个人网站上查看内容。这一系列的文章是我作为一名研究科学家在 Alegion 完成的背景研究的一部分。如果你喜欢这篇文章,请随时查看该公司和任何相关的空缺职位——我们总是希望与对深度学习相关主题感兴趣的积极个人进行讨论或雇用他们!

参考书目

[1]https://arxiv.org/abs/1604.04494

2https://arxiv.org/abs/1406.2199

2https://arxiv.org/abs/1604.06573

[3]https://arxiv.org/abs/1611.02155

[4]https://papers . nips . cc/paper/2012/hash/c 399862d 3 b 9d 6 b 76 c 8436 e 924 a 68 c 45 b-abstract . html

https://arxiv.org/abs/1510.00562

[6]https://arxiv.org/abs/1705.07750

https://ieeexplore.ieee.org/document/6165309

https://arxiv.org/abs/1711.10305

https://arxiv.org/abs/1711.11248

https://arxiv.org/abs/1712.04851

https://arxiv.org/abs/1412.0767

[12]https://www . cv-foundation . org/open access/content _ cvpr _ 2015/html/Szegedy _ Going _ Deeper _ With _ 2015 _ CVPR _ paper . html

https://arxiv.org/abs/1611.02155

https://arxiv.org/abs/1812.03982

视频深度学习(下):双流架构的兴起

原文:https://towardsdatascience.com/deep-learning-on-video-part-two-the-rise-of-two-stream-architectures-f830d5c655d0

用于视频理解的双流架构(图片由作者提供)

本文是探索视频数据深度学习主题的系列博客文章中的第二篇。这一系列博客文章的目标是概述视频深度学习的历史,并为希望涉足该领域的研究人员或从业人员提供相关背景。在本系列的第一篇文章中,我回顾了关于使用 3D 卷积从视频中提取可学习特征的主题的最早出版物。

在这篇文章中,我将概述视频深度学习的下一个主要阶段:双流网络架构的引入和普及。视频识别的双流架构由两个独立的卷积神经网络(CNN)组成,一个处理空间特征,一个处理时间/运动特征。这些单独的 CNN 通常被称为双流架构中的“空间”和“时间”网络,并且这些单独的网络组件的输出可以被组合在一起以形成时空视频表示。双流架构极大地提高了视频动作识别的性能,使它们在一段时间内成为视频深度学习的标准方法。

本文将首先概述相关的初步信息,例如双流架构的定义/公式以及以前工作的局限性。然后,我将概述关于双流网络架构的文献,包括最初提出该架构的论文以及后来更复杂的变体。在帖子的最后,我将讨论在此期间提出的其他相关的视频理解方法,并概述双流网络的局限性,从而推动即将到来的改进。

预赛

在概述视频深度学习的双流方法之前,必须讨论几个初步概念。关于 2D/3D 卷积的公式、视频数据的结构以及双流架构之前视频深度学习的现有方法的所有细节,我请读者参考该系列的第一篇。然而,鉴于对这些概念的理解,我试图以一种即使只有最少背景知识也能理解的方式概述相关信息。

我们试图解决什么问题?

人体动作识别问题描述(图片由作者提供)

这篇文章中概述的大多数方法都是研究基于视频的人体动作识别 (HAR)的问题。HAR 数据集包含许多可变长度的视频,每个视频都与一个语义标签相关联,对应于视频中正在执行的动作。通常,数据集中的视频集中在执行所讨论的动作的单个实体上,并且视频不会在动作执行之前或之后延伸很远。因此,底层模型的目标是在给定视频作为输入的情况下预测语义动作标签。到目前为止,HAR 是双流网络架构时代最常研究的视频理解问题。

为什么我们需要新的东西?

在本系列的第一篇文章中,我们概述了几种可能的视频深度学习方法。这些方法通常采用 3D CNN 模型(即,由非线性激活层分隔的 3D 卷积的几个连续层),将原始视频或手工制作的视频特征(例如,光流方向梯度)作为这些模型的输入,并基于分配给每个视频的语义标签通过反向传播来执行监督训练。鉴于这些方法的存在,人们可能会开始怀疑为什么需要一种新的视频深度学习方法。

这个问题的答案很简单——现有的模型表现不佳。事实上,早期基于深度学习的 HAR 方法通常优于手工制作的启发式方法,并且与使用单个帧作为输入(即完全忽略视频的时间方面)的深度学习模型相当[1,2]。鉴于深度学习在图像识别领域的巨大成功,如此糟糕的表现令人震惊[3]。因此,研究界一直在思考如何让深度学习对视频更有用。

3D CNN 架构最初的不良性能主要归因于缺乏用于视频理解的大型监督数据集[1]。例如,当时 HAR 最常用的数据集 UCF-101HMDB-51 ,每个数据集分别只包含 13320 和 7000 个带标签的视频剪辑。相比之下,ImageNet——一个广泛使用的图像分类基准——包含大约 130 万个训练样本。虽然为 HAR 提出了更大的数据集(例如,Sports1M [1]),但它们通常是自动收集的,并且相当嘈杂,导致更经常使用较小的精选数据集。因为与它们的 2D 对应物相比,3D CNNs 包含大量的参数,所以它们需要大量的数据来学习有意义的、有区别的表示。因此,当时的小规模数据集是不够的— 需要更多的数据或不同的学习范式来实现更好的性能

双流网络体系结构

双流架构朝着基于深度学习的方法迈出了第一步,超越了 HAR 的启发式和单帧方法的性能,催化了视频理解新时代的到来。简而言之,这种架构通过将运动信息直接编码到网络输入中,实现了高性能视频理解,尽管缺乏足够的监督数据。现在,我们将概述双流架构的基本细节,为本文其余部分概述的相关研究提供背景。

双流网络架构2是由生物学中人类视觉皮层的双流假设[4]激发的,该假设认为大脑具有识别物体和运动的独立路径。为了模仿这种结构,用于视频理解的双流网络架构利用两个独立的网络组件,分别专用于处理空间和运动信息。因此,双流架构将对象识别和运动理解的任务委托给单独的网络组件,形成空间和时间线索的不同路径。

双流架构的输入通常以输入视频中的单个帧为中心,该单个帧作为输入直接传递到网络的空间流中(即,不考虑相邻帧)。作为时间流的输入,选择L个连续帧(以作为空间流输入的帧为中心)。然后为该组中的每个相邻帧计算水平和垂直光流场,形成大小为H x W x 2L的输入(即HW只是原始图像的高度和宽度)。然后,这个光流场堆栈作为固定大小的输入传递到网络的时间流中。

从这里开始,空间和时间流使用具有相似结构的独立卷积网络来处理帧和光流输入——各个网络之间的唯一区别是时间流适于接受具有更大数量的信道(即,2L信道而不是 3 个信道)的输入。一旦计算出每个流的输出,流表示就被融合在一起以形成用于预测的单个时空表示。后来的两个流架构在两个流之间利用更复杂的融合策略,但是现在我们假设这个简单的“后期”融合策略。双流体系结构的公式如下所示。

一个基本的双流网络架构的图示(图片由作者提供)

如上所示,双流架构的输入仅包含空间流的单个帧和时间流的固定大小的光流图组。虽然有人可能会认为这种方法有局限性,因为它只查看视频中固定大小的不完整部分,但可以通过从底层视频中采样几个固定大小的剪辑并对其输出进行平均以产生最终预测来缓解这一问题。此外,用作两个流结构的输入的剪辑可以用步长采样(即,不是采样相邻的帧,而是以两个、三个、四个等的连续间隔采样那些帧)。使得网络考虑基础视频内更大的时间范围。

为什么会这样?

在提供了基本的双流网络架构的公式之后,人们可能会开始想为什么这样的架构会优于 3D CNN 之类的东西。毕竟,3D CNNs 具有非常高的表示能力(即大量参数),因此它们应该能够学习良好的空间和时间特征,对吗?

然而,回想一下,在提出双流架构时,用于视频理解的监督数据量是有限的。因此,双流方法提供了一些主要优势,使其能够超越 3D CNNs 的性能。首先,光流作为输入被直接传递到时间流的事实允许更容易地学习基于运动的特征,因为与运动相关的信息(即,光流)作为输入被直接传递,而不是被学习。此外,因为空间流只是在单个帧上操作的 2D CNN,所以它可以在大型图像分类数据集(例如,ImageNet)上进行预训练,这提供了巨大的性能优势。双流架构和 3D CNNs 之间的这些区别点如下所示。

描述双流和 3D CNN 网络之间的差异(图片由作者提供)

虽然 3D CNNs 将空间和时间表示为等效的维度(即,这与生物学中的双流假设相矛盾)[1],但双流架构能够实现更好的性能,因为 i) 运动信息直接编码在输入中(即,不再需要从数据中学习这一点)并且 ii) 大量的图像分类数据可以用来训练空间网络。在低数据状态下,这种基本的双流架构朝着超越最佳手工制作的启发式视频理解方法的性能迈出了一大步。

双流体系结构的演变

现在已经介绍了双流体系结构和其他初步信息,我将探讨双流体系结构的提议和开发背后的一些文献,以便理解视频。我将从探索这个主题的早期论文开始,然后是后来出现的更高级的架构变体——仍然遵循两个流的方法。

早期方法

语境与视网膜中央凹流[1]。双流架构的概念虽然在后来的一篇论文2中得到更正式的推广,但在[1]中却得到了松散的探索。在本文中,作者为输入数据创建了两个独立的处理流:上下文和视网膜中央凹流。这些单独的流中的每一个都共享相同的网络架构,并以相同数量的帧作为输入。

为了提高计算效率,在作为输入提供给每个流之前,帧被减小到其原始面积的 50%,但是上下文和视网膜中央凹流采用不同的方法来减小输入的大小。也就是说,上下文流中的帧只是被调整大小,而视网膜中央凹流中的帧被居中裁剪。简而言之,这种方法确保了上下文和视网膜中央凹流分别接收低分辨率和高分辨率的输入——一个网络在低分辨率下看到完整的帧,而另一个网络在全分辨率下只看到每个帧的中心。

请注意,与针对双流架构提供的原始描述不同,这种方法没有明确地尝试将运动和空间识别分离到单独的处理流中。相反,每个流被给予相同组的原始帧(与单个帧和光流堆栈相反)作为输入(即,只是不同地调整大小/裁剪),并通过相同但独立的网络架构传递这些帧。然后,这两个流的输出在预测之前被组合。有关描述,请参见下图。

用于视频理解的上下文和视网膜中央凹流(图片由作者提供)

假设两个流都负责检测空间和时间特征,则必须确定如何最好地将时间信息结合到流中。我们不能只在每个流中采用 2D CNN,因为这将永远不会考虑相邻帧之间的关系。为了确定如何最好地融合空间和时间信息,作者测试了每个流的 CNN 架构的几种可能性:

  • 早期融合:将每个流的第一个卷积层改为 3D 卷积。
  • 后期融合:对每个流使用 2D CNN,在相距 15 帧的两个帧上计算它们的输出,然后合并每个流中两个帧的最终输出。
  • 慢速融合:与早期融合相比,将每个流内的所有卷积层改变为具有更小时间范围(即,核大小在时间上更小)的 3D 卷积。

作者发现慢融合一贯表现最好。因此,最终的网络架构采用了两个流的方法(不严格),其中每个流都是一个 3D CNN,将一组帧作为输入。这些流之间的唯一区别是它们的输入——帧在上下文流内被调整大小(即,较低的分辨率)并且在视网膜中央凹流内被居中裁剪(即,较高的分辨率)。虽然这种方法与以前的 3D CNNs 相比是有效的(即,由于减少了输入图像的维度)并且表现相当,但是它仅比 HAR 上的单帧 3D CNNs 表现稍好,并且通常优于手工制作的启发式方法。因此,这种方法必须得到扩展和改进。

最初的双流架构2预备章节中描述的双流架构是在前一章节[1]中描述的架构之后不久提出的。该体系结构首次采用了通过将一帧或一堆光流图分别作为输入传递给空间和时间流,在每个流内分别处理空间和时间特征的方法。因此,双流架构是第一个明确致力于捕捉底层视频中的运动信息的架构。通过采用预备部分中描述的后期融合,空间和时间网络输出可以被组合以形成高度鲁棒的时空特征。

最初提出的双流架构是第一个基于深度学习的方法,在 HAR 基准测试中,与单框架和启发式基线方法相比,性能不断提高。因此,它成为视频理解的标准,在后来的工作中被大量研究、利用和扩展。双流体系结构依赖于手工制作的光流特征作为输入(以及体系结构设计的其他几个先前讨论的方面),在面对有限的数据时能够实现更好的性能,但是这种依赖于手工制作的特征(即光流)作为输入最终被视为体系结构设计的限制。

最佳实践[5]。除了最初提出和探索双流网络架构的主要论文之外,以下工作采用了该架构,并探索了实现最佳性能的最佳实践。特别是,[5]探索了原始双流架构的更深层次的变体,发现在架构的每个流中使用具有更多层的 CNN 主干(例如,VGG [6]和盗梦空间式网络[7])如果训练得当,可以产生显著的性能优势。作者声称,更深的双流架构的性能改善来自于底层网络的表示能力的增加,这对于像 HAR 这样的复杂任务是有益的。

为了产生最佳可能的性能,分别使用图像分类和光流数据(即,从图像识别数据集生成的)对空间流和时间流进行预训练(即,与仅预训练空间流相反)。然后,该模型以低学习率和高水平的数据扩充和正则化来训练,产生的最终性能超过了先前在 HAR 上实现的双流架构。

高级变体

在双流架构的最初提议之后,几个后续工作提出了该架构的变体(稍加修改),这些变体产生了大规模改进的性能。这些更高级的变体保持了相同的一般网络架构和输入模式,但是在网络中添加了补充模块或连接,以改进时间信息的表示。这些修改的动机是原始双流网络严重依赖于空间信息,不能很好地表示时间数据。

改进双流架构的融合[8]。最初对双流架构的批评声称,现有的公式没有正确地合成空间和时间信息。也就是说,因为时间和空间特征仅在两个流的输出层融合,所以该模型没有学会正确地利用时间信息,并且主要依赖于空间信息来生成正确的分类。此外,两个流架构的时间“尺度”是有限的,因为它仅将固定大小的帧子集视为时间流的输入(即,与完整视频相对)。

为了改善双流架构中时间信息的融合,作者[8]探索了双流架构中空间和时间流的特征表示之间的多种融合方法。正如之前的工作[5]所推荐的,更深的 VGG 网络被采用作为每个流的主干。然后,作者考虑以下融合类型:和、最大、连接、卷积(即,连接特征图,然后与一组 1×1 滤波器卷积),以及双线性(即,计算每个像素处的特征的矩阵外积和像素位置上的和,以输出单个向量)。在双流网络内的不同层测试了这些融合类型中的每一种之后,作者发现在 VGG 网络的最后一个卷积层之后(即 ReLU 之前)采用卷积式融合结合时间池操作产生了最佳性能。

除了在双流网络中开发更好的融合方法,作者还提出了一种采样方法,允许底层网络考虑整个视频中的帧。即,在整个视频中对几个不同的“剪辑”进行采样,每个剪辑具有不同的时间跨度;见下图。通过在视频中的许多不同位置对剪辑进行采样,并利用小步幅和大步幅,最终的方法能够考虑基础视频中的大量帧,尽管双流架构的输入是固定大小的。当这种采样方法与以前提出的架构改进相结合时,作者能够在 HAR 基准中设置一个新的最先进的性能。

从具有不同时间跨度的底层视频中进行剪辑采样(图片由作者提供)

剩余双流架构【9】。在提出改进的融合技术后不久,双流架构被调整为在其每个流中使用 ResNet 风格的 CNN 架构。这种修改是由用于图像识别的 CNN 架构的 ResNet 系列取得的惊人成功[10]推动的,该系列架构至今仍被广泛使用。然而,除了 ResNet 架构的流行之外,还出现了许多利用 CNN 架构实现图像识别最佳性能的最佳实践(例如,批量标准化[11]、最大化感受野、避免信息瓶颈等)。)还没有在视频深度学习中使用。因此,[9]的作者试图将包括 ResNet 架构在内的众多改进引入双流网络。

[9]中用于空间和时间流的 ResNet 体系结构从其原始公式略有修改。也就是说,在空间和时间流之间添加了补充的残差连接【T2 I】(即,作者声称该连接有助于空间和时间信息的融合)以及在时间流中正在处理的相邻帧之间添加了补充的残差连接【T4 ii】(即,这是通过转换网络流内的大量卷积层以利用 3D 卷积来实现的)。作为这些补充剩余连接的结果,所得到的架构具有大的时空感受域(即,在空间和时间上都考虑了视频的全部范围)。

有趣的是,两个网络流的参数都是使用来自 ImageNet 的预训练权重来初始化的。然后,以这样的方式初始化 3D 卷积,该方式使用与中心帧的原始对应 2D 卷积相同的权重,但是在 3D 卷积的感受域内形成与每个相邻帧的时间上的剩余连接(即,仅仅是相同的操作)。这种残差、两个流的架构(即,包括流之间和贯穿时间的补充残差连接)被示为学习和提取更好地表示空间概念随时间的演变的特征,从而进一步改进了用于 HAR 的先前方法的性能。

人们还尝试了什么?

尽管双流架构是视频深度学习的一个非常受欢迎的选择,但在此期间,并不是所有关于视频理解的研究都利用了这种方法。事实上,许多其他有趣的算法是与双流架构并行开发的,尽管使用了完全不同的方法,但它们能够在 HAR(或其他视频理解基准)上实现令人印象深刻的性能。

新的 3D CNN 变体【12】。另一个流行的架构是 C3D 网络[12],它采用完全由 3D 卷积(即每层中的3x3x3个内核)组成的卷积架构。特别是,该网络将一组固定长度的帧作为输入,并通过一系列卷积层和汇集层,然后是网络末端的两个全连接层。为了训练这个网络,作者使用 HAR 的大规模 Sports1M 数据集[1],从而使网络能够在大数据集上学习区分特征(即,回想一下,由于缺乏足够的监督数据,以前在 3D CNNs 上的尝试表现不佳)。尽管如此,这种架构的表现优于更先进的双流变体,并因仅考虑视频内有限的时间窗口而受到批评。结果,C3D 没有双流建筑变体受欢迎。

连体网络【13】。在不同的方面,视频识别的并行工作探索了使用暹罗网络来模拟视频中的动作[13]。更具体地说,这项工作声称视频中的任何动作都可以由该动作给环境带来的变化来定义。受这一想法的启发,作者开发了一种方法,将底层视频分为“前置条件”和“效果”状态,分别代表动作发生前后的视频部分。然后,这些帧组通过单独的 CNN 架构,以提取每个帧的特征表示。从这里,动作被建模为线性变换(即,矩阵乘法),其将前置条件特征变换为效果特征(即,这可以用预测和实际效果特征向量之间的余弦距离来测量)。有趣的是,这整个暹罗网络架构(包括动作转换)可以使用期望最大化过程来训练,以在 HAR 基准上实现有竞争力的性能。

其他材料… 一些关于视频理解的工作研究了更有效的 3D 卷积表示,发现可以通过将 3D 卷积分解为单独的 2D 空间和 1D 时间卷积来学习鲁棒的时空关系,这些卷积按顺序应用[14]。与相应的 3D CNN 架构相比,所得到的架构包含明显更少的参数,因此可以在有限的数据范围内实现更好的性能。此外,并行工作超出了 HAR 问题领域,并考虑了动作检测问题[15],其中动作必须在底层视频中被识别/分类和定位。通过采用利用早期双流架构变体的区域提议和特征提取方法[1],可以在动作检测基准上实现令人印象深刻的性能。

结论和未来方向…

虽然同时探索了许多视频理解的方法,但是双流方法的令人印象深刻的性能导致了该技术的普及。尽管如此,双流架构仍然——在其核心——依赖于从底层视频中提取的手工制作的特征。特别是,它依赖于从底层视频中提取的光流图,并作为输入传递给时间流。尽管这些特征对基础视频做出了最小的假设(即,仅仅是平滑度和连续性假设),但是这种对手工制作的光流特征的依赖将受到后续工作的批评,从而导致更复杂的架构变体的开发,这将在本系列的下一篇文章中讨论。

非常感谢你阅读这篇文章!希望对你有帮助。如果你有任何反馈或担忧,请随时评论帖子或通过 twitter 联系我。如果你想关注我未来的工作,你可以在媒体上关注我或者查看我的个人网站上的内容。这一系列的文章是我在 Alegion 做研究科学家时背景研究的一部分。如果你喜欢这篇文章,请随时查看该公司和任何相关的空缺职位——我们总是希望与对深度学习相关主题感兴趣的积极个人进行讨论或雇用他们!

参考书目

[1]https://static . Google user content . com/media/research . Google . com/en//pubs/archive/42455 . pdf

2 https://arxiv.org/abs/1406.2199

[3] https://arxiv.org/abs/1803.01164

[4] https://pubmed.ncbi.nlm.nih.gov/1374953/

[5] https://arxiv.org/abs/1507.02159

[6] https://arxiv.org/abs/1409.1556

[7] https://arxiv.org/abs/1409.4842

[8] https://arxiv.org/abs/1604.06573

[9] https://arxiv.org/abs/1611.02155

[10] https://arxiv.org/abs/1512.03385

[11] https://arxiv.org/abs/1502.03167

[12] https://arxiv.org/abs/1412.0767

[13] https://arxiv.org/abs/1512.00795

[14] https://arxiv.org/abs/1510.00562

[15] https://arxiv.org/abs/1411.6031

深度学习,强化学习,最优控制:知道区别

原文:https://towardsdatascience.com/deep-learning-reinforcement-learning-optimal-control-what-a-mess-507dff27a603

在计算智能领域有很多热门关键词。你可能分别知道深度学习、强化学习或最优控制,它们是如何工作的,或者你甚至可能知道如何使用这些工具。但有时很难全面了解它们之间的关系。

马丁·鲍登在 Unsplash 上拍摄的照片

你可能也不熟悉最优控制。如果是这样的话,我建议你在这里看一些题目的介绍。无论如何,最优控制通常被认为属于控制理论领域,而不是所谓的人工智能领域。

控制理论和人工智能有很多相似之处。强化学习的概念实际上是在人工智能和控制理论的旗帜下出现的,只是符号不同。

让我们深入研究一下这件事,看看共同点和不同点。在整篇文章中,DL 将作为深度学习的简称,RL 作为强化学习的简称。

预测工具

我们可以很快提醒在哪些应用中可以使用这三种工具:

  • 深度学习用于根据现有数据进行预测。深度学习的主要应用之一是图像处理。例如特征识别,给定一组猫和狗的图片,算法应该预测先前未见过的图片是否属于狗或猫类别。

来源:https://arxiv.org/pdf/1604.07043.pdf

作者图片

  • 虽然深度学习可以回答一个特定的问题,是猫还是狗,但强化学习和最优控制有助于以行动的形式做出连续的选择。如果我们使用深度学习来预测 2 个连续的图片,这些预测是独立的。在 RL/最优控制中,一个选择会影响下一个选择。例如,如果我们试图确定股票市场的最佳月度投资,当前的投资可能会扰乱市场,并改变我们进行下一个选择的方式。从这个意义上说,我们做出的选择是在对国家采取行动,因此我们正在采取行动。

作者图片

总的来说,我们可以区分状态(或声明)和控制(或动作)的概念。
——深度学习让我们可以对最有可能的说法做出预测,是猫还是狗。

  • RL 和最优控制试图预测最佳行动、要进行的投资、已知的状态,这里是股票市场的当前状态。

这都是最小化问题

照片由乌萨马·阿扎姆Unsplash 上拍摄

所有这些工具都有另一个重要的共同点:总体目标是最小化某个值,(或最大化它),有时被称为成本函数,下面用 J 表示。

  • 对于深度学习,最小化的成本是预测函数(神经网络)预测的结果与样本状态的实际值之间的差异。对于数据集的每个样本,该误差被累加。深度学习方法的训练过程在于找到预测函数,以使其最小化总预测误差。

深度学习问题的近似表述。

  • 对于强化学习和最优控制,成本可以是用户设计的任何东西。火箭使用的燃料,机器人到达期望点所需的时间……与深度学习的区别在于,这里的成本也取决于所采取的行动。因为这是一个连续的行动过程,我们需要把它们加起来计算最终成本。

在这两种情况下,我们将通过采用公式的离散和无限时间版本来简化。

  • 对于强化学习,我们试图最小化任何潜在状态(someState)的总成本,形成一个策略。这意味着,无论火箭目前的状态如何,我们总是知道该采取的最佳行动(相对于成本)。

强化学习问题的近似表述。

  • 在最优控制框架中,我们只想在给定一些初始条件的情况下最小化成本,这将为我们提供一系列要采取的最佳行动。然而,只要初始条件改变,我们就可以重复这个过程。这是模型预测控制(MPC)的基础。

最优控制问题的近似公式,这里 f 代表系统的动态方程。

不同的方法来解决最低成本

我们看到,每个问题都必须找到总成本函数 j 的最优最小值。然而,对于每个问题,用于降低成本的技术各不相同:

  • 对于深度学习,使用神经网络。神经网络对用户友好的主要原因是它们的高度非线性特性,但是能够非常容易地知道这个复杂函数的梯度(通过反向传递)。因为我们有梯度,梯度下降可以用于向最优成本 J*移动。
  • 至于强化学习,也是一个迭代的过程。然而,任务更加复杂。我们必须为系统的每个状态计算 J*。为此,选择策略的初始猜测。通过遵循当前近似策略运行连续实验来改进策略。
  • 在控制理论中,由于没有使用数据,最重要的部分在于选择正确的工具以最小的成本求解。最常见的是,将使用优化工具,但是如果能够提供例如动态梯度,则梯度下降也是一个选项。

无模型与基于模型

目前,强化学习看起来更像最优控制,而不是深度学习!令人惊讶的是,RL 和 DL 应该都属于人工智能领域。

RL 和最优控制都处理动态系统,即状态随时间变化的系统。系统的状态根据一些确定的(但可能是未知的)规律而变化。比如说经济学,是由一系列复杂的规律(通货膨胀、通货紧缩等)驱动的。)是我们试图近似的。一个机械系统在万有引力定律下运转。

为了理解这种无模型与基于模型的方法,请考虑从手中放下一个物体。如果你知道万有引力定律,你就能够用牛顿力学来预测物体在空中下落的运动。如果你对重力一无所知,但重复同样的经历足够多的次数,你就能从统计学上预测物体从不同位置落下的运动。

最优控制将试图利用我们所拥有的关于动力系统如何进化的知识,并将它以约束的形式包含在我们试图解决的问题中。

另一方面,强化学习不对模型做任何假设。从收集的数据中反复计算出最佳策略。这里的关键方面是 RL 不是试图提出系统的模型,而是直接针对成本函数的特定选择的策略。

现在,每种方法的优点/缺点是什么?

  • 动态系统的模型可以是任意复杂的。通常,在最优控制框架中使用的模型需要被线性化,以便找到足够快的解决方案。如果我们还包括不确定性或扰动,用于计算的模型很可能离现实太远。对于难以建模的系统,绕过模型直接使用数据可能是有利的。但当模型足够可靠时,它总是会击败数据,因为数据可能很难收集。
  • 有了动态系统的模型,我们可以很容易地在不同的成本之间切换。假设你正在使用基于模型的方法进行投资,你可以在不同的目标之间切换,在更快的投资回报或更长期的利益之间交替。对于无模型方法,如果您对成本函数进行任何修改,您将需要再次重新计算整个策略。
  • 关于不确定性问题,无模型方法可以与随机结果分离,并且比固定模型方法更好地处理随机性。
  • 然而,使用模型,即使是简化的,也总能提供最佳解决方案的基线,并具有一定程度的一致性。否则求解者会抱怨问题不可行,可以使用一些更安全的方法(停车,停止投资)。使用无模型方法,如果收集的数据没有探索该区域,则该政策对于一组州来说可能是糟糕的。
  • 最后,在处理真实系统时,我们必须考虑约束。它们可以转化为行动约束(例如火箭推力有限)或状态约束(例如火箭不能离太阳太近)。虽然无模型方法可以处理动作约束,但是对于状态约束却很难。

神经网络融合

艾莉娜·格鲁布尼亚克在 Unsplash 上的照片

我们现在能够更好地理解为什么强化学习被归为人工智能类别。与深度学习一样,它依赖于数据(而不是模型)。

然而,它不止于此。总的来说,强化学习试图制定一项政策。但是政策只不过是将状态映射到要采取的最佳行动的函数。由于这个函数可能是高度非线性的,我们可以用神经网络来逼近它。并且由于 RL 使用数据,因此可以在 RL 框架中策略迭代的同时训练该神经网络。这叫做深度强化学习。关于这个话题的更多实用信息,请看这篇文章
现在我们更好地理解了为什么强化学习属于人工智能社区。实际上,无模型方法与神经网络的结合将强化学习推向了另一个层次。

然而,人们可以在最优控制框架内很好地使用神经网络。由于最优控制是基于模型的,模型本身也可以用神经网络近似,如[1]中所做的。在这种情况下,在使用常规的最优控制技术之前,还需要收集数据和训练网络。

最后,还可以将基于模型的方法的可靠性与系统的扰动和随机性的数据驱动估计相结合。例如,文献2提供了一种基于 MPC 框架的机器人手臂控制器,并结合了噪声的高斯近似。

结论

  • 神经网络:一个高度非线性的函数,具有容易计算的梯度。
  • 深度学习:使用神经网络来发表声明。
  • 强化学习:构建给定状态(策略)下的最佳行动图。地图通过收集数据按顺序更新,不依赖于任何模型。
  • 深度强化学习:用神经网络近似策略。
  • 给定一些初始条件和系统如何随时间演变的模型,最优控制提供了采取行动的最佳顺序。

虽然深度学习显然是在人工智能方面,而最优控制显然是在控制理论方面,但强化学习是这两个领域的交叉点。它试图使用人工智能工具解决控制问题。
我们还看到,最终,所有这些人都试图预测最佳结果。虽然无模型和基于模型的方法都有自己的优势,但我们不能否认,利用数据可以达到手工无法达到的复杂程度。同时,系统的模型提供了一个提供可靠性的基础事实。

利用数据和模型将是一条出路,但这需要打破控制和人工智能领域之间的区别。

参考资料:

[1]:神经网络连续时间系统辨识:模型结构和拟合准则, Marco Forgione,Dario Piga

深度学习回顾文献

原文:https://towardsdatascience.com/deep-learning-reviews-the-literature-3abd90027c90

用变形器对复杂文本进行分类的指南

Jaren R Haber (乔治城大学) Thomas Lu (加州大学伯克利分校) Nancy Xu (加州大学伯克利分校)

Unsplash 上由 Jaredd Craig 拍照

对于研究人员和科学家来说,有太多的东西需要阅读:更多的博客文章、期刊文章、书籍、技术手册、评论等等。等。比以往任何时候都多。然而,我们必须阅读,因为从过去学习和避免重复发明轮子——或者更糟的是,爆胎——的主要方法是通过历史悠久的文献综述。当我们的书桌和书架上堆满了知识,提供了太多的内容让一个人消化不了的时候,我们如何才能跟上邻居的步伐——也就是说,学术的最新发展及其随着时间的推移而发生的许多变化?

数字时代最受欢迎的消遣来了:计算机阅读东西,或者自然语言处理(NLP)。我们提供 NLP 工具和示例代码来帮助解析数量呈指数级增长的学术期刊文章——尤其是交叉于学术领域的跨学科研究。作为一个案例研究,我们开发了阅读和跟踪一个特定的跨学科领域的方法:社会学和管理学学科中的组织理论。我们使用深度学习来帮助大规模阅读这些文献,我们称之为计算文献综述的管道。

在 JSTOR 提供的约 70,000 篇期刊文章的语料库中,我们具体追踪了现代社会学和管理学研究中的三大视角——人口统计学、关系学和文化学。我们的模型通过利用一套强大的 NLP 工具:预训练的 Transformer 模型来确定在学术文本中使用这些视角中的哪些(如果有的话)。变形金刚可以处理大量文本,使用注意力层和神经网络在高层次上“理解”其内容,并生成有用的输出,如预测。它们是用于训练机器学习分类器的最先进、细致入微的工具,我们构建这些工具是为了帮助审阅学术文献。

读完这篇博客后,你应该能够:

  • 了解我们在数字文本语料库中跟踪想法的工作流程,从预处理到模型训练
  • 比较几种深度学习算法在获取组织理论方面的性能
  • 想象一下随着时间的推移,学术文本中的观点是如何转变的
  • 找到(并在此基础上构建!)我们的计算文献综述代码

工作流程

我们团队中的专家组织学者手工编写了大约 700 篇文章,为每个观点的出现提供了基础事实。我们使用这些带标签的训练数据,在来自 JSTOR 的预处理过的全文数据上训练基于 Transformer 的分类器。这些模型为组织理论中的三个观点输出二元预测,我们根据交叉验证的准确性选择了最好的一个。然后,我们对未标记的数据进行预测,并分析社会学和管理学学科中每个角度的趋势。

我们的目标是对学术文章进行分类,这些文章平均长度为 7640 个单词。然而,传统的基于变压器的模型通常无法处理长序列: BERT 依赖于自我关注,这与序列长度成二次比例,并且在处理更长的序列时产生难以维持的计算需求。这个限制在这里很重要,因为我们的分析单位通常是长篇文章。此外,BERT 预先接受了 ebook scorpus(800 万字)和英语维基百科(2500 万字)的培训,这可能会限制其在科学文献领域的可移植性。出于这些原因,我们将我们的关注点缩小到两种变形的 Transformer 模型,以及用于比较的 BERT 本身:SciBERT 和 Longformer。 SciBERT 基于 BERT,但在与我们的语料库更相似的学术期刊文章上进行训练:具体来说,它的语料库是语义学者的 114 万篇论文(18%来自计算机科学,82%来自生物医学领域)。

我们的计算文献综述工作流程。作者图片。

相比之下, Longformer 是一个基于 Transformer 的模型,在与 BERT 相同的原始数据集上训练,但它最多可以接受 16K 个令牌(与 BERT 和 SciBERT 的 512 个相反)。Longformer 通过不执行所有可能的比较,而只执行与相关单词的比较,最大限度地减少了注意力的计算负担。Longformer 提供了许多策略来做到这一点(见下图),例如让大多数令牌只关注它们的邻居,而只有少数令牌关注每个令牌(并且每个令牌也关注它们)。

变形金刚的高效注意力模式。资料来源:I. Beltagy,M. Peters,A. Cohan,Long former:The Long-Document Transformer(2020),arXiv

这种更长的序列长度允许我们的深度学习分类器捕捉组织观点,这些观点不仅嵌入在学术文章的摘要或介绍中,还嵌入在文本的主体中(如理论部分)。我们用最大令牌长度 1024(出于资源管理的原因)训练 Longformer 模型,远低于 16K 的限制。即便如此(剧透警告),在我们的案例中,这种更长的序列长度被证明是 Longformer 的一个关键优势,与 BERT 相比,准确性提高了 2%左右。

我们工作流程的代码

预处理

因为像 BERT 这样的转换器利用句子的结构,并根据单词周围的单词(相当于每个单词的上下文唯一向量,这比像 word2vec 这样的单词嵌入更有优势)为单词分配意义,所以停用词和语法结构对于模型训练很重要。因此,我们对文本进行最低限度的预处理,用 Apache Lucene tokenizer 对文本进行标记,只删除 HTML 标记、页面格式标记和其他各种垃圾。以下简单的正则表达式替换清理了 JSTOR 用 OCR 对文章文本进行数字化后留下的页面格式和 XML 标签。我们完整的文本预处理代码可以在这里找到。

def remove_tags(article):
    """
    Cleans page formatting and XML tags from JSTOR articles.
    """ article = re.sub(‘<plain_text> <page sequence=”1">’, ‘’, article)
    article = re.sub(r’</page>(\<.*?\>)’, ‘ \n ‘, article)
    # remove xml tags
    article = re.sub(r’<.*?>’, ‘’, article)
    article = re.sub(r’<body.*\n\s*.*\s*.*>’, ‘’, article) return article

过采样

因为我们的二元分类器只预测两个类,所以我们的训练数据中的最佳类平衡将是 1:1: 50%真实正例(本文中引用了视角),50%真实负例(未引用视角)。然而,我们手工标注的数据中,阴性病例是阳性病例的两倍。为了解决训练和测试样本中的这种类不平衡,我们通过从少数类(正例)进行引导来进行过采样,以达到 1:1 的类比率。

from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler

def oversample_shuffle(X, y, random_state=52):
    """
    Oversamples X and y for equal class proportions.
    """

    fakeX = np.arange(len(X), dtype=int).reshape((-1, 1))
    ros = RandomOverSampler(random_state=random_state, sampling_strategy=1.0)
    fakeX, y = ros.fit_resample(fakeX, y)
    p = np.random.permutation(len(fakeX))
    fakeX, y = fakeX[p], y[p]
    X = X.iloc[fakeX.reshape((-1, ))] return X.to_numpy(), y.to_numpy()

列车测试分离

模型训练需要单独的训练集和测试集来提供准确的预测。为了创建用于训练的数据集,我们执行训练-测试分割,然后对两个数据集进行过采样,以解决前面提到的类别不平衡问题。

def create_datasets(texts, scores, test_size=0.2, random_state=42):
    """
    Splits the texts (X variables) and the scores (y variables) 
    then oversamples from the minority class to achieve a balanced sample.
    """ 

    X_train1, X_test1, y_train1, y_test1 = train_test_split(
        texts, scores, test_size=test_size, 
        random_state=random_state) X_train1, y_train1 = oversample_shuffle(
        X_train1, y_train1, random_state=random_state)
    X_test1, y_test1 = oversample_shuffle(
        X_test1, y_test1, random_state=random_state)

    return X_train1, y_train1, X_test1, y_test1

模特培训

我们在 HuggingFace Transformers 库中的 Longformer 模型的基础上构建我们的分类器,并使用一个线性层将 Longformer 表示转换为每个标签的一组预测。下面是 BERTClassifier 类,它包含用于加载和初始化具有指定最大令牌长度的预训练 long former 模型的代码,以及用于模型训练和交叉验证的“get_batches()”、“forward()”和“evaluate()”等帮助函数。这是模特训练的完整代码。

class BERTClassifier(nn.Module):
    """ 
    Initializes the max length of Longformer, the pretrained model, and its tokenizer.
    """ def __init__(self, params):
        super().__init__()
        self.tokenizer = LongformerTokenizer.from_pretrained('allenai/longformer-base-4096')
        self.bert = LongformerModel.from_pretrained("allenai/longformer-base-4096", gradient_checkpointing=True)
        self.fc = nn.Linear(768, self.num_labels)

然后,我们在“BERTClassifier”类中定义“get_batches()”辅助函数,该函数使用 BERT 标记器标记文本。

 def get_batches(self, all_x, all_y, batch_size=10):
        """
        Tokenizes text using a BERT tokenizer. 
        Limits the maximum number of WordPiece tokens to 1096.
        """ batch_x = self.tokenizer.batch_encode_plus(
            x, max_length=self.max_length, truncation=True, 
            padding='max_length', return_tensors="pt") batch_y=all_y[i:i+batch_size] ## code omitted return batches_x, batches_y

为了运行该模型,我们在“BERTClassifier”中定义了一个“forward()”函数。这将获取模型输出,并通过完全连接的线性图层运行最后一个图层,以通过执行所有输出类别的 softmax 来生成预测,这在 logit 模型中很常见。

 def forward(self, batch_x):
        """
        Gets outputs from the pretrained model and generates predictions.
        Note that Longformer is a RoBERTa model, so we don't need to pass in input type ids.
        """        # First, get the outputs of Longformer from the pretrained model
        bert_output = self.bert(input_ids=batch_x["input_ids"],
                        attention_mask=batch_x["attention_mask"],
                        output_hidden_states=True)

        # Next, we represent an entire document by its [CLS] embedding (at position 0) and use the *last* layer output (layer -1).
            bert_hidden_states = bert_output['hidden_states']
            out = bert_hidden_states[-1][:,0,:]
            if self.dropout:
                out = self.dropout(out)
            out = self.fc(out) return out.squeeze()

最后,我们为“BERTClassifier”定义一个“evaluate()”函数,以基于批次 y 确定批次 x 的预测的准确性

 def evaluate(self, batch_x, batch_y):
        """
        Evaluates the model during training by getting predictions (batch x) and comparing to true labels (batch y).
        """ self.eval()
        corr = 0.
        total = 0. # disable gradient calculation
        with torch.no_grad():
            for x, y in zip(batch_x, batch_y):
        # for each batch of x and y, get the predictions
        # and compare with the true y labels
                y_preds = self.forward(x)
                for idx, y_pred in enumerate(y_preds):
                    prediction=torch.argmax(y_pred)
                    if prediction == y[idx]:
                        corr += 1.
                    total+=1    

       return corr/total

然后,我们训练模型并确定最佳超参数。我们使用了微软 Azure 的机器学习服务器来搜索超参数空间,并最大化交叉验证的准确性;下面是完成这项工作的完整代码。此代码中的主要部分是下面的“train_bert()”函数,它使用“create_datasets()”来准备训练和测试数据,然后从“BERTClassifier”类中“get_batches()”来获取用于模型构建的批。接下来,我们定义超参数变量、Adam 优化器及其调度程序。然后,我们训练和评估该模型,记录它的最佳开发精度和时期,用于模型比较。

def train_bert(texts, scores, params, test_size=0.2):
    """
    This deep learning workhorse function trains a BERT model 
    with the given hyperparameters, 
    returning the best accuracy out of all epochs.
    (Code is simplified for ease of reading.) Inputs:
        texts: a list of article texts for training
        scores: a list of labels for the texts (0 or 1)
        test_size: proportion of input data to use for model testing (rather than training)
        params: a dict of parameters with optional keys like batch_size, learning_rate, adam_beta, weight_decay, max_memory_size, and num_epochs (full list omitted).
    """ # cross entropy loss is used for binary classification
    cross_entropy=nn.CrossEntropyLoss()
    best_dev_acc = 0.
    best_dev_epoch = 0. for epoch in tqdm(range(num_epochs)): # loop over epochs
        bert_model.train()
        # Train model
        optimizer.zero_grad() # Clear any previously accumulated gradients
        for i, (x, y) in enumerate(zip(batch_x, batch_y)):
            # Find the loss for the next training batch
            y_pred = bert_model.forward(x)
            loss = cross_entropy(y_pred.view(-1, bert_model.num_labels), y.view(-1))
            loss.backward() # Accumulate gradients for back-propagation if (i + 1) % (batch_size // max_memory_size) == 0:
                # Check if enough gradient accumulation steps occurred. If so, perform a step of gradient descent
                optimizer.step()
                scheduler.step()
                optimizer.zero_grad()

       # Evaluate model and record best accuracy and epoch 
        dev_accuracy=bert_model.evaluate(dev_batch_x, dev_batch_y)
        if epoch % 1 == 0:
            if dev_accuracy > best_dev_acc:
                best_dev_acc = dev_accuracy
                best_dev_epoch = epoch

    print("\nBest Performing Model achieves dev accuracy of : %.3f" % (best_dev_acc))
    print("\nBest Performing Model achieves dev epoch of : %.3f" % (best_dev_epoch)) return best_dev_acc

模型评估

我们用“cross_validate()”函数将上述所有内容联系在一起,该函数接受一个预处理文本列表、一个分类分数列表(如果文章是关于给定的视角,则为 1,否则为 0)和一个要优化的超参数列表。然后,该函数训练该模型并返回交叉验证精度,或者跨数据的几个(这里是 5 个)切片或折叠中的每一个的模型的平均精度。与单一训练/测试分割相比,k-折叠交叉验证提供了一种更通用的模型性能测量方法,最大限度地降低了模型过度拟合输入数据的风险。

def cross_validate(texts, scores, params, folds=5, seed=42):
    """
    Trains BERT model and returns cross-validation accuracy.
    Communicates directly with Azure to run model 
    and record accuracy of given hyperparameters.
    """

    np.random.seed(seed)
    states = np.random.randint(0, 100000, size=folds)
    accs = []
    for state in states:
        print(state)
        accs.append(train_bert(
            texts, scores, params, test_size=round(1/folds, 2),
            random_state=state, save=False))
    acc = np.mean(accs)
    run.log('accuracy', acc) # works with Azure return acc

下面是我们代码的一个用法示例,使用“orgs”Pandas 数据框架作为训练数据。每行是一篇社会学文章,列“全文”包含文章的全文,而“组织分数”表示文章是关于组织的(1)还是关于组织的(0)。

orgs['text_no_tags'] = [remove_tags(art) for art in orgs['full_text']]
orgs = orgs[orgs['orgs_score']!=0.5]

cross_validate(orgs['text_no_tags'], orgs['orgs_score'],{'batch_size': 16, 'lr': 1e-4, 'dropout': 0.2, 'weight_decay':0.005, 'num_warmup_steps': 60})

然后可以使用 Azure 的专业机器学习仪表板来可视化结果。下图显示了我们训练的模型的超参数组合(批量大小、学习率、预训练学习率、重量衰减、热身步骤数和辍学率)。最精确的模型(紫色线)对除辍学率之外的所有超参数都有相对较低的值。

跨超参数的模型性能。图片由作者提供,使用 Microsoft Azure 平台制作。

与 BERT 和 SciBERT 相比,我们发现基于 Longformer 的优化二元分类器在所有三个角度和组织社会学方面表现最佳。下表比较了我们测试的不同变压器型号的精度。最佳模型超参数见附录 A,Longformer 性能替代指标见附录 B。

做出并可视化预测

由于其卓越的性能,我们使用 Longformer 为 65K 左右尚未标记的 JSTOR 文章生成二元和连续预测(是/否以及参与每个视角的概率)。你可以在这里找到生成预测的完整代码。

**def** get_preds(model, batch_x, batch_y):
    """
    Using input model, outputs predictions (1,0) and 
    scores (continuous variable ranging from 0 to 1) for batch_x
    Note that batch_y contains dummy labels to take advantage of pre-existing batching code
    """ model**.**eval()
    predictions **=** []
    scores **=** [] **with** torch**.**no_grad():
        **for** x, y **in** tqdm(zip(batch_x, batch_y)):
            y_preds **=** model**.**forward(x)
            **for** idx, y_pred **in** enumerate(y_preds):
                predictions**.**append(torch**.**argmax(y_pred)**.**item())
                sm **=** F**.**softmax(y_pred, dim**=**0)
            **try**:
                scores**.**append(sm[1]**.**item())
            **except**:
                print(len(scores))
                print(sm**.**shape, sm) **return** predictions, scores

我们将 JSTOR 语料库分为两个主要主题(社会学和管理&组织行为),并从三个角度(人口统计、文化和关系)分析参与度。我们过滤掉了预测概率低于 0.7 的关于组织的社会学文章。

*# filter to only organizational sociology articles using threshold of 0.7* minority_threshold_orgs **=** 0.7
df_orgsoc **=** merged[merged['primary_subject']**==**'Sociology'][merged['org_score'] **>** minority_threshold_orgs]

接下来,我们创建一个辅助函数“cal_yearly_prop()”来计算涉及每个视角的文章的年度比例。这有助于随着时间的推移,可视化参与每个视角的文章比例。

def cal_yearly_prop(df_category):
    """
    For graphing purposes, calculate the proportion of articles 
    engaging a perspective (cultural, relational, or demographic) 
    for a given discipline (sociology or management studies) over years. Arg:
        df_category: DataFrame of article predictions with columns ‘publicationYear’ indicating year published and ‘article_name’ as identifier Returns:
        A list of proportion of articles engaging in a perspective over the years
    """

    yearly_grouped = df_category.groupby('publicationYear').count()
    yearly_total = list(yearly_grouped[‘article_name’])
    years = list(yearly_grouped.index) # get the yearly counts of articles 
    counts = df_category.groupby('publicationYear').count()[['article_name']].reset_index()
    not_present = list(set(years) - set(counts.publicationYear)) # create a dataframe for the article counts of missing years (0)
    df2 = pd.DataFrame.from_dict({'publicationYear':not_present, 'article_name': [0]*len(not_present)})

    # concatenate the counts df with df of missing years, and divide the yearly counts by yearly total to get a list of yearly proportions
    return np.array(pd.concat([counts, df2]).reset_index().sort_values('publicationYear')['article_name'])/yearly_total

下面的堆积条形图显示了一段时间内社会学和管理学文章比例的变化。这一指标的分子是在给定年份从事文化社会学、关系社会学或人口社会学的文章数量;分母是当年社会学的文章总数。管理研究中可视化趋势的代码非常相似。你可以在这里找到可视化预测的完整代码。

# Visualize engagement across perspectives in organizational sociology
relt_fig = plt.figure()
relt_fig.set_figwidth(10)
relt_fig.set_figheight(8)

yearly_grouped = df_orgsoc.groupby('publicationYear').count()
years = list(yearly_grouped.index)

ax = plt.gca()# the start and end years are omitted because they are outliers
demog_fig = plt.bar(years[1:-1], demog_soc_minority_prop_os[1:-1], color = 'yellowgreen')
cult_fig = plt.bar(years[1:-1], cult_soc_minority_prop_os[1:-1], bottom = demog_soc_minority_prop_os[1:-1], color = 'gold')
relt_fig = plt.bar(years[1:-1], relt_soc_minority_prop_os[1:-1], bottom =  demog_soc_minority_prop_os[1:-1] + cult_soc_minority_prop_os[1:-1], color = 'lightblue')

plt.legend([ 'demog_soc','cult_soc', 'relt_soc' ])

plt.title('Changes in Proportion of Articles in Organizational Sociology from 1971 - 2015')

作者图片。

作者图片。

简而言之,我们看到人口统计学的观点在(组织)社会学的文章中是最常见的,而关系学的观点在管理和组织行为学中是最常见的。人口统计管理类文章从 2010 年开始逐渐下降,文化管理类文章在观察时间段内逐渐增长。所有其他类别多年来有所波动,但没有显示出随着时间的推移而普遍增长或下降。

结论

我们希望您喜欢了解我们的深度学习工作流,该工作流用于跟踪复杂文本中随时间变化的想法,并应用于我们对学术文章中的组织理论的案例研究。回顾:我们准备了一个完整的出版文献语料库,使用几个 transformer 模型来分析和预测文章内容,然后绘制跨时间和学科的特定理论的参与度。我们的总体目标是通过使用数据科学工具阅读和解释大量文本来提高文献综述的可访问性。我们认为变形金刚非常适合这种应用,激发我们的技术洞察力,将变形金刚应用于各种社会科学学科的学术文章——这是我们以前从未见过的练习。

我们希望您发现我们的演示很有趣,并对您自己的工作有所帮助。请关注我们未来探索其他采样方法、手工编码模型验证和更长序列长度的工作(关于这方面的具体想法,请参见附录 C)。我们还计划分析引用模式和组织理论中不断变化的词汇代表观点。敬请期待,感谢阅读!

附录

A.最佳模型超参数

作为增加输入序列长度的另一种方法,我们还利用 BERT 探索了“分块”技术:我们将文本分成更小的部分,评估每个块上的模型,并组合结果(引用)。我们发现使用 BERT 进行分块和使用其默认的 512 个标记的序列长度之间没有显著差异。

B.备选 Longformer 指标

C.未来的工作

手动编码的模型验证

为了验证我们的模型,我们使用似然阈值来选择六组样本(跨两个学科的三个视角),包括少数和多数类以及那些具有低置信度预测的类(用于可能的主动学习应用)。我们的专家合作者对这些样本进行手工编码将有助于验证模型,确保过去大量假阳性的经验不会重复。

分层抽样

在数据预处理过程中,除了对少数类进行过采样之外,使用分层采样可能是有益的。总的来说,从 1970 年到 2005 年,吸引我们观点的 JSTOR 文章总数稳步增加,在 2005 年达到顶峰,从 2005 年到 2016 年开始急剧下降。除了观点的潜在变化之外,上述文章频率和比例的变化可能是由于某些期刊改变了发表文章的数量。为了确保样本中代表了某些观点,JSTOR 文章的总体可以根据出版年份和期刊等特征划分为子总体。然而,这也取决于 JSTOR 提供的文章数量。

长模型输入长度

由于时间约束和计算限制,我们用最大令牌长度= 1024 来训练长成形器。然而,继续用更大的标记长度训练长成形器并观察准确性的任何变化将是有见地的。截取一个长文本的中间部分(参考)也是值得探索的。此外,在预处理过程中,我们还可以删除摘要或元数据信息,如开头的作者姓名和电子邮件。

深度学习直观解释

原文:https://towardsdatascience.com/deep-learning-visually-explained-a9fff874d280

使用视觉示例理解深度学习

朱利安·特朗瑟在 Unsplash 拍摄的照片

深度学习是最强大的人工智能技术之一,然而,它可能很难理解。在这篇博客中,我将尝试使用视觉和例子来解释深度学习。

深度学习架构的灵感来自于我们大脑的工作方式。这是神经元的连接。深度学习模型可以有很多参数。参数的数量基于层和神经元的数量,对于复杂的体系结构,层和神经元的数量可以呈指数增长。

在这篇博客中,我将举一个金融欺诈检测的业务用例。欺诈检测的最大挑战之一是类别不平衡的问题,这意味着用于训练机器学习模型的数据很少有欺诈案例。

深度学习架构(图片由作者提供)

这就像训练一个机器学习模型大海捞针。欺诈检测是一个特殊的问题,这证明了采用深度学习架构等复杂方法的合理性。

数据

在这个例子中,我将从银行交易系统中获取数据。数据如下所示。该数据具有金融交易的类型、金额以及来源和目的地旧余额和新余额。还有一个标志表明交易是否是欺诈性的。

数据集的引用可以在博客的末尾找到。

欺诈检测数据(图片由作者提供)

数据分为训练数据和测试数据。深度学习模型是在训练集上开发的,然后在测试数据上进行验证。那么这个模型可以用来预测对看不见的数据的欺诈。

训练/测试分割(图片由作者提供)

深度学习模型

这里显示了欺诈预测的深度学习模型。输入神经元对应于事务数据。每个神经元对应于输入数据中的一列,例如交易类型、金额以及起点和终点的余额信息。

有一个中间层,然后是最后一层,它有两个神经元,一个预测非欺诈,另一个预测非欺诈。

这些线是在不同层之间传递的信号。绿线表示正信号,红线表示负信号

欺诈检测的深度学习模型(图片由作者提供)

观察神经元内部

我们看到神经元 1_0 正在向神经元欺诈传递一个积极的信号。

这意味着它已经深刻了解了欺诈性交易的样子!这太令人兴奋了!

神经元 1_0 正在向神经元 2_1 传递积极信号(欺诈)(图片由作者提供)

让我们来窥探一下神经元 1_0 的内部吧!

《神经元内部 1_0》(图片由作者提供)

雷达图代表了神经元对数据的了解。蓝线表示高值,红线表示低值。雷达图显示在原点有一个很高但几乎相似的新旧平衡。但是,在目的地新旧余额有非常大的差别。

这种情况表明存在欺诈。这种情况可以形象的展示在下面。

直观显示欺诈交易的样子(图片由作者提供)

使用混淆矩阵的模型精度

这里显示的是使用混淆矩阵的深度学习模型的准确性。

困惑矩阵(图片由作者提供)

总的来说,大约有 95000 笔交易,其中有 62 笔欺诈交易,这远远少于总交易。然而,深度学习模型做得很好,因为它能够正确地将 52 识别为欺诈,这也被称为真阳性(tp)

有 1 个误报(fp),意味着它不是欺诈,但模型错误地将其标记为欺诈。所以精度,也就是 tp / (tp +fp),等于 98%。

此外,还有 10 个假阴性(fn),这意味着它们是欺诈性交易,但我们的模型无法预测它们。因此,测量召回率为 TP/(TP+fn)83%

结论

深度学习架构非常强大,因为它有助于解决欺诈检测等复杂问题。分析深度学习架构的可视化方法有助于理解该架构以及它如何解决问题

用于欺诈检测的合成金融数据集的数据源引用

用于欺诈检测的合成财务数据集可在此处获得:https://www . ka ggle . com/competitions/space ship-titanic/overview

如许可证部分所述,它拥有许可证 CC BY-SA 4.0

  • 分享 —以任何媒体或格式复制和再分发材料
  • 改编 —对材料进行重新混合、转换和构建,用于任何目的,甚至是商业目的。

用我的推荐链接加入 Medium

https://pranay-dave9.medium.com/membership

订阅每当我发布一个新的故事时,请保持通知。

https://pranay-dave9.medium.com/subscribe

额外资源

网站(全球资讯网的主机站)

你可以访问我的网站进行零编码分析。https://experiencedatascience.com

在网站上,您还可以参加即将举行的人工智能研讨会,体验有趣和创新的数据科学和人工智能。

Youtube 频道

这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接

深度学习 vs 线性回归

原文:https://towardsdatascience.com/deep-learning-vs-linear-regression-ea74aca115ea

通产省Unsplash 上拍摄的照片

机器学习问题中关于 MLP 和线性回归的理论差异和实例。

让我们从一个简单的考虑开始:我们都喜欢神经网络

我们喜欢它们,因为它们是直观的,大多数时候它们不需要领域知识,它们易于编码并且非常简单易懂。即使现在它们如此时髦和常用,它们也有相当长的历史了。

如今,由于硬件的改进,神经网络比以前快得多,也更容易开发。但是我们真的需要它们吗?

在这本笔记本中,我们将使用一种深度学习算法(多层感知器),并将它与最简单、最直接的机器学习方法进行比较,即线性回归。在这篇文章的最后,我们将会更清楚什么时候我们真的需要深度学习,什么时候我们可以只使用一个非常简单的算法并侥幸成功。

让我们从有趣的部分开始:

0.图书馆

在我们的例子中,我们将使用 Python 和一些非常著名的库(numpy,pandas,sklearn,…)。在开始复制粘贴代码之前,请全部导入。

对于可视化部分,我使用了 plotly ,因为它允许对我们正在做的事情进行 3d 可视化,并且是交互式的。对于机器学习部分来说,它真的不是必需的,所以你可以安全地避免安装它,跳过结果的可视化

1.第一个例子

在第一个例子中,我编造了一些二次相关数据。我为什么这么做?说明线性回归也可用于模拟多项式函数!但是我们会到达那里的。

让我们构建这个数据集:

因为可以看到目标是以下函数:

t = c_0 +c_1*x+c_2*y+c_3*x*y

我知道你在想什么:这个模型不完全是线性的。但是再想想:x 和 y 基本上是同一个变量,所以可以认为是这样的东西:

t = c_0 +(c_1+c_2)*x+c_3*x*x

所以我们还是好的。😃

让我们绘制表面:

1.1 线性回归

现在让我们定义我们的预测函数:

我们编造了我们的数据,所以我们确切地知道参数,我们可以测试预测功能是否有效:

我们可以看到,它的工作非常完美!我们继续吧。
线性回归任务中使用的损失 函数为均方误差,其表达式如下:

这是我们的损失函数梯度,我们希望将其设置为 0:

对于我们的“完美”解决方案,损失函数和梯度正确地为 0:

线性回归任务的最优解如下:

实现如下:

这个方法非常有效!我说的完美是指我们唯一的不确定性是计算机在计算矩阵的逆矩阵和矩阵乘法时可能产生的数值误差,如果与机器学习算法的误差相比,这个误差低得离谱。

注意:我们使用了整个数据集,但在这种情况下我们真的需要 4 个数据点,我们会得到同样精确的结果。

1.2 深度学习

在深度学习算法中,我们正在做一些不同的事情。我们有数据,我们有多个处理输入数据的隐藏层,以及一个提供预测的输出节点。我们定义了一个介于 0 和 1 之间的得分函数,我们希望将它最大化。在我们的例子中,我们使用 R2 :

让我们将此方法应用于我们正在考虑的数据集:

  1. 让我们进行列车试运行:

2.我们来看看比分:

3.我们来策划一下吧:

得分 99.2%。这当然真的很好,但是与我们在之前的方法中看到的确切结果相比太糟糕了!

1.3 考虑因素

如果你有一个线性或多项式问题,使用线性回归模型很容易解决。你不仅不需要神经网络,而且如果你使用神经网络而不是线性回归,你实际上会有更低的性能。

2.第二个例子

让我们通过添加一个具有随机幅度的正弦函数来复杂化我们之前的情况:

现在我们有:

t = c_0 +c_1*x+c_2*y+c_3*x*y + Rsin(x)

其中 R 是-5 和 5 之间的随机振幅。

2.1 线性回归:

如果你使用线性回归模型,你会得到和以前一样的结果。这意味着线性模型尽了最大努力,但损失仍然很可怕:(

2.2 深度学习:

如果你使用深度学习,结果真的很低:

同样,R2=99.20%,与前一种方法的巨大损失相比,这是很大的损失。

2.3 考虑因素

在这种情况下,模型不是多项式,而是受到随机正弦函数的干扰,我们得到的结果是线性模型工作很糟糕,而深度学习模型工作得很好。

3.计算时间

在处理高维数据时,你真的想使用机器学习,甚至是回归问题。事实上,对一台计算机来说,对一个庞大的矩阵求逆是一件大事。

让我们对一个不同维数的随机矩阵求逆:

让我们画出你做这个反转需要的时间:

在深度学习环境中,你可以尝试使用一些卷积网络,找到一个数值解,这可能与解析解略有距离,但需要的时间更少。

4.结论

结论永远是这样:先看看你的数据。
如果你能注意到有一些“线性”或“多项式”的行为,不用担心深度学习。只需使用一些线性回归模型,你就会以非常低的(如果不是空的)误差完成你的工作。

如果行为很奇怪,很难被认为是线性或多项式的,如果你有足够的数据,使用深度学习可能是有意义的。

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。

深度时间序列预测与流量预测第 1 部分:鳄梨价格

原文:https://towardsdatascience.com/deep-time-series-forecasting-with-flow-forecast-part-1-avocado-prices-276a59eb454f

利用深度学习进行时间序列框架流量预测的实例预测时态数据

图片来自 Unsplash

流量预测【FF】,是 PyTorch 内置的最先进的时间序列预测框架深度学习。在这个正在进行的系列中,我们将使用 FF 对真实世界的时间序列数据集进行预测(和分类)。在第一个例子中,我们将使用 FF 对位于 Kaggle(开放数据库)上的公开可用的 Avocado 数据集进行预测。预测农产品价格对消费者和生产者来说都是有价值的,因为它可以帮助确定最佳购买时间或预期收入。

数据集:该数据集包含 2015-2020 年美国每周鳄梨销售的信息。数据分散在不同的大都市地区,如芝加哥、底特律、密尔沃基等。对于数据集,总共大约有九列。所有事情都认为这是一个相对“简单”的数据集,因为在列中没有太多缺失值。

鳄梨数据集的示例。

我们现在将尝试根据 total_volume 和其他几个列(如 4046)来预测鳄梨的平均价格(例如,销售的某些包的数量)。

值得注意的是,在现实中,对于长期预测问题,我们通过模型进行一次以上的预测(例如,我们将来自目标模型的预测连接到其他要素,并将其重新输入到模型中),我们可能需要将 total _ volume 4046 等内容也视为目标,因为我们无法提前几个时间步访问它们的真实值。然而,为了简化本教程,我们将假设我们有(这些值也可以来自其他单独的估计或其他模型)。

方法 1: 我们将尝试的第一种方法是 DA-RNN,这是一种更古老但仍然有效的时间序列预测模型深度学习方法。为此,我们将首先设计一个包含模型参数的配置文件:

the_config = {                 
   "model_name": "DARNN",
   "model_type": "PyTorch",
    "model_params": {
      "n_time_series":6,
      "hidden_size_encoder":128,
      "decoder_hidden_size":128,
      "out_feats":1,
      "forecast_history":5, 
      "gru_lstm": False
    },
    "dataset_params":
    { "class": "default",
       "training_path": "chicago_df.csv",
       "validation_path": "chicago_df.csv",
       "test_path": "chicago_df.csv",
       "forecast_length": 1,
       "batch_size":4,
       "forecast_history":4,
       "train_end": int(len(chicago_df)*.7),
       "valid_start":int(len(chicago_df)*.7),
       "valid_end": int(len(chicago_df)*.9),
       "test_start": int(len(chicago_df)*.9),
       "target_col": ["average_price"],
       "sort_column": "date",
        "no_scale": True,
       "relevant_cols": ["average_price"j., "total_volume", "4046", "4225", "4770"],
       "scaler": "StandardScaler", 
       "interpolate": False,
       "feature_param":
         {
             "datetime_params":{
                 "month":"numerical"
             }
         }
    },

    "training_params":
    {
       "criterion":"DilateLoss",
       "optimizer": "Adam",
       "optim_params":
       {"lr": 0.001},
       "epochs": 4,
       "batch_size":4
    },
    "inference_params":{
        "datetime_start": "2020-11-01",
        "hours_to_forecast": 5,
        "test_csv_path":"chicago_df.csv",
        "decoder_params":{
            "decoder_function": "simple_decode", 
            "unsqueeze_dim": 1
        } 
    },
    "GCS": False,

    "wandb": {
       "name": "avocado_training",
       "tags": ["DA-RNN", "avocado_forecast","forecasting"],
       "project": "avocado_flow_forecast"
    },
   "forward_params":{},
   "metrics":["DilateLoss", "MSE", "L1"]
}

在这种情况下,我们将使用 DilateLoss 函数。DilateLoss 函数是一个损失函数,根据 2020 年提出的时间序列的值和形状返回一个误差。这是一个很好的训练功能,但不幸的是,它并不适用于每个模型。我们还将在配置文件中添加月份作为一个特性。

现在,我们将使用训练函数为几个时期训练模型:

from flood_forecast.trainer import train_function
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
secret_value_0 = user_secrets.get_secret("WANDB_KEY")
os.environ["WANDB_API_KEY"] = secret_value_0
trained_model = train_function("PyTorch", the_config)

现在让我们分析一些关于权重和偏差的结果:

我们可以看到这个模型似乎收敛得很好。基于 validation_loss,我们甚至可以挤出一些更多的性能训练用于下一个或两个时期。

预测在这里用红色显示,实际的平均价格用蓝色显示。

此外,预测并不可怕,特别是考虑到我们没有广泛地调整参数。然而,另一方面,我们可以看到模型在预测价格时并没有真正使用总量(至少根据 SHAP 的说法)。

你可以在这个教程这里和 W & B 日志这里看到完整的代码。

方法 2: 我们将使用 GRU 的概率版本来预测美国不同地区鳄梨的价格。

概率 m 的优势在于它可以预测预测值的上限和下限。我们再次定义一个配置文件:

the_config = {                 
   "model_name": "VanillaGRU",
   "model_type": "PyTorch",
    "model_params": {
      "n_time_series":6,
      "hidden_dim":32,
      "probabilistic": True, 
      "num_layers":1,
      "forecast_length": 2,
      "n_target":2,
      "dropout":0.15, 
    },
    "probabilistic": True,
    "dataset_params":
    { "class": "default",
       "training_path": "chicago_df.csv",
       "validation_path": "chicago_df.csv",
       "test_path": "chicago_df.csv",
       "forecast_length": 2,
       "forecast_history":5,
       "train_end": int(len(chicago_df)*.7),
       "valid_start":int(len(chicago_df)*.7),
       "valid_end": int(len(chicago_df)*.9),
       "test_start": int(len(chicago_df)*.9),
       "target_col": ["average_price"],
       "sort_column": "date",
        "no_scale": True,
       "relevant_cols": ["average_price", "total_volume", "4046", "4225", "4770"],
       "scaler": "StandardScaler", 
       "interpolate": False,
       "feature_param":
         {
             "datetime_params":{
                 "month":"numerical"
             }
         }
    },"training_params":
    {
       "criterion":"NegativeLogLikelihood",
       "optimizer": "Adam",
       "optim_params":
       {"lr": 0.001},
       "epochs": 5,
       "batch_size":4
    },
    "inference_params":{
        "probabilistic": True,
        "datetime_start": "2020-11-01",
        "hours_to_forecast": 5,
        "test_csv_path":"chicago_df.csv",
                 "decoder_params":{
         "decoder_function": "simple_decode", "unsqueeze_dim": 1, "probabilistic": True}
    },
    "GCS": False,

    "wandb": {
       "name": "avocado_training",
       "tags": ["GRU_PROB", "avocado_forecast","forecasting"],
       "project": "avocado_flow_forecast"
    },
   "forward_params":{},
   "metrics":["NegativeLogLikelihood"]
}

这里我们将使用负概率损失作为损失函数。这是概率模型的特殊损失函数。现在,像前面的模型一样,我们可以检查权重和偏差的结果。

训练似乎也进行得很顺利。

这里的模型有一个上限和下限以及一个预测的平均值。我们可以看到,该模型在预测平均值方面相当不错,但在上限和下限方面仍然相当不确定(甚至有一个负的下限)。

你可以在本教程笔记本中看到完整代码。

方法 3: 我们现在可以尝试使用一个神经网络同时预测几个地理区域。为此,我们将使用一个简单的变压器模型。像最后两个模型一样,我们定义一个配置文件:

the_config = {                 
    "model_name": "CustomTransformerDecoder",
    "model_type": "PyTorch",
    "model_params": {
      "n_time_series":11,
      "seq_length":5,
      "dropout": 0.1,
      "output_seq_length": 2, 
      "n_layers_encoder": 2,
      "output_dim":2,
      "final_act":"Swish"
     },
     "n_targets":2,
    "dataset_params":
    {  "class": "default",
       "training_path": "multi_city.csv",
       "validation_path": "multi_city.csv",
       "test_path": "multi_city.csv",
       "sort_column": "date",
       "batch_size":10,
       "forecast_history":5,
       "forecast_length":2,
       "train_end": int(len(merged_df)*.7),
       "valid_start":int(len(merged_df)*.7),
       "valid_end": int(len(merged_df)*.9),
       "test_start": int(len(merged_df)*.9),
       "test_end": int(len(merged_df)),
       "target_col": ["average_price_ch", "average_price_dt"],
       "relevant_cols": ["average_price_ch", "average_price_dt", "total_volume_ch", "4046_ch", "4225_ch", "4770_ch", "total_volume_dt", "4046_dt", "4225_dt", "4770_dt"],
       "scaler": "MinMaxScaler",
       "no_scale": True,
       "scaler_params":{
         "feature_range":[0, 2]
       },
       "interpolate": False,
       "feature_param":
         {
             "datetime_params":{
                 "month":"numerical"
             }
         }
    },
    "training_params":
    {
       "criterion":"MSE",
       "optimizer": "Adam",
       "optim_params":
       {
        "lr": 0.001,
       },
       "epochs": 5,
       "batch_size":5

    },
    "GCS": False,

    "wandb": {
       "name": "avocado_training",
       "tags": ["multi_trans", "avocado_forecast","forecasting"],
       "project": "avocado_flow_forecast"
    },
    "forward_params":{},
   "metrics":["MSE"],
   "inference_params":
   {     
         "datetime_start":"2020-11-08",
          "num_prediction_samples": 20,
          "hours_to_forecast":5, 
          "test_csv_path":"multi_city.csv",
          "decoder_params":{
            "decoder_function": "simple_decode", 
            "unsqueeze_dim": 1},
   }

}

对于这个模型,我们将回到使用 MSE 作为损失函数。我们现在可以分析来自 W&B 的结果。

该模型似乎收敛得很好(尽管它可能没有足够的数据,因为变压器需要大量数据)。

绿色阴影区域是置信区间。

然而,芝加哥模型看起来有点偏离,通过一些额外的超参数调整,它可能会表现良好(特别是更多的辍学)。

完整代码

权重和偏差日志

结论

在这里,我们看到了三个不同模型在五周内预测鳄梨价格的结果。FF 使得训练许多不同类型的模型来进行预测以及查看哪种模型表现最好变得很容易。本系列的第二部分将回顾杂货销售预测。

PyTorch 中 Animals-10 数据集上的深度迁移学习

原文:https://towardsdatascience.com/deep-transfer-learning-on-the-animals-10-dataset-in-pytorch-53c84b33ad39

使用 Resnet-18 深度学习 CNN 实现 97%验证准确率的迁移学习教程

Animals-10 数据集 |塞犍陀·维韦克上可视化深度迁移学习(使用 Resnet-18)标签

应用于图像预测任务的深度卷积神经网络非常受欢迎。似乎每隔几个月就有一个新的算法挑战 CNN 架构的极限。就在几年前,这些最先进的算法以及构建这些算法背后的知识只有少数专家可以获得。然而,由于越来越容易获得和低成本的计算资源,以及世界上任何地方的任何人都可以运行的开放代码,这种情况正在发生变化。有一些了不起的人愿意为了更大的利益分享他们的知识和专长。

在本教程中,我将使用脸书人工智能研究实验室开发的流行深度学习框架 PyTorch,通过一个这样的最先进的算法 Resnet-18 来预测 10 个类别的动物。我将展示迁移学习如何实现非常高的准确率(这里的 97%,基于测试数据)。

我将在本教程中完成以下步骤:

导入和格式化加载和预处理数据 模型训练和可视化 可视化结果

导入和格式化

import os
import numpy as np
import torch
from torch import nn
from torch import optim 
from torch.autograd import Variable
import torch.utils.data as data
import torchvision
from torchvision import models
import pandas as pd
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils
from torchvision.datasets import ImageFolder

res_18_model = models.resnet18(pretrained=True)

注意,最后我从 PyTorch 导入了 resnet18 模型,并选择 pretrained=True。这是因为对于迁移学习,我们通常使用深度学习算法在其他数据上学习的(一些或大部分)权重。然后,我们修改几层,以适用于我们的情况。

T = transforms.Compose([
     transforms.Resize((224,224)),
     transforms.ToTensor(),
     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

我们需要从 kaggle 下载图片并解压。接下来,我们使用 PyTorch 内置转换框架将数据集转换成标准化格式。这允许我们将图像调整为某种标准格式(在本例中为 224x224),并进行其他预处理。对于迁移学习模型,重要的是标准化输入数据,以匹配原始模型的训练内容(尽可能)。否则,您可能会面临糟糕的性能风险(根据经验:)。对于 Resnet-18,PyTorch 官方文档建议进行转换。规格化([0.485,0.456,0.406],[0.229,0.224,0.225])。由 3 个元素组成的第一个阵列对应于 3 个 RGB 通道的平均值。第二个数组也由 3 个元素组成,对应于相同 3 个通道的标准偏差。

加载和预处理数据

#download the dataset from here: https://www.kaggle.com/alessiocorrado99/animals10 

dataset = ImageFolder('./archive/raw-img/', transform=T)
train_set, val_set = torch.utils.data.random_split(dataset, [int(len(dataset)*.8), len(dataset)-int(len(dataset)*.8)])
train_loader = torch.utils.data.DataLoader(train_set, batch_size=64)
test_loader = torch.utils.data.DataLoader(val_set, batch_size=64)

接下来,我们从各自的图像文件夹中加载数据,应用转换,并随机分成 80%的训练和 20%的测试。现在我们准备训练(差不多!)但是有一个问题……如果你看 Resnet-18 架构,它看起来相当复杂——这只是最初的几层。总共有 18 层做各种事情(卷积,batchnorm,relu,maxpool 等。).

前几个 Resnet-18 层|塞犍陀·维韦克

然而,如果你看最后一层,它有 1000 个特征。这是因为 Resnet-18 模型最初被训练为对 1000 个类进行预测。这与我们有 10 个类的动物数据集不匹配。

最后的 Resnet-18 fc 层|塞犍陀·维韦克

所以我们至少需要改变这最后一层。结果非常简单,如下所示:

res_18_model.fc= nn.Linear(512, 10)

好了,现在我们可以开始训练了!

Resnet-18 将最后一层修改为具有 10 个对应于 10 个类别的特征|塞犍陀·维韦克

模型训练和评估

model=res_18_modelif(torch.cuda.is_available()==True):
    model=res_18_model.cuda()

optimiser=optim.SGD(model.parameters(),lr=1e-2)
loss=nn.CrossEntropyLoss()

我有一个 NVIDIA GPU,所以我把它用于相应的培训。这里的两个重要变量是优化器和损失函数。对于优化器,我使用 PyTorch 的随机梯度下降函数。我在这里使用交叉熵,因为它是多类预测的常用损失函数。

# My training and validation loops
nb_epochs = 5
acc_tot=np.zeros(nb_epochs)
for epoch in range(nb_epochs):
    losses = list()
    accuracies = list()
    model.train()     
    for batch in train_loader: 

        x,y = batch
        if(torch.cuda.is_available()==True):
            x=x.cuda()
            y=y.cuda()        

        # 1 forward
        l = model(x) # l: logits

        #2 compute the objective function
        J = loss(l,y)

        # 3 cleaning the gradients
        model.zero_grad()
        # optimiser.zero_grad()
        # params.grad.zero_()

        # 4 accumulate the partial derivatives of J wrt params
        J.backward()

        # 5 step in the opposite direction of the gradient
        optimiser.step()

        losses.append(J.item())
        accuracies.append(y.eq(l.detach().argmax(dim=1)).float().mean())

    print(f'Epoch {epoch + 1}', end=', ')
    print(f'training loss: {torch.tensor(losses).mean():.2f}', end=', ')
    print(f'training accuracy: {torch.tensor(accuracies).mean():.2f}')

    losses = list()
    accuracies = list() 
    model.eval()
    for batch in test_loader: 
        x,y = batch
        if(torch.cuda.is_available()==True):
            x=x.cuda()
            y=y.cuda()

        with torch.no_grad(): 
            l = model(x)

        #2 compute the objective function
        J = loss(l,y)

        losses.append(J.item())
        accuracies.append(y.eq(l.detach().argmax(dim=1)).float().mean())

    print(f'Epoch {epoch + 1}',end=', ')
    print(f'validation loss: {torch.tensor(losses).mean():.2f}', end=', ')
    print(f'validation accuracy: {torch.tensor(accuracies).mean():.2f}')
    acc_tot[epoch]=torch.tensor(accuracies).mean().numpy()

损失和准确性指标|塞犍陀·维维克

形象化

太好了!现在,我们如何将我们的神奇模型可视化呢?由于这是一个图像数据集,可视化的结果特别强大。首先,我定义了一个函数,用于在初始规范化后将图像转换为正确的格式。

def imformat(inp, title=None):
    """Imshow for Tensor."""
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    return(inp)

数据附带了一个字典——我在下面使用它。原来这个数据集的标签不是英文的。为了便于理解标签,我们来转换一下。

class_names = dataset.classes
translate = {"cane": "dog", "cavallo": "horse", "elefante": "elephant", "farfalla": "butterfly", "gallina": "chicken", "gatto": "cat", "mucca": "cow", "pecora": "sheep", "scoiattolo": "squirrel", "dog": "cane", "cavallo": "horse", "elephant" : "elefante", "butterfly": "farfalla", "chicken": "gallina", "cat": "gatto", "cow": "mucca", "spider": "ragno", "squirrel": "scoiattolo"}
t_inv = {v: k for k, v in translate.items()}

最后,让我们把结果可视化!

train_loader2 = torch.utils.data.DataLoader(train_set, batch_size=9)

plt.figure(figsize=(15, 13))

inputs, classes = next(iter(train_loader2))
preds=model(inputs.cuda()).argmax(dim=1)

for i in range(0,9):
    ax = plt.subplot(3, 3, i + 1)
    img=imformat(inputs[i])

    plt.imshow((img))

    try:
        plt.title('True:'+str(t_inv[class_names[classes[i]]])+'    Pred:'+str(t_inv[class_names[preds[i]]]))
    except:
        plt.title('True:'+str(translate[class_names[classes[i]]])+'    Pred:'+str(translate[class_names[preds[i]]]))
    if(i==9):
        plt.axis("off")

深度迁移学习(使用 Resnet-18)标签在 Animals-10 数据集 |塞犍陀·维韦克上的可视化

还有成功!将经过适当训练的算法的结果可视化是相当强大的!

下面是Github上的代码。快乐深度迁移学习!

来源:

  1. https://www.youtube.com/watch?v=OMDn66kM9Qc&ab _ channel = PyTorchLightning
  2. https://py torch . org/vision/main/generated/torch vision . transforms . normalize . html
  3. https://www.kaggle.com/alessiocorrado99/animals10(牌照 GPL2 )

如果你还不是中会员,想支持我这样的作家,可以通过我的推荐链接随意报名:https://skanda-vivek.medium.com/membership

关注我 如果你喜欢这篇文章——我经常在复杂系统、物理学、数据科学和社会的界面上写作。

获取每周数据透视 订阅此处

深度视频修复

原文:https://towardsdatascience.com/deep-video-inpainting-756e60ddcaaf

用深度神经网络去除视频中不需要的对象。问题设置和最先进的审查。

图片作者。

虽然抹去记忆的技术,如著名电影【一尘不染的心灵的永恒阳光】(还)不存在,但我们在图像和视频方面取得了良好的进展。把这个拿着啤酒的陌生人从假日视频中移除,破坏了令人惊叹的日落,这不是很酷吗?**

视频修复使我们能够屏蔽视频中不想要的部分。一段时间以前,这项工作需要动画师和图形设计师花费数百个小时来一帧一帧地手工编辑视频。

但是随着机器学习的出现,我们经常可以实现结果,如果不是更好,但肯定非常接近艺术家的能力——用无缝适合视频的内容替换不需要的对象。

随便看看下面的一个小片段,是不是印象深刻?

使用流引导视频完成方法(FGVC) 制作的视频。GIF 由高晨制作。

对我来说,上面的动画看起来难以置信!注意,例如,跳舞女孩前景中的草的高频纹理是如何被保留下来的。为了了解我们是如何得到这些结果的,下面我设置了问题,讨论了可用的解决方案和基准。在最底部,我还包含了每个讨论过的方法的代码链接。说完这些,让我们开始吧!

我们要解决什么?

从图像修复的例子开始更容易,目标是用背景替换场景的一部分,例如 Adobe Photoshop 为照片提供了这个选项。

用 Adobe Photoshop 修复图像的例子。图像、选定区域、结果。图片作者。

在蒙版这个图像时,我指定了我想要修补的区域。Photoshop 使用“内容感知填充”将前景(所选区域)替换为背景纹理。

对于一个视频来说,这个过程会更加费力,因为我们需要标记物体出现的所有帧。

视频修补在概念上类似于图像修补,但是有一点复杂——需要满足整个视频的时间一致性。随着时间逐渐变化的视频不应该具有闪烁的伪像或者在修补区域中对象的颜色或形状的突然变化。并且有许多变量会影响实现时间一致性的难度——场景的复杂性、摄像机位置的改变、场景的改变或用于修补的选定区域的移动(例如移动的对象)。

虽然与图像修复相比,视频修复更具挑战性,但由于需要满足帧之间的时间一致性,它固有地具有更多线索,因为一帧中缺失区域的有效像素可能出现在其他帧中。

可用的解决方案

通常可用的方法可以通过修补机制来区分。它们可以是复制和粘贴——在附近的帧中搜索丢失像素的信息,一旦找到就复制到指定的位置,或者是生成,使用某种形式的生成模型根据整个视频的内容在该区域中产生像素信息。

这两种方法各有利弊。复制和粘贴方法在可以通过视频跟踪像素的情况下工作良好,但在无法检索信息时显然会失败,例如删除了静止区域的静止视频。在这种情况下,创成式模型会更合适。与此同时,生成式方法在重建中可能不太准确,通常会产生模糊的、跨可能选项的平均解。

通常提出的解决方案采用卷积神经网络(CNN)、视觉转换器或 3D CNNs。后者由于内存和时间的限制而不太受欢迎。

复制并粘贴

为了从邻近的帧中复制丢失像素的内容,我们首先需要追踪该像素的位置。这通常通过光流来完成。

光流提供了关于背景和物体跨帧位移的信息。如果计算的流量误差很小,我们可以从第一帧到最后一帧跟踪特定像素的精确位置。光流可以向前和向后,即从第一帧向前计算位移,或者以相反的顺序从最后一帧开始计算位移。

寻找光流是具有挑战性的,因为可能有多种可能的解决方案。这就是为什么后向流和前向流经常不匹配,并且经常两者都被计算,并且联合信息被用于改进估计。关于光流方法的快速解释,请查看这篇文章

如果我们不能借用内容来从附近的帧中恢复当前帧,则仍然未知的区域可以与单个帧的单个图像修补合成,然后传播到视频的其余部分。

下面我将按照时间顺序介绍复制和粘贴架构。由于用于流量估计的基于优化的方法的计算复杂性很高,下面描述的许多方法或者找到替代、简化或者近似它的机制。

深度视频修复 ,2019

图片由大婚金

该论文的叙述围绕确保修补视频的时间一致性的方法而构建。作者为第 t 帧的生成设置了三个要求。它应该与时空相邻帧 X(t+/-N)一致,其中 N 表示时间半径;先前生成的帧 yt1;以及之前的所有历史。

第一种机制是基于反馈上一步生成的帧以及当前步骤中的帧——循环反馈。在组合之前,源特征点和参考特征点是对齐的。这种策略有助于模型从相邻帧借用可追踪的特征。为了实现这一点,流量子网络被用来估计四个空间尺度(1/8、1/4、1/2 和 1)的特征图之间的流量。

第二种机制包括在具有流网 2 的两个连续帧之间的最细尺度的显式流监控。

虽然循环反馈连接了连续的帧,但填补大洞需要更多的长期知识。第三种机制基于时间记忆层,有助于长期连接不同时间步长的内部特征。这里,作者采用了卷积 LSTM ( ConvLSTM )层和翘曲损耗。

该模型用三种损失训练——重建损失(L1 + SSIM)、流量估计损失和扭曲损失。

复制粘贴网络进行深度视频修复 ,2019

图片由李成浩提供。

该网络基于三个阶段—对齐、复制和粘贴。

在对齐步骤中,参考帧与测试帧对齐,以匹配像素之间的对应关系。

复制阶段由编码器和上下文匹配模块组成。在编码器模块中,对齐的帧及其掩码通过特征编码器。所得的嵌入然后被传递到上下文匹配模块——帧特征和它们的可见性图之间的余弦相似性的组合。这个步骤产生了每个像素对丢失区域的贡献的权重。

解码器(粘贴模块)获取权重和连接的帧特征,并输出结果图像。

用 L1、风格、感知和总变差损失的组合来训练整个网络。

深度流引导图像修复 ,2019 (DFC-Net)

图片由徐睿拍摄。

本文通过一个由粗到细的深度流完井网络(DFC-Net)解决了光流的高计算复杂度问题,该网络由三个小的子网(DFC-S)组成。这三个子网中的每一个都接受大小调整为原始大小的 1/2、2/3 和 1 的输入。

第一个 DFC-S 接受两种类型的输入:用 FlowNet2 估计的流图的串联,以及相关的二进制掩码序列,指示每个流图中缺失的区域。网络输出流量的精确估计。

估计的流场用于引导像素的传播以填充缺失的区域。具体而言,DFC-Net 遵循由粗到细的细化来完成流场,同时通过硬流示例挖掘来进一步提高其质量。

优化目标是最小化预测和地面真实流量之间的 L1 距离。然而,因为视频序列中的大部分流动区域是平滑的,所以使用 L1 损失直接导致在训练期间平滑区域占优势,而预测中的边界区域是模糊的。为了克服这一点,所有像素按照损失的降序排序。前 p 个百分比的像素被标记为硬样本。它们的损失被加权以迫使模型更多地关注那些区域。

【流边引导】视频完成 、2020 (FGVC)

图片由高晨提供。

流边缘引导的视频完成(FGVC)由流完成、时间传播和融合步骤组成。

在流完成步骤中,用流网 2 计算相邻和非相邻帧之间的反向和正向流。为了提供尽可能多的信息,该方法使用本地和非本地帧之间的流。由于流中的一些信息丢失(由于掩码),该方法首先使用 Canny 边缘检测器和 EdgeConnect 在流中找到边缘,以连接不相交的区域。基于边缘,执行边缘引导的流动补偿。对于非局部帧,使用单应变形来补偿大的运动。

在时间传播期间,遵循流动轨迹来传播每个缺失像素的一组候选像素。分配五个候选像素——两个来自前向和后向流动通道,三个来自远处的非局部帧。

在无缝融合步骤中,通过置信度加权平均来融合候选对象。在梯度域中执行融合,以避免可见的颜色接缝。最终融合是通过泊松优化获得的。

https://arxiv.org/abs/2108.06765,2021 (VOIN)

********

图片由雷科

VOIN 基于这样的假设,即关于被遮罩对象遮挡的形状的知识可以提高算法的性能。

该模型由三个模块组成——(I)基于变换器的形状完成模块,其学习从可见掩模区域推断完整的对象形状;(ii)遮挡感知流完成模块,用于通过加强流一致性来捕捉移动对象并跨甚至在时间上相距很远的帧传播内容;(iii)流动引导的视频对象修补

在形状完成模块中,对象轮廓引导流动预测过程。在整个对象区域内实施流动平滑(流动梯度通常很小,除非沿着不同的对象运动边界)。

为了填充剩余像素,作者训练了一个遮挡感知门控生成器来修复视频对象的遮挡区域。该模型采用残差时间移位模块(TSM)作为构建模块,将部分通道沿时间维度移位,进行联合时空特征学习。

该模型使用两个鉴别器进行训练——一个用于感知质量和时间一致性,另一个用于对象语义。为了调整流完成网络,采用了 L1 流损失、图像梯度损失、拉普拉斯金字塔损失和幻觉内容的扭曲损失。用二元交叉熵和骰子损失来训练形状完成模块。

除了这些方法,作者还提供了一个新的数据集 YouTube-VOI-包含 5305 个视频,一个 65 类标签集,包括常见对象,如人、动物和车辆,以及超过 200 万个用于移动视频对象的遮挡和可见遮罩

生成模型

标准的图像生成模型,例如基于 2D CNN 的生成敌对网络(GANs ),尽管在图像修复方面取得了成功,但对于视频修复却不太适用。其主要原因是无法说明像素之间的时间关系。因此,使用 3D CNNs 或视觉转换器。由于这种类型中表现最好的模型是基于变形金刚的,所以我把重点放在它们身上。作为视觉变形金刚的回顾,看看的博客

然而,变形金刚的一个大缺点是对注意力层的内存要求非常高,这限制了它们的输入大小。不同的模型以不同的方式处理这个问题。

学习联合时空变换进行视频修复 ,2020 (STTN)

图片由提供。

该模型的设计基于多尺度多头变压器。STTN 将以目标帧为中心的帧窗口和从视频的其余部分均匀采样的帧作为输入。该模型使用不同比例的小块,而不是处理完整的帧。

STTN 由三部分组成——帧级编码器、多层多头时空转换器和帧级解码器。帧级编码器是 CNN,它为每一帧编码深度特征。类似地,帧级解码器将特征解码回帧。

时空转换器是核心组件,学习所有缺失区域的联合时空转换。不同的变换头计算不同尺度的空间块的相似性。转换器运行“嵌入-匹配-参与”算法。在嵌入步骤中,用编码器或前层变换器提取的特征被映射到查询和存储器(键值)。在匹配步骤中,通过匹配从所有帧中提取的空间碎片中的查询和关键字来计算区域相似性。在关注步骤中,检测相关区域并对缺失区域进行变换,其输出通过对相关面片进行加权求和来计算。

该模型是用 L1 和对抗性损失训练的。

解耦空间变压器用于视频修复 ,2021 (DSTT)

图片由刘锐拍摄。

DSTT 是不同于 STTN 的建筑,因为它分别处理空间和时间特征。该方法基于几个架构模块——堆叠特征编码器(卷积神经网络)、一个空间解耦和一个临时解耦的变换器以及一个解码器。

分层特征编码器是卷积层的堆叠,底层特征附加到更深的层,从而从一层到另一层传播低级细节。

然后,单个帧的聚集特征被分割成小块,并通过空间解耦的变换器。暂时解耦的变换器使用在相同空间位置的几个连续帧的特征片作为输入嵌入。然后,输出被汇总并通过解码器。

该模型是用 L1 和对抗性损失训练的。

FuseFormer:融合变形金刚中的细粒度信息进行视频修复 ,(2021)

图片由刘锐提供。

这篇论文是由研究 DSTT 的同一个小组提出的。然而,这部作品并没有提到 DSTT,只是将自己与 STTN 进行了对比。作者依赖于最初提出的视觉变形器 (ViT)的单一尺度架构,而不是在变形器之前引入基于不同尺度补丁的基于 CNN 的特征提取器。

新模型引入了对原始 ViT 的两个修改——贴片软分裂和软贴片组合,以及融合形成器块。正如这篇论文所说,所有这些都提高了基线模型的性能。

软分割模块将图像分割成具有重叠区域的小块,并且相应地,软合成将这些重叠的小块组合回图像。

融合前馈网络(F3N)取代了标准变压器模型中的两层 MLPs。在 F3N 中,在两个完全连接的层之间,每个 1D 令牌都以其原始的空间形状被整形回 2D 面片,然后被软合成为一幅完整的图像。重叠区域中像素的重叠特征将来自所有相邻块的对应值相加,用于进一步的细粒度特征融合。然后,面片被软分割和平坦化成 1D 向量,这些向量被馈送给第二个 MLP。

该模型是在 L1 和对抗性损失的组合上训练的。

基准测试—如何比较模型

评估视频生成模型的困难在于找到一个好的基准来测试:空间质量、时间一致性和真实性。视频修复是一项特别具有挑战性的评估任务,因为它取决于多种因素,如相机运动、对象运动和遮罩大小,并且在评估性能时应该跟踪所有这些因素。

这些方法性能的最佳评判者是人类观察者。然而,用人类参与者进行实验既昂贵又耗时。我在两篇文章中详细讨论了改进当前数据收集方法的挑战和方法:深度图像质量评估用于成对比较的主动采样

代替人类观察者的是客观的质量度量。最近的一篇论文有一个吸引人的名字:魔鬼在细节中:视频修复的诊断评估基准在一个新的详尽的基准数据集上比较了最先进的方法,该数据集涵盖了视频修复的各种场景的过多组合:

  • 面具尺寸小/大
  • 掩模形状变化缓慢/快速
  • 掩模移动速度慢/快
  • 背景运动高/低
  • 摄像机运动高/低

该基准包括九个图像质量指标,包括典型的图像质量和逼真度 FID、LPIPS、PSNR 和 FSIM,以及考虑时间一致性的指标——VFID、PVCS 和扭曲误差。

摘要

创建一个功能齐全的视频修复应用程序是一个巨大的工程挑战。虽然有一些非常令人印象深刻的结果,但所有方法仍然会产生明显的伪像,而获得实时速度是一项巨大的工程工作。

另一个需要解决的现象是与修补对象相关联的帧部分,例如阴影或反射,它们有时很难被发现,但是仍然需要被去除。处理这类问题的方法之一是最近由谷歌、牛津和魏茨曼科学研究所发表的一篇 Omnimate 论文。

然而,和往常一样,巨大的进步伴随着技术的误用。视频修复最明显的例子之一就是深度赝品。这在以持续不断的信息流为基础的现代世界中可能会在更大范围内严重损害个人生活和社会。想象一下法庭上的伪造证据或社交媒体上的错误信息,这些信息的来源往往未经核实。这就是为什么有论文通过开发修复区域检测的方法来解决这个问题,就像的那篇

在这篇文章中,我谈到了以下论文中的深度视频修复列表作品:

复制粘贴: 深度视频修复,2019,( 代码);复制粘贴网络进行深度视频修复,2019,( 代码);深度流引导图像修复 (DFC-Net),2019,( 代码);流边引导视频完成 (FGVC),2020,( 代号);遮挡感知视频对象修复 (VOIN),2021 ( 项目-页面 )。

生成式: 学习联合时空变换进行视频修复 (STTN),2020,( 代码);用于视频修复的解耦空间变换器 (DSTT),2021,( 代码); FuseFormer:融合变形金刚中的细粒度信息进行视频修复,2021,( 代码 )。

我希望你喜欢这篇文章,如果是的话,让我们一起关注这个领域——与朋友分享吧!

阅读更多关于机器学习和图像处理的**按订阅**

喜欢作者?保持联系!

我错过了什么吗?不要犹豫,直接在 LinkedInTwitter 上给我留言、评论或发消息吧!

**** ****

DeepAR:通过深度学习掌握时间序列预测

原文:https://towardsdatascience.com/deepar-mastering-time-series-forecasting-with-deep-learning-bc717771ce85

亚马逊的自回归深度网络

创造了稳定的扩散[1]

几年前,时间序列模型只能处理单一序列。

因此,如果我们有多个时间序列,一种选择是为每个序列创建一个模型。或者,如果我们可以将我们的数据“列表化”,我们可以应用梯度增强的树模型——即使在今天也非常有效。

第一个可以本地处理多个时间序列的模型是DeepAR【2】,这是由亚马逊开发的自回归递归网络

在本文中,我们将深入了解 DeepAR 如何工作,以及为什么它是时序社区的一个里程碑。

如果你想了解受 DeepAR 启发的其他深度学习模型,请查看这篇文章:

什么是 DeepAR

DeepAR 是第一个将深度学习与传统概率预测相结合的成功模型。

让我们看看为什么 DeepAR 脱颖而出:

  • 多时间序列支持:模型针对多个时间序列进行训练,学习进一步提高预测准确性的全局特征。
  • 额外协变量: DeepAR 允许额外特性(协变量)。例如,如果你的任务是温度预报,你可以包括humidity-levelair-pressure等。
  • 概率输出:该模型利用分位数损失来输出预测区间,而不是进行单一预测。
  • “冷”预测:通过从数以千计潜在共享一些相似性的时间序列中学习, DeepAR 可以为很少或根本没有历史的时间序列提供预测。

DeepAR 的 LSTMs

DeepAR 使用 LSTM 网络来创建概率输出。

长短期记忆网络 ( LSTMs)在众多的时间序列预测模型架构中使用:例如,我们可以使用:

  • 普通 LSTMs
  • 多层 LSTMs
  • 带 CNN 的 LSTMs
  • 时间 2 秒的 LSTMs
  • 编码器-解码器拓扑中的 LSTMs
  • 注意【3】(图 1 )的编码器-解码器拓扑中的 LSTMs

图 1: 谷歌神经机器翻译——GNMT 架构(来源)

此外,虽然变形金刚确实在 NLP 领域占据主导地位,但在与时间序列相关的任务中,它们并没有决定性地胜过 LSTMs。主要原因是 LSTMs 更擅长处理本地时态数据。

关于循环网络 vs 变压器的更多信息,查看这篇文章

DeepAR —建筑

与之前的模型相反, DeepAR 使用 LSTMs 的方式有点不同:

DeepAR 没有使用 lstm 直接计算预测,而是利用 lstm 来参数化高斯似然函数。即估计高斯函数的*θ = (μ, σ)*参数(表示标准偏差)。

图 2图 3 显示了 DeepAR训练推理模式下的架构概况:

图 2: 训练期间 DeepAR 中的数学运算(来源)

先说培训。假设我们在时间序列i的时间步t:

  1. 首先,LSTM 单元将当前时间步t的协变量x_i,t和先前时间步t-1的目标变量z_i,t-1作为输入。此外,LSTM 接收前一时间步的隐藏状态hi,t-1
  2. 然后,LSTM 单元输出其隐藏状态hi,t,该隐藏状态被馈送到下一步骤。
  3. *μ**σ* 值是从hi,t间接计算出来的,并且“成为”高斯似然函数 *p(y_i|θ_i)= l(*z_i,t|Θι,t*)*的参数。论文用希腊字母 theta *θ = (μ, σ)* 定义了这些参数。如果你不理解这部分,不要担心,我们稍后会更详细地解释。
  4. 换句话说,该模型试图回答这个问题:构造高斯分布的最佳参数*μ**σ*是什么,该高斯分布输出尽可能接近目标变量z_i,t的预测?
  5. 训练步骤t到此结束。当前目标值z_i和隐藏状态hi,t被传递到下一个时间步骤,并且训练过程继续。由于 DeepAR 每次训练(和预测)单个数据点,该模型被称为自回归。****

****图 3:DeepAR 训练推理中的数学运算(来源)

推理的步骤几乎是一样的。

不过有一点改变了:现在,在每个推理步骤t,我们使用在之前的时间步骤t-1中采样的预测变量ž_i,t-1来计算新的预测ž_i,t

记住,ž_i,t现在是从我们的模型在训练期间学习的高斯分布中采样的。但是,我们的模型并没有直接学习参数*μ**σ*

我们将在下一节看到这些参数是如何计算的。

高斯似然

在深入研究 DeepAR 的自回归特性如何工作之前,理解似然函数如何工作是很重要的。如果你熟悉这个概念,你可以跳过这一节。

最大似然估计的目标是找到更好地解释样本数据的最佳分布参数。

让我们假设我们的数据遵循高斯(正态)分布。每个高斯分布由平均值*μ* 和标准偏差*σ* *θ = (μ, σ)* 参数化。于是,高斯似然ℓ给出了*θ = (μ, σ)* 的定义为:

现在,看一下图 4** 😗*

****图 4:2 个高斯分布的最大似然估计(图片由作者提供)

我们有绿色和橙色的数据点,每一个都遵循不同的高斯分布。让我们假设给你这些数据点,你的目标是估计它们的两个高斯分布。

更正式地说,任务是在两个分布中找到最佳的*μ* *σ*来最佳地拟合那些数据( DeepAR 假设只有一个分布)。在统计学中,这个任务也被称为最大化高斯对数似然函数:

对于所有时间步长t[t…τmax]i[1…N],函数被最大化,其中N是我们数据集中时间序列的总数。

参数估计

在统计学中,参数*μ* *σ* 通常是使用mle 公式(mmaximumlog-likelihoodestimulator s)来估计的,这些公式是通过对似然函数进行微分而得到的。

我们这里不这么做。

相反,我们让 LSTM 和 2 个密集层基于模型的输入导出那些参数。这个过程如图图 5:** 所示**

****图 5:μσ参数计算(图片由作者提供)

估算*μ**σ* 的过程很简单:

  • 首先, LSTM 计算其隐藏状态hi,t
  • 然后,hi,t经过密集层W_μ计算平均值*μ*
  • 同样,相同的hi,t通过第二个密集层W_σ并计算平均值*σ*
  • 现在我们有了*μ**σ*模型使用这些参数创建高斯分布,并采集样本。然后,模型检查该样本与实际观察值的接近程度z_i,t
  • 时间步长t的训练到此结束。LSTM 权重和两个密集层W_μW_σ在反向传播期间被训练。

换句话说, DeepAR 通过hi,t, W_μW_σ间接计算*μ* *σ* 。这样做是为了通过反向传播使他们的计算成为可能。

在推理过程中,我们没有目标变量z_i,t进行比较。 DeepAR 已经学习了所有的神经网络权重,并使用它们来创建预测ž_i,t

就是这样!我们现在已经看到了 DeepAR 如何端到端地工作。

在接下来的章节中,我们将解释更多关于 DeepAR 的机制。

****注:估计的均值和标准差参数在统计学中正式用μ hatσ hat符号化。

自动缩放

处理多种不同的时间序列是棘手的。

想象一个产品销售预测场景:一个产品可能有几百个订单的销售额,而一个不同的产品可能有几百万个订单的销售额。

不同数量级的时间序列之间的这种巨大差异可能会混淆模型。为了克服这个问题, DeepAR 引入了一个自动缩放机制。更具体地说,该模型计算一个依赖于项目的ν_ι来重新调整自回归输入z_i,t。这由以下公式给出:

因此,在每个时间步t,来自前一步的自回归输入z_i,t首先被该因子缩放。

****注意:DeepAR 的自动缩放机制非常好用。然而,在实践中,最好首先手动标准化我们的时间序列。这样做将增强我们模型的性能。

时序景观中的 DeepAR

在本节中,我们将讨论 DeepAR 如何与其他型号竞争,以及它的局限性。

统计模型

作者表明 DeepAR 优于传统的统计方法,如 ARIMA 。此外, DeepAR 相对于那些模型的巨大优势是它不需要额外的特征预处理(例如,首先使时间序列平稳)。

亚马逊后来发布了一个更新版本,名为 DeepVAR[4] ,显著提高了性能。我们将在以后的文章中描述这个模型。

深度学习模型

自从 DeepAR 发布以来,研究界已经发布了无数用于时间序列预测的深度学习模型。

并不是所有的都可以和 DeepAR 直接比较,因为他们的工作方式不同。据我所知,我能想到的最接近的是时间融合变压器(TFT) [5]。****

让我们来讨论一下 DeepAR 和 TFT 之间的两个显著区别:

1。多个时间序列 DeepAR 为每个时间序列计算一个单独的嵌入。这种嵌入被用作 LSTM 的一个特征,帮助 DeepAR 区分不同的时间序列。

TFT 也利用 LSTMs,工作方式类似。然而,TFT 使用这些嵌入来配置 LSTM 的初始隐藏状态h_0。这种方法好得多,因为 TFT 在每个时间序列上适当地调节 LSTM 单元,而不改变时间动态。

2。预测类型 TFT 不是自回归模型——它被归类为 多时段预测模型 。两种类型的模型都可以输出多步预测。然而,多时段预测模型一次生成预测,而不是像自回归模型那样逐个提供预测。**

这种方法的优势在于,多时段预测模型可以为其协变量没有任何值的时间步长创建预测。TFT 在这方面表现出色,因为它是功能多样性最丰富的型号之一。

结束语

DeepAR 是一个卓越的深度学习模型,是时间序列社区的一个里程碑。

此外,这种模型在生产中很普遍:它是亚马逊用于时间序列预测的 GluonTS [6] 工具包的一部分,可以在亚马逊 SageMaker 上进行训练。

在下一篇文章中,我们将使用 DeepAR 来创建一个端到端的项目。
敬请期待!

感谢您的阅读!

我每个月写一篇有影响力的 AI 论文的深度分析。
保持连接!

参考

[1]用稳定扩散创建,CreativeML Open RAIL-M license。文字提示:“穿越太空的星云,数码艺术,插画”

2 D. Salinas 等 DeepAR:用自回归递归网络进行概率预测 《国际预测杂志》(2019)。

[3]吴永辉等谷歌的神经机器翻译系统:弥合人与机器翻译的鸿沟 (2016)

[4] D. Salinas 等 低秩高斯 Copula 过程的高维多元预测 国际预测杂志(2019)。

[5] Bryan Lim 等人 用于可解释的多时间范围时间序列预测的时间融合变换器 (国际预测杂志,2021 年 12 月)

[6]亚马逊的 GluonTS 包,https://ts . gluon . ai/stable/API/Glu onts/Glu onts . model . deepar . html

DeepMind 的 AlphaCode 解释道:你需要知道的一切

原文:https://towardsdatascience.com/deepminds-alphacode-explained-everything-you-need-to-know-5a86a15e1ab4

第一个在编程比赛中达到竞技水平的人工智能

弗洛里安·奥利佛在 Unsplash 上拍摄的照片

很长一段时间以来,编程一直是一项高地位、高需求的技能。

各行各业的公司和企业在非常基础的层面上依赖于人类开发人员的能力:编写和理解计算机语言的人。最近,随着大型语言模型的出现,AI 公司开始探索可以学习编码的系统的可能性。 OpenAI 的 Codex——嵌入 GitHub Copilot——是第一个显著的例子。Codex 可以读取简单的自然语言命令和指令,并编写符合用户意图的代码。

然而,编写小程序和解决简单的任务“远非真实世界编程的全部复杂性。”像 Codex 这样的人工智能模型缺乏大多数程序员在日常工作中所依赖的解决问题的技能。这就是 DeepMind 想要用 AlphaCode 填补的空白,AlphaCode 是一个经过训练的人工智能系统,可以“理解”自然语言,设计算法来解决问题,然后将它们实现为代码。

AlphaCode 展示了自然语言理解和解决问题能力的独特技能组合,结合了大型语言模型的统计能力特征。该系统在流行的竞争性编程平台 Codeforces 上与人类程序员进行了测试。AlphaCode 在 10 场竞赛中的平均排名为 54.3%,这使得它成为第一个在竞争性编程竞赛中达到人类程序员水平的人工智能。

我已经研究了 AlphaCode 论文以理解 AlphaCode 是什么和不是什么,这些令人印象深刻的结果意味着什么,意味着什么,以及人工智能和人类开发者的未来。我还研究了人工智能专家和有竞争力的程序员对 AlphaCode 的看法,所以你有不同的独立观点来形成你自己的观点。

本文是一篇全面的综述,分为 6 个部分(及其各自的小节)。为清晰起见,我遵循本文的结构:

**1\. Setup: Competitive programming**
  1.1 What is competitive programming?
  1.2 The backspace problem
  1.3 Evaluating AlphaCode
**2\. AlphaCode: What it is and what it does**
  2.1 A brief overview of AlphaCode
  2.2 Model architecture
  2.3 Datasets: Pre-training (GitHub) and fine-tuning (CodeContests)
  2.4 Sampling, filtering, and clustering
**3\. Results**
  3.1 Codeforces competitions against humans
  3.2 CodeContests: Detailed performance analysis
  3.3 APPS: How AlphaCode compares to previous AI coding systems
  3.4 Qualitative results
**4\. Broader impact**
  4.1 Applications
  4.2 Potential risks and benefits
**5\. Reception and criticism**
  5.1 Looking at statistics from another perspective
  5.2 Human level is still light-years away
  5.3 Narrow AI vs broad humans
  5.4 Monkeys typing Hamlet
**6\. Conclusion**

我将在整篇文章中加入评论,以更深入地探讨一些问题、想法和结果。拥抱你自己,因为这是一篇很长的文章,但是一旦你读了它,你就会知道关于 AlphaCode 的一切,并且能够更好地理解未来的 AI 编码系统。

(如果你已经读过这篇论文,你可以跳到第 4-6 部分。)

1.设置:竞争性编程

1.1 什么是竞争性编程?

这是一项运动。全世界的程序员在互联网上相遇,并试图在一定的约束条件下解决问题,比如时间或语言。这些比赛,像 ICPC T1 和 T2 IOI T3,越来越受欢迎,每年都吸引成千上万的参与者。

一些比赛可以持续几周,而另一些则持续几个小时。这就是 DeepMind 测试 AlphaCode 能力的每周竞赛 Codeforces 的情况。参与者有 5-10 个问题描述(带有一些测试案例和相应的解释),他们必须在大约 3 个小时内提交尽可能多的解决方案。根据正确提交的数量对他们进行评估——根据隐藏的测试用例进行测试——并对不正确的提交进行处罚。

竞争性编程需要一套非常特殊的技能,即使对人类来说也极具挑战性,而且它广受欢迎——这是测试 AlphaCode 能力的完美舞台。

1.2 退格问题

为了让你对解决竞争性问题有一个实际的感觉,让我们想想退格问题(1500 分的中等难度)。在退格问题中,你从两串英文字母“s”和“t”开始。这个想法是试图用退格键将 s 转换成 t。您必须键入 s,但您可以随时决定按退格键来删除前一个字母,而不是键入一个字母。你必须确定你是否能通过这个过程从 s 得到 t。

例如,如果 s=ababa,t=aba,答案是“是”您键入 s 的前四个字母(abab ),然后按 backspace 而不是最后一个字母,得到 aba。有些情况很简单,就像这个例子,但是对于复杂的测试例子来说就很棘手了。

这种性质的问题需要求解者经历一系列的步骤。首先,你需要了解问题的内容和限制(例如,按退格键只能在输入字母的而不是时完成,不能在字母之后)。第二,你必须想出一个有效的算法来满足需求并正确地覆盖所有的测试用例(至少,因为还有隐藏的用例)。第三,你必须用编程语言在代码上实现它,比如 Python。

AlphaCode 成功解决了退格问题,展示了与以前依赖显式指令的人工智能编码系统相比的改进程度。

为了理解这个过程有多微妙,以及 AlphaCode 的能力必须有多精确,让我们考虑一下问题描述中的“替代”一词。如果问题允许你在输入一个字符后按退格键,那么问题、算法和程序就需要完全不同。这个细微的细节很容易被人忽略。然而,AlphaCode 设法解决了这个问题和许多其他类似的问题。

1.3 评估字母代码

DeepMind 在 Codeforces 上测试了 AlphaCode,以将其与人类程序员进行比较。然而,Codeforces 的评估系统对于那些想在这些发现的基础上发展的人来说并不是一个容易复制的基准。这就是为什么 DeepMind 提出了一个通用的度量标准:n@k。

该指标被定义为“每个问题使用 k 个样本中的 n 个提交解决的问题的百分比”,它与 Codeforces 评估提交的方式非常相似。AlphaCode 允许生成 k 个样本(最大范围为 100K-1M ),从这些样本中,系统可以提交 n 个解决方案(n≤k)进行评估。如果 n 次提交中至少有一次解决了问题,AlphaCode 就成功了。

DeepMind 的研究人员使用 10@k 来评估 Codeforces 上的 AlphaCode,模拟不正确提交的惩罚条件。他们使用 pass@k 对系统进行了进一步评估——提交所有样本进行评估。它衡量潜在的最大成功率(并允许与以前的系统如 Codex 进行比较)。

2.字母代码:它是什么和它做什么

2.1 字母代码的简要概述

  1. 预训练:AlphaCode 是一个基于 transformer 的大型语言模型,最初在 GitHub 代码库中进行训练(与 Codex 的方式类似)。
  2. 微调:DeepMind 创建了一个名为 CodeContests 的竞争性编程数据集,以微调和专门化 AlphaCode。它弥补了竞争性编程问题的公开例子数量少的不足。
  3. 生成:对于每个 Codeforces 问题,AlphaCode 会生成多达一百万个样本(k)。
  4. 过滤和聚类:通过在可见的测试用例上评估来过滤样本。在丢弃了那些没有通过测试的样本之后,它们根据它们对定制测试用例的输出对剩余的样本进行聚类(参见第 2.4 节)。然后,他们选择候选人进行评估(n)。

图 AlphaCode 概述

2.2 模型架构

DeepMind 创建了五种尺寸的 AlphaCode 模型:300M、1B、3B、9B 和 41B 参数。所有这些都被命名为 AlphaCode,但该公司在他们的通信中提到的一个(它产生了最好的结果)是 9B 和 41B 模型与聚类相结合的集合。他们建立了不同规模的模型,以比较规模、训练时间和计算效率等因素的影响(见第 3.2 节)。

2.3 数据集:预训练(GitHub)和微调(CodeContests)

所有模型都在一个包含大约 700 GB GitHub 开源代码的数据集上进行了预训练。这使得 AlphaCode 能够学习代码表示,并解决简单的显式任务,就像以前的 AI 编码系统一样。

这些模型随后在 CodeContests 上进行了微调,CodeContests 是 DeepMind 设计的一个定制竞争编程数据集。这一步不包括 DeepMind 的过程,并允许 AlphaCode“适应目标竞争编程领域”。CodeContests 是来自 Codeforces、Description2Code 和 CodeNet 的问题、解决方案、测试案例和元数据(难度等级和类型标签)的汇编。

代码竞赛最初有一个主要问题:误报。因为测试数据很少,AlphaCode 可以产生被标记为正确的解决方案——因为它们解决了可见的测试用例——但实际上是不正确的。一个相关的问题是“慢阳性”。正确但算法效率低下的解决方案。这个问题在以前的人工智能编码数据集如 APPS 和 HumanEval 中普遍存在,误报率为 30–60%。

DeepMind 的研究人员找到了一个解决方案。他们通过改变现有的测试用例生成了额外的测试用例:“随机增加或减少整数,交换和改变字符串中的字符。”然后用正确的解决方案验证这些测试。结果令人印象深刻,将假阳性率降低到了 4%。

2.4 采样、过滤和聚类

一旦模型被完全训练,他们必须生成样本。为了获得最佳性能,他们对每个问题取样多达一百万个解决方案。为了使如此大量的样本多样化,他们混合了 Python 和 C++解决方案,使用了高温、随机标签和评级。

为了从庞大的样本库中进行选择,并模拟竞争性编程竞赛的条件,他们应用了过滤和聚类,将数十万个样本减少到 10 个提交。消除坏样本的直接方法是在可见的测试用例上过滤它们,并移除不正确的解决方案。

对可见测试进行过滤会移除绝大多数样本。似乎对数十万个错误的解决方案进行采样对于 AlphaCode 取得如此令人印象深刻的结果至关重要。这与人类通过几次尝试就能找出正确解决方案的能力形成对比。这让我想起了有监督的深度学习系统在分类任务中看到成千上万个例子才能区分它们的必要性,而人类几乎不需要几个例子。

过滤去除了+99%的样本,但仍留下数千个潜在的提交。为了进一步将样本数量减少到 10 个,DeepMind 的研究人员想出了一个聪明的过程。他们从样本中提取聚类,将语义上(而非句法上)等价的解决方案分组。

为此,他们在相同的 GitHub 数据集上使用与主模型相同的架构预训练了一个测试输入生成模型。想法是让模型为问题生成新的测试输入。AlphaCode 的样本可以通过这些新的测试进行评估——不一定有效或正确,但不管怎样都是有用的。然后,被评估的样本根据他们对新测试给出的答案进行分组——错误的答案比正确的答案提供了更多的信息。

为了选择 10 个提交样本,他们发现从最大到最小的每个集群中选择一个样本可以获得最佳性能。

3.结果

3.1 Codeforces 与人类的竞赛

在 Codeforces 上评估 AlphaCode 有两个独特的特性。首先,它防止系统利用数据集的弱点,如误报。第二,它将 AlphaCode 与“这项任务中表现最好的人类竞争者”进行了比较。在人类 ELO 等级中排列 AlphaCode 让我们对这些系统的技术水平和真正能力有一个清晰的认识。

DeepMind 的研究人员模拟运行 AlphaCode live,并向 Codeforces 平台提交了每次比赛的 10 个解决方案。结果是 10 场比赛的平均值。取得最佳成绩的 41B+9B 字母代码组合获得了 54.3%的估计平均排名(表 1),这相当于 1238 的 ELO 评级——在过去 6 个月中至少参加过一次比赛的参与者中排名前 28%(图 2)。

表 Codeforces 上的 AlphaCode 结果。每次竞赛的最佳、预计和最差排名。平均估计排名为 54.3%,将 AlphaCode 置于人类竞争性程序员的水平。

图 Codeforces 上的 AlphaCode 结果。ELO 评分为 1238,在过去 6 个月内至少参加过一次比赛的所有参与者中排名前 28%。

作者表示,“据我们所知,这是计算机系统首次在编程竞赛中与人类参与者竞争。”Codeforces 创始人 Mike Mirzayanov 表达了他的敬畏和兴奋:“我可以有把握地说,AlphaCode 的结果超出了我的预期。我对此持怀疑态度,因为即使在简单的竞赛问题中,也经常需要不仅实现算法,而且(这是最困难的部分)发明算法。AlphaCode 的表现达到了一个有前途的新竞争对手的水平。我迫不及待地想知道前方会发生什么!”

为了正确看待这些结果,我想提出两个条件。

首先,人类顶级表演者达到 90%左右的收视率。AlphaCode 的成就令人印象深刻——该系统本身,包括训练过程和采样选择程序,是人工智能编码的一次飞跃——但将其与人类进行比较很容易被夸大。竞争性编程需要一套 AlphaCode 所缺乏的能力,并通过采样数十万个解决方案来弥补。

其次,DeepMind 又重复了三次评估过程,以评估 10 次提交和无限制提交设置的差异和性能。他们发现,在模型的不同运行中,一些比赛的排名可能会有 30%的差异。例如,在竞赛#1618 中,对于 10 次提交的设置,AlphaCode 得分为 62.3%、32.1%和 62.3%。

估计评级中 30%的变化揭示了 AlphaCode 可能不够健壮而不可靠。然而,在无限制提交的设置中,AlphaCode 表现出了更稳定的性能——这进一步强调了 AlphaCode 对提交许多样本解决方案的依赖,这使得它几乎无法与人类相提并论。

有趣的是,由于计算能力的限制,研究人员没有对模型进行重新采样。他们在所有的评估中使用相同的样本,但以不同的顺序绘制,并用不同的种子进行聚类。据推测,重新训练和重新采样模型会产生更高的方差。

3.2 代码竞赛:详细的性能分析

除了评估 Codeforces 上的 AlphaCode,DeepMind 还评估了 CodeContests 数据集上的模型。Codeforces 的结果允许研究人员与人类参与者进行比较,CodeContests 的结果允许他们对 AlphaCode 的行为和表现进行深入分析。

与 Codeforces 的比赛一样,他们使用了我已经提到的两个版本的度量标准 n @ k:10 @ k 和 pass@k。第一个评估了当他们需要从更大的样本集中缩减提交集时模型的表现——过滤和聚类过程有多好。第二个评估样本生成过程 AlphaCode 在创建可以解决竞争问题的代码方面有多好。

41B 模型结合聚类给出了 10@k 度量的最佳结果。使用 100 万个样本,它在验证集中实现了 34.2%的解决率(表 2)。

表 2:代码竞赛的 AlphaCode 结果。41B 模型总是比 9B 模型好,聚类在所有情况下都改善了结果。

3.2.1 对求解速度的影响:参数、数据集、样本和计算

DeepMind 的研究人员进一步研究了一系列特征对解决率的影响:模型的大小(参数数量)、数据集的大小、样本预算和计算预算。

正如预期的那样,解决率随着参数计数的增加而增加,在所有条件相同的情况下,9B 和 41B 型号大大超过了其他型号。

他们还改变了 1B 模型微调数据集中的问题和解决方案的数量,并发现了另一个预期结果:增加数据集大小提高了样本预算所有范围内的解决率(图 3)。

图 3:从 10 到 10K 的样本预算中,解决率随着数据集规模的增加而增加(增加了问题和解决方案的数量)。

在 10@k 和 pass@k 指标中,当增加样本的数量时,求解速率呈对数线性变化(图 4)。这两个图的差异突出了样本选择的重要性(pass@k 测量上限,即过滤和聚类完美完成的情况)。

图 4:在 10@k 和 pass@k 两种情况下,当增加样本预算时,模型大小的求解率都有所提高。

有趣的是,在 10@k 的情况下,尽管提交的数量保持不变,但更多的采样改善了结果。充分搜索解决方案空间与充分选择提交内容同样重要。然而,DeepMind 的研究人员提到,由于采样成本,试图通过这种方式提高性能变得非常困难。

此外,在给定样本预算的情况下,较大的模型性能更好,并且随着样本预算的增加,它们的性能会进一步提高。从经济角度来说,如果我们想提高性能,增加模型的大小比样本预算更可取。

训练计算求解比例(图 5a)。它还与采样计算成比例(图 5b)。我们可以看到,较大的模型需要更多的计算能力来生成样本,但最终,随着计算能力的提高,最佳模型大小也会增加。这意味着,如果我们有更大的计算机,我们可以更好地利用更大的模型,并充分利用它们的能力来产生更好的样本。

图 5:解决率随着训练计算(较大的模型性能更好)和采样计算(较大的模型需要更多的计算能力,但性能更好)而增加。

3.2.2 对求解率的影响:过滤和聚类

研究人员发现,过滤过程移除了模型大小中 99%的样本(表 3)。随着模型变得越来越大,他们也越来越擅长产生通过示例测试的样本。在 100K 个样本中,所有模型至少有一个样本通过了+80%问题的示例案例(41B 模型为+90%)(表 3)。(请记住,通过示例测试的样本不一定是正确的。)

表 3:过滤过程去除了+99%的样本。模型很可能找到至少一个通过示例测试的样本(+80%)。

研究人员还研究了过滤和聚类对解决率的影响。他们比较了四种情况(图 6):随机提交 10 个样本(没有过滤,没有聚类)。过滤后随机提交 10 个样本(无聚类),提交 10 个具有完整过滤-聚类过程的样本,提交所有样本进行评估(pass@k)。

图 6:过滤和聚类对解决率的影响:两者都有帮助,但仍远未达到理论上的最大值。

他们发现过滤和聚类提高了解决率(在大样本预算下更是如此,因为选择的影响越来越大)。尽管如此,即使将这两种技术结合起来,结果也远未达到理论上限。这意味着选择是至关重要的,但也是可以改善的。

3.3 应用程序:AlphaCode 与以前的人工智能编码系统相比如何

为了与以前的模型如 Codex 和 GPT-尼奥进行比较,他们在 GitHub 上预先训练了 1B 模型,并在应用程序上对其进行了微调(没有聚类),其中包括 10,000 个竞争性编程问题。

他们发现,在 1B,AlphaCode 已经在三项任务(介绍、面试和竞争)上超过了 GPT-尼欧,在面试和竞争方面超过了 Codex(表 4)。

表 4: AlphaCode 1B、Codex 12B 和 GPT-Neo 2.7B 在应用程序上的比较。AlphaCode 总体上更好,除了对 Codex 的介绍性任务。

这些结果无疑将最好的 AlphaCode 模型放在了人工智能编码系统的第一位,并将 DeepMind 授予了人工智能树另一个分支的宝座——与国际象棋和围棋的 AlphaZero、电子竞技的 AlphaStar、生物学的 AlphaFold 和 NLP 的 Gopher 一起。

3.4 定性结果

3.4.1 从训练数据复制

AlphaCode 的一个可能的限制,上面的分析很难发现,是一种假设的倾向,即记忆来自训练集的代码块,以解决验证集和现实世界中的看不见的问题。

为了测试 AlphaCode 是否参与这些行为,研究人员分析了它的编程模式。他们将它与人类进行了比较,看两者在多大程度上倾向于逐字使用训练中的代码。他们发现他们的分布非常相似——他们几乎不使用直接从数据集提取的大块代码(图 7)。

图 7: AlphaCode(橙色)和 human(蓝色)在解决方案和训练数据之间有相似的公共代码字符串分布。

这表明 AlphaCode 和人类都不是通过复制代码来解决问题,而是通过正确处理问题描述。

3.4.2 模型解特征

AlphaCode 倾向于在 Python 上更好地编程(C++语法更难),它产生的死代码(编写的代码,但不提供解决方案)数量与人类相似。随着模型变得越来越大,它们总是能更好地解决跨类型标签的问题。模型也更擅长解决难度评级较低的问题。

3.4.3 对问题描述和元数据的敏感性

测试这些参数对于抛弃 AlphaCode 通过尝试每种问题类型的许多不同可能性来解决问题的可能性是至关重要的,只要它处理一个关键字(例如,如果单词“prime”出现在描述中,则尝试与质数相关的所有类型的程序)。

他们发现,当信息模糊时,解决率会下降。特别是,当模型被给出相关但不同的问题时,以及当描述中的部分被删除或文字被删除时,解决率大大恶化。有趣的是,当描述简化时,解决率会增加——这进一步加强了 AlphaCode 对其显著但有限的语言理解技能的依赖。

模型也会受到元数据变化的影响。研究人员发现,在样本之间随机分配标签更好,因为它使模型能够尝试更多样化的解决方案。

4。更广泛的影响

人工智能编码模型和其他大型语言模型一样,对社会非常有益。熟练的模型可以集成到不同的应用程序中,方便人类程序员的日常工作,降低非程序员的准入门槛。

但是它们也会造成伤害。DeepMind 的研究人员研究了这些模型总是伴随着的潜在风险——从偏见和毒性到环境影响,再到未来的文明水平威胁。

4.1 应用

人工智能程序员的一个直接现实应用是成为人类的配对程序员。在大型语言模型出现之前,开发人员使用代码生成器。它解决了时间问题,有助于专注于非重复性、高认知性的任务。人工智能系统可以显著改善这种共生关系。它们可以生成更好的代码,完成更复杂的任务,甚至可以缓解沟通上的限制。

在未来,这纯粹是猜测,人工智能编码系统可能会继续让人类远离计算机语言的趋势。首先,我们用机器语言编程,然后用汇编语言编程,然后我们设计更高级的编程语言。人们可能很快就会用我们更熟练的语言与人工智能系统交流;自然语言。

快速工程和无代码迁移是朝着这个方向迈出的第一步。如果我们可以简单地用英语与人工智能交谈——就像我们现在与 Codex 交谈一样,但更好——并获得有用的建议和解决方案,会怎么样?人类程序员可以专注于他们觉得更有成就感的任务(想象一下钢铁侠和贾维斯的关系)。

人工智能编码系统也可以消除非程序员的进入壁垒。不是让编程工作变得无关紧要,而是解决简单的任务,这些任务对开发人员来说微不足道,但对非技术人员来说却意义重大(例如,在银行、管理、金融、销售等领域)。)

4.2 潜在风险和收益

4.2.1 可解释性:为什么 AlphaCode 做它要做的事情?

神经网络中一个众所周知的问题是它的“黑箱”性质,这个问题在大型语言模型中会变得更糟。神经网络为什么以及如何做它所做的事情是我们通常没有答案的问题。其固有的随机性和统计性使得投入产出关系很难追踪。

作者解释说,编码模型更容易研究,因为生成的算法可以正常测试。"代码生成模型输出的代码是人类可读的,可以用传统方法分析."然而,输入一个问题描述和得到一个程序作为输出之间的过程仍然是不可理解的。

一旦代码生成了,我们可以很容易地分析它,但是如果代码是错误的呢?调试它和调试任何其他类型的神经网络一样困难。

4.2.2 概括——而不是外推

对非分布数据的性能是神经网络的一个弱点。DeepMind 的研究人员表示,对于人工智能编码系统来说,这个问题并不存在:“通过足够多的测试会使代码更有可能通过甚至是非分布测试。”但是他们混淆了概括和推断。一般化不足以解决非分布情况,这只有在模型具有外推能力的情况下才有可能。

如果一个神经网络被训练来识别猫,并且它主要在房子里看到猫,它可能识别野生的猫(泛化),但不能将学习过程外推至房子或人。编码模型也是如此。竞争问题描述有一个非常特殊的风格和格式。如果我们在足够不同的输入上测试 AlphaCode,它不会表现出这么好的性能。神经网络在训练范围内非常擅长概括,但在训练范围之外却不行。

外推不同于一般化,对于任何类型的神经网络都是有问题的。这就是缺货的原因。正如我在之前的一篇文章中所写的:“机器学习系统无法执行这个过程,很大程度上是因为‘独立同分布(iid)’假设。它声称真实世界的数据和训练数据具有相同的分布,这是错误的。”

4.2.3 偏见、公平和代表性

像其他神经网络一样,编码系统将重现训练数据中存在的偏差。这些偏见包括对边缘化少数群体的歧视,通过定型观念的延续,以及使用过时的库,这可能导致性能和安全问题。

这个问题在大型语言模型(编码模型是其中的一种特殊类型)中普遍存在,只要我们继续从互联网上提取数据,这个问题就会一直存在。

4.2.4 安全

除了与过时代码相关的问题,人工智能编码系统可能会帮助恶意行为者以更高的速度生成更高级的恶意软件。

4.2.5 环境影响

大型语言模型消耗大量的计算能力。据估计,训练 GPT 3 号产生的碳足迹大约相当于驾驶一辆新车往返月球

在过去十年中,用于训练神经网络的计算资源急剧增加。从 2012 年到 2018 年,消费金额增长了 30 万倍。这相当于每 3.4 个月翻一番。

编码模型有一个额外的计算开销来源:采样。AlphaCode 要求每个问题有几十万个样本才能达到人类的平均表现。研究人员强调,尽管采样显著提高了解决率——在其他条件相同的情况下——但试图通过这种方式获得更好的结果很快就变得“令人望而却步”

与其他类型的神经网络相比,编码系统的主要优势在于,一旦生成了成功的程序,就可以在常规计算机上轻松、廉价地运行。此外,谷歌关于环境影响的政策的一个特殊性是,该公司购买“与消耗量相等的可再生能源”。

4.2.6 知识产权

知识产权仍然是 GitHub 和微软发布 GitHub Copilot 时提出的一个重要问题。人们过去(现在也是)担心生成的代码侵犯了合法性。

一位用户 GitHub 首席执行官 Nat Friedman 询问 Copilot 生成的代码属于哪种许可——如果代码违反了法律,谁该负责。弗里德曼回答说,“在公共数据上培训 ML 系统是合理使用”,这是只有法官有权声称的事情。即使以前的类似系统是真的,这个问题仍然是开放的。

Flask 的创造者阿明·罗纳彻展示了 Copilot 如何在版权所有的许可下生成代码,比如 GPL(衍生作品在法律上必须在同一许可下)。这使得在商业项目中使用 Copilot 很危险,因为商业项目可能希望获得闭源许可。

DeepMind 的研究人员指出,AlphaCode 的训练数据已经根据许可证进行了仔细过滤。尽管如此,使用在开源许可下授权的代码需要适当地归功于作者——否则,它被认为是剽窃。这只是知识产权和开源许可合规性这个经常模糊的世界中的一个例子。

4.2.7 自动化

对于技术和人工智能,尤其是代码生成系统,一个常见的担忧来源是它们取代人类工人的潜力。自从技术存在以来,它就一直在夺走人类的工作——尽管最终,它创造的工作岗位会比它耗费的多,但受益者很少是最初在职业上受到伤害的人。

随着深度学习系统的出现,人工智能现在能够完成许多狭窄的任务。曾在苹果、微软和谷歌担任高管的人工智能专家李开复认为 40%的工作可能在未来 15 年(他在 2019 年说过这话)。

OpenAI 的首席执行官 Sam Altman 在去年六月做了一个非常规的预测:

他指的是大型语言模型的总体效果。人工智能编码系统没有什么不同。他们可以促进程序员的工作,直到他们变得足够好,以减少人类员工的比例。在供应增加的同时,对人类程序员的需求可能会减少,这将造成供应过剩的局面,压低工资和就业机会。

一个常见的反对理由是,正如 ide 和编译器所发生的那样,程序员的工作可以简单地转向只有人类才能完成的更复杂的任务。我同意这种可能性,但如果我们从技术的历史中学到了什么,那就是总有一些人跟不上快速的进步。我们不应该忘记他们,否则就太晚了。政府必须提供安全网,并建立其他更具雄心的措施,如普遍基本收入,以保护人们免受自动化世界即将到来的现实的影响。

4.2.8 高级人工智能风险

DeepMind 研究人员关于这种风险的说法让我产生了奇异的共鸣:“从长远来看,代码生成可能会导致高级人工智能风险。编码能力可能会导致能够递归编写和自我改进的系统,从而迅速导致越来越先进的系统。”

自该领域诞生以来,专家们一直在警告人工智能推翻人类的可能性。目前还不清楚这是否会发生或何时发生。乐观的人说,再过一二十年,机器的智能可能会超过我们。其他人对这个问题持怀疑态度,因为现实世界中的规划、决策或常识推理等能力发展缓慢。

然而,无论它永远不会发生,还是在遥远的未来,或者我们即将进入一个通往超级智能的不归路,我们都应该为这种可能性做好准备。这就是为什么该领域的一些专家认为这是人工智能提出的最重要的问题。我们如何才能建立一个人工智能,以便一旦它比我们更聪明,它的内部机制——我们希望人工智能的重新设计技能仍然无法达到——保持它与我们的价值观和福祉一致?

这个问题不仅从科学的角度来看很难——将“价值”或“福祉”的概念转化为人工智能的数学本质是极其困难的——而且从伦理的角度来看也是如此。我们如何定义价值观,才能不让一个人掉队?有没有可能定义一套我们共有的普世价值,在一个由机器主宰的世界里确保最低限度的福祉?

我可以就这个话题写几篇文章。结论应该是,如果更先进的人工智能系统真的有可能自我改进,那么我们应该在构建未来的人工智能编码系统时考虑这一点(而不仅仅是作为论文结尾“风险”部分的一个注释)。否则,当我们争论这种可能性的存在时,系统将会自我复制。

5.接受与批评

到目前为止,我已经审查并评论了 DeepMind 的 AlphaCode 论文(除了一些高度技术性或不太有趣的部分)。本部分旨在包括和浓缩竞争性编程和人工智能领域专家的观点、见解和思考。

这个想法是将 AlphaCode(第 1-4 节)的结果和意义的客观分析与一些主观的外部分析结合起来。它将让你更好地了解什么是 AlphaCode,它有什么功能,以及它的局限性,还有人们对它的看法,以及他们如何看待它在更广泛的社会背景下的影响。

5.1 从另一个角度看统计数据

Horace He 重新审视了 AlphaCode 的结果,并对其进行了透视。我们知道统计数据很容易被修饰,这取决于你如何看待它们——他对 AlphaCode 结果进行了去修饰。

为了评估何的说法,我检查了三个 DeepMind 账户( SelectorUnlimitedWaggleCollideAngularNumeric )上每场比赛的 AlphaCode 提交内容。

我发现他基本上是对的。

以下是综合三个帐户提交的结果(div#表示问题的难度,div 1 最难,然后是 div 2,依此类推):

  • 竞赛 1591:已解决 2/6 —估计排名:44.3%(第 2 部分)
  • 竞赛 1608:解决了 1/7 —估计排名:46.3%(第 1 部分+第 2 部分)
  • 竞赛 1613:已解决 3/6-估计排名:66.1%(第 2 部分)
  • 大赛 1615:解决了 3/8 —估计排名:62.4% (-)。
  • 竞赛 1617:解决了 1/6 —估计排名:73.9%(第 2 部分)
  • 竞赛 1618:解决了 4/7 —估计排名:52.2%(第 3 部分)
  • 竞赛 1619:解决了 3/8-估计排名:47.3%(第 3 区)
  • 竞赛 1620:已解决 2/7 —估计排名:63.3%(第 2 部分)
  • 竞赛 1622:已解决 2/6 —估计排名:66.2%(第 2 部分)
  • 竞赛 1623:已解决 2/5 —估计排名:20.9%(第 2 部分)

AlphaCode 总共解决了 66 个问题中的 23 个。这是 34.8%的解决率,与 DeepMind 强调的 54.3%的结果形成鲜明对比。

排名百分比(AlphaCode 以上的参与者数量)对解决率说明不了什么。关键隐藏变量不会反映在排名值上(例如,问题有多难,或者解决了多少字母代码)。解决 7 个中的 1 个(竞赛 1608)使其处于参赛者的上半部分(46.3%),而解决 6 个中的 3 个(竞赛 1613)仅使其达到 66%的百分位数。

此外,问题按难度排序,对于每场比赛,AlphaCode 几乎总是解决最简单的问题(即,如果 AlphaCode 解决了三个问题,它们是 A、B 和 C)。

在查看统计数据时,重要的是从不同的角度来全面了解正在发生的事情。与以前最先进的模型相比,AlphaCode 的结果是疯狂的。该系统本身是代码生成系统的巨大飞跃。然而,它可能没有 54.3%的平均排名所暗示的那么好。

5.2 人类水平还差几光年

魁北克人工智能研究所 Mila 的成员 Dzmitry Bahdanau 和代码力量专家 T1(约 2200 ELO)在关于字母代码能力的讨论中发表了自己的意见。他客观地肯定了 AlphaCode 处于普通人类程序员的水平——我们已经在许多科技 新闻 媒体的标题中看到了这一点。

Bahdanau 指出,“许多参与者是高中生或大学生,他们正在磨练解决问题的技能。”这可以解释一些比赛中高排名和低解决率之间的差距——如果大多数参与者都在学习,预计 AlphaCode 会超过他们,即使它整体上做得不是很好。

他提到时间限制是这些比赛中的一个“关键难点”。人类有 3 个小时来解决 5-8 个问题,而 AlphaCode 利用其超人的计算能力来克服这一限制。AlphaCode 被限制只能提交 10 个样本,以模仿编程竞赛的特质,但这并不妨碍该系统抽取多达 100 万个样本。人类参与者在解决问题时感受到的时间压力,或者与意外错误相关的问题对于 AlphaCode 来说并不存在。

他还提到,竞争性编程通常不太依赖创造力。“从我的经验来看,这确实需要写很多样板代码,”他说。"许多问题涉及标准算法的部署."尽管 DeepMind 得出结论,AlphaCode 不会从训练数据中复制(至少不会比人类复制更多),但 Bahdanau 认为,更改变量名称将不再是复制。他建议更彻底地研究这个潜在的问题,例如使用“使用神经表征找到的最近邻解决方案”

他总结说,AlphaCode“在击败人类方面不是 AlphaGo,在彻底改变整个科学领域方面也不是 AlphaFold。”

5.3 狭义人工智能对广义人类

科技博客 TechTalks 的作者本·迪克森(Ben Dickson)在他的文章“什么是 DeepMind 的 AlphaCode,什么不是”中分析了 AlphaCode 的能力。他强调的一个关键观点是,我们可能会无意识地将“AlphaCode 在编程比赛中达到了具有竞争力的性能水平”这句话与一个更广泛、更雄心勃勃的说法等同起来:“AlphaCode 与普通人类程序员一样好。”

他说,谬误在于我们“将狭隘的人工智能与人类解决问题的一般能力相比较。”例如,一个伟大的棋手很可能发展了其他相关的能力,比如“计划和策略”相反,alpha zero——目前最好的人工智能棋手——只知道下棋。它永远无法像人类那样将这些技能推广到其他真实世界的场景中。

“竞争性编程也是如此。一个在编码挑战中达到竞争水平的人类程序员已经花了数年时间学习。他们可以抽象地思考问题,解决更简单的挑战,编写简单的程序,并表现出许多其他被认为是理所当然的技能,这些技能在编程竞赛中不会得到评估。”

纽约大学人工智能教授加里·马库斯(Gary Marcus)与这些思考不谋而合。他告诉美国消费者新闻与商业频道“你应该把[AlphaCode]看作是程序员的助手,就像计算器曾经帮助会计一样……它不会取代真正的人类程序员。我们还需要几十年的时间。”

如果一名人类开发人员申请一份工作,评估将围绕硬编程技能——人工智能编码系统最有可能擅长的技能。但这只是因为我们人类拥有的所有其他能力,尤其是从事开发人员工作所需的能力,都被认为是理所当然的。这些是人类程序员的基线,但不是像 AlphaCode 这样的系统。

如果一个人类程序员有一个问题,并且已经尝试了他们手中的一切来解决它,它可以简单地说“嘿,你能帮我吗?”给队友。这是一个人技能组合中的终极工具,AlphaCode 无法与之竞争。

5.4 猴子打哈姆雷特

纽约大学计算机科学教授 Ernest Davis 在《脸书邮报》上评论了 AlphaCode。他说,这是“令人印象深刻的成就”。“AlphaCode 生成的代码通常有 20 行长,错综复杂,非常聪明,绝不是千篇一律的。英语规范和代码特征之间的关系是相当间接的。”这加强了字母代码语言理解和编码能力是一流的结论。

像他的同事加里·马库斯一样,他不同意“[AlphaCode]和普通人类程序员一样好”的论断 AlphaCode 为每个问题生成了多达一百万个样本,而且几乎没有一个通过测试,即使是输入了问题描述的示例测试。“这里有相当多的猴子在模仿哈姆雷特。AlphaCode 已经在很大程度上成功地训练了猴子,但它们仍然需要大量的猴子。正如他指出的,这种方法没有什么“不公平”,但是“它有两个后果。”

第一个结果是,可以合理地预期,随着程序的长度,所需的样本数按指数规律增加,尽管在实验测试之前,人们不能肯定这一点这是有道理的,如果 AlphaCode 尝试许多组合,其中+99%是错误的,它将需要越来越多的样本来编写线性更长的程序。

假设 AlphaCode 使用 100K 个样本解决了问题 A,其中只有 1000 个(1%)通过了示例案例。假设这 1000 个程序的平均长度为 20 行。现在,让我们说 AlphaCode 必须解决问题 B,它更复杂,需要大约 40 行程序。要找到 1000 个 40 行的正确解,需要找到 100K 个直到第 20 行都是正确的样本!求解 b 需要产生 1000 万个样本。

当然,其他变量使这一分析更加复杂,但正如戴维斯所建议的,这是一个足够合理的近似值。

“第二个结果是,AlphaCode 的成功完全依赖于提供特定的输入和输出,它可以使用这些输入和输出进行过滤。”这是一个类似迪克森的论点。人类有更广泛的智力,并且知道如何克服没有示例测试的限制。“相比之下,”戴维斯说,“如果没有提供具体的例子,AlphaCode 会完全不知所措;成功率会下降大约 100 倍。”

6.结论

DeepMind 又做到了。与 AlphaFold、AlphaZero 和 Gopher 一样,AlphaCode 在人工智能领域达到了一个新的最高水平。这是唯一一个在编程竞赛中达到竞争水平的代码生成器系统。与类似的条件相比,它优于以前的系统。

AlphaCode 可以支持多种应用,这些应用既有好处也有风险。在生产中部署类似 AlphaCode 的系统之前,对这些进行充分的评估是至关重要的。此外,尽管在性能和工程方面都有惊人的突破,但我们应该在正确的视角下将其与人类进行比较。AlphaCode 不具备人类级别的编程技能,因为它还是一个狭义的智能。

随着系统的不断改进,AI 和人类在编码环境中的差距将会缩小。我将继续为您带来像这篇文章一样的经过消化的文章,这样您就可以了解我们的现状以及您对未来的期望。

如果你已经读到这里,可以考虑订阅我的免费双周刊 【明天的思想】 !每两周一次关于人工智能和技术的新闻、研究和见解!

您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃

DeepSpeed 深潜—推理的模型实现(MII)

原文:https://towardsdatascience.com/deepspeed-deep-dive-model-implementations-for-inference-mii-b02aa5d5e7f7

深入了解来自 DeepSpeed 的最新开源库

由作者创建的图像-使用稳定扩散创建

这是怎么回事?

DeepSpeed 团队最近发布了一个新的开源库,名为推理的模型实现 (MII),旨在使强大模型的低延迟、低成本推理不仅可行,而且易于访问。你可以在他们的博客文章中读到所有相关内容。

当我开始探索 MII 图书馆时,我意识到那里有许多其他 DeepSpeed 技术的参考资料,例如零推理和 DeepSpeed 推理。因此,这篇博文的目的不是重述 DeepSpeed 的博文,我认为该团队在描述技术和潜在好处方面做得很好。相反,我将致力于解释一些底层技术和相关术语,并指出我在深入研究 MII 图书馆时发现的一些“问题”。这基本上是我希望在四周前开始钻研这个图书馆时就能拥有的博文😅

为什么这很重要?

没有一个星期会有新的、令人兴奋的人工智能模型发布。在写这篇博文的时候(2022 年 11 月 17 日),本周的 AI 模型是 Meta 的 Galactica 模型。这些大型模型中有许多是开源的(BLOOM,OPT ),理论上每个人都可以访问。当然,挑战在于这些模型太大,客户很难部署它们。当他们设法部署它们时,他们面临着推理延迟和成本方面的挑战。

在过去的几年中,DeepSpeed 做了很多工作来克服这些挑战,现在他们将努力整合到一个库,DeepSpeed-MII。他们的目标是实现强大模型的低延迟、低成本推断,不仅可行,而且容易获得。就在上周,2022 年 11 月 9 日,他们宣布他们能够生成稳定扩散的图像,每张图像不到一秒。像这样的努力将使这些模型的使用民主化,并最终允许每个人运行这些模型🤗

DeepSpeed 术语

首先要注意的是,DeepSpeed-MII 实际上是现有 DeepSpeed 技术的集合,特别是 DeepSpeed 推理和零推理。当我开始了解更多关于 DeepSpeed 的库时,我有点困惑,所以我想首先概述一下在使用 DeepSpeed 时可能会遇到的所有术语:

:于 2022 年 9 月推出,作为 零无穷大 (从 2021 年 4 月开始)的继任者——它通过在 CPU 或 NVMe 内存中托管模型权重来适应和优化 GPU 上的零无穷大技术,从而在 GPU 中不托管权重:

图片作者。来源:https://www.deepspeed.ai/2022/09/09/zero-inference.html

deep speed-推论:2021 年 3 月推出。这种技术与 ZeRO 技术没有关系,因此并不专注于托管不适合 GPU 内存的大型模型。相反,它引入了几个特性来有效地服务于基于 transformer 的 PyTorch 模型,比如定制推理的内核。你可以在他们的博客文章中了解更多。

所以,总结一下:

  • 零推理 专为要求 GPU 加速但缺乏足够的 GPU 内存来托管模型的推理应用而设计。此外,零推理针对面向吞吐量并允许大批量的推理应用程序进行了优化。
  • deep speed-Inference另一方面,将整个模型放入 GPU 内存(可能使用多个 GPU)更适合延迟敏感或批量较小的推理应用。

DeepSpeed MII 将这两种技术整合到一个框架中。然而,如上所述,它们用于不同的目的,因此不能一起使用:

图片由 authour 提供。来源:https://github . com/Microsoft/deep speed-MII/blob/v 0 . 0 . 3/MII/deployment . py

代码深潜

在这一节中,我们将浏览一下 MII 的代码,从中提取一些有用的信息。这个库还很年轻(当前版本是 0.0.3 ),有些东西还没有完全文档化。例如,在上面的代码片段中,我们看到 MII 结合了深度速度推理和零推理。

另一个有用的信息是,MII 的目标是让用户的生活更轻松。根据型号类型、型号大小、批量大小和可用的硬件资源,MII 自动应用 DeepSpeed-Inference 的一套适当的系统优化,以最大限度地减少延迟和最大限度地提高吞吐量。

例如,如果我们想要运行一个 Eleuther 模型,它将选择一组适当的配置值:

图片作者。来源:https://github . com/Microsoft/deep speed-MII/blob/v 0 . 0 . 3/MII/models/load _ models . py

最终,优化和加速模型的代码与我们单独使用 DeepSpeed-Inference 时使用的代码是相同的:

图片作者。来源:https://github . com/Microsoft/deep speed-MII/blob/v 0 . 0 . 3/MII/models/load _ models . py

知道这一点很好——这意味着我们不想通过 gRPC 服务托管我们的模型,我们仍然可以通过使用 DeepSpeed-Inference 获得相同的效率。不过,在未来,我希望我们能在 MII 内部选择使用 gRPC 或不使用 gRPC 的优化。

是时候进行一些动手实验了

为了确保 MII 和 DeepSpeed-Inference 提供相同的加速,我们可以运行一些测试:我们可以首先在完全没有 DeepSpeed 加速的情况下运行一个文本生成任务,然后比较使用 DeepSpeed-Inference 和 MII 时延迟如何变化。

我们将在一个配备了 T4 (16 GB GPU 内存)的实例上使用 BLOOM-560M 模型进行这个实验。根据 MII 的博客文章,当使用 MII-公共选项时,我们应该看到以下收益:

图片作者。来源:https://www.deepspeed.ai/2022/10/10/mii.html

这些测试的代码可以在这个 GitHub repo 中找到。

基线-无深度速度的拥抱面管道

我们可以通过运行 BLOOM-560M 模型的标准拥抱面管道来获得基线。

我们看到结果大致符合 MII 博客帖子的预期:

作者图片

深度推理

现在我们可以使用 DeepSpeed-Inference 框架运行相同的测试。为此,我们设置了与上面相同的拥抱面部管道,然后用优化的 DeepSpeed 模型替换底层模型:

一旦管道准备就绪,我们可以运行与上面相同的测试,并看到 4 毫秒的显著改进:

作者图片

广播级录象机的型号之一

设置 MII 推理机也很容易,我们只需按照 MII GitHub repo 中的说明操作即可:

一旦生成器启动并运行,我们就可以再次生成新令牌,并测量生成它们所需的时间。生成器的结果是一个原型 MultiString 对象,可能源于我们正在与一个 gRPC 服务器进行交互。我确信有很好的方法来解析这个回复,但是我将通过一点正则表达式的魔力来快速地完成它:

现在我们计算相同的指标,看看结果:

作者图片

正如预期的那样,我们确实看到了与 DeepSpeed-Inference 完全相同的加速(4 毫秒)🤗

结论

我对这一举措感到非常兴奋。我认为,对 BLOOM、OPT 等大型模型的民主化访问将是 2023 年的主要挑战之一,而 MII 是应对这一挑战的一个伟大项目。它仍然是一个非常年轻的库(创建于 2022 年 10 月,当前版本是 0.0.3)。但是看起来这个库是活跃的,社区正在使用和测试它,并提出了 GitHub 的问题。希望随着时间的推移,我们将看到这个库的成长和改进——我肯定会尽我的一份力量😊

默认的拥抱脸模型可能就是“普通”图像分类所需要的全部

原文:https://towardsdatascience.com/default-hugging-face-models-are-probably-all-you-need-for-vanilla-image-classification-9d0ee19c85fa

在土地覆盖分类任务中探索作为特征提取器和转移学习器的拥抱面部中枢上的预训练图像模型

代码回购:[ 链接,笔记本:[ 链接

图一。来自著名的切萨皮克保护区土地覆盖数据集的卫星图像(NAIP 卫星)的四种表示法。

介绍

在这篇文章中,我将探索表征学习中的一种常见做法——使用预训练的神经网络作为学习的特征提取器。具体来说,我感兴趣的是检查在这些提取的神经网络特征上训练的简单模型的性能如何与通过迁移学习启动的微调神经网络进行比较。目标受众包括其核心的数据科学家,以及更普遍的对地球观测、计算机视觉和机器学习感兴趣的任何人。

向前跳一点… 下面的结果表明,在提取的神经网络特征上训练的 scikit-learn 模型产生的性能与在相同的预训练权重下微调的完整网络几乎相当(平衡精度下降-3%到-6%)。

背景

今天,全世界的微软每年都会发布数千个预先训练好的神经网络。而且,这些模型变得越来越有性能和可访问性。有了这么多开源的模型检查点,神经网络作为 AI/ML 的中心焦点的演变就不太令人惊讶了。考虑到各地的人们都听说过达尔-E-2稳定扩散——可以将文本提示转化为图像/艺术的神经网络,据报道,稳定扩散已经被超过 1000 万用户下载许多人不知道的是,这些技术今天的存在在很大程度上归功于被称为表征学习的统计学子领域的进步。

“21 世纪 20 年代看起来像是在 ML 中实现表征学习承诺的时代。有了在特定领域(有监督或无监督)训练的模型,我们可以在处理输入时使用它们的后期激活作为它们输入的表示。表征可以以多种方式使用,最常见的是用作下游模型的直接输入,或者用作与多种类型的模型(文本和视觉、GNN 和文本等)共同训练共享潜在空间的目标。)." —凯尔·克拉宁**【1】

让我们来检验一下这些说法…

数据集详细信息

下面使用的图像数据集来自 2013/2014 年切萨皮克保护区土地覆盖项目【2】。它由国家农业图像计划(NAIP)卫星图像组成,具有 4 个波段的信息,分辨率为 1 平方米(红色、绿色、蓝色和近红外)。原始地理空间数据最初跨越 6 个州的 100,000 平方英里:弗吉尼亚、西弗吉尼亚、马里兰、特拉华、宾夕法尼亚和纽约。首先对其进行二次抽样,以获得 n =15,809 个大小为 128 x 128 像素的独特斑块和尽可能多的土地覆被标签*。当检查示例补丁(见图 1)时,1 平方米的分辨率显得相当精细,因为图像中的结构和对象可以以较高的清晰度进行解释。

*旁注:原始切萨皮克保护区土地覆盖数据集包括标注掩膜,用于分割而非分类。为了改变这一点,我只保存了在采样地理空间数据时单个类出现频率至少为 85%的补丁。然后,过度表示的类被记录为该补丁的唯一标签,导致简化的 5 路分类问题。

实验中使用的 5 种土地覆盖类型定义为…

  1. 水域:开阔水域,包括池塘、河流和湖泊
  2. 树冠和灌木:木本植被,包括乔木和灌木
  3. 低植被:高度小于 2 米的植物材料,包括草坪
  4. 不毛之地:没有植被的天然土质区域
  5. 不透水表面:人造表面

经过检查,数据集似乎具有许多有趣的特征,包括季节变化(例如,树叶)、噪声(例如,偶尔的云层覆盖)以及跨 6 个州的分布变化。少量的“自然”噪声有助于使这个有些简化的分类任务变得更加困难,这是有益的,因为我们不希望监督的任务过于琐碎。

使用美国各州作为拆分机制来生成训练集、验证集和测试集。来自宾夕法尼亚州的补丁被选择用于测试集( n = 2,586,16.4%的数据),来自特拉华州的补丁被选择用于验证集( n = 2,088,13.2%的数据),其余的被用于训练集( n = 11,135,70.4%的数据)。

最后,作为一个整体,数据集在一定程度上存在明显的类别不平衡:贫瘠(49/15,809)和不透水表面(124/15,809)严重不足,而树冠和灌木(9,514/15,809)严重过量。低植被(3,672/15,809)和水(2,450/15,809)相比之下更加平衡。由于这种标签的不平衡,我们在下面的实验中使用平衡的精确度。该指标采用每个类别的个体准确性的平均值,从而平等地加权每个类别,而不考虑大小。

see: torchgeo.datasets

习得特征

一般来说,学习到的特征可以被定义为那些源自黑盒算法的特征。通过提取图像表示的学习特征,你经常委托 CV 社区中其他人的工作——最初训练黑盒的团队。例如,人们可以从神经网络中提取学习到的特征,这些神经网络在预训练(如 ImageNet)中使用 keras、pytorch 和 transformers 等包看到了大型基准数据集。无论是无人监督还是有人监督,学习到的特征通常是下游任务的优秀表现。所做的假设是模型的权重以稳健的方式被预先训练。幸运的是,你可以委托谷歌/微软/脸书来做这件事。

在某些情况下,原始图像在输入神经网络时会经历几层连续的变换,其中每个隐藏状态层都会从原始图像中提取新信息。将图像输入网络后,可以直接提取隐藏状态或嵌入作为特征。通常的做法是使用最后一个隐藏状态嵌入作为提取的特征,即监督任务头之前的层。

在这个项目中,我们将研究两个预训练模型:微软的双向编码器图像转换器(BEiT) [3]和脸书的 ConvNext 模型[4]。BEiT-base 和 ConvNext-base 是拥抱人脸图像分类中最受欢迎的两个检查点,在初始测试中与其他选项相比表现良好。由于提取的隐藏状态通常具有比 1 x n 更高的维度,通常的做法是沿着较小的维度取平均值,以每幅图像 1 x n 的嵌入结束。下面,我们从基本 BEiT 得到 1 x 768 大小的嵌入,从基本 ConvNext 得到 1 x 1024 维度的嵌入。为了可视化,嵌入物被任意调整大小为矩形,这揭示了其中的一些不同模式。

图二。数据集中四个随机示例的两个已学习特征表示。顶行对应 BEiT Vision Transformer 嵌入件,底行对应 ConvNext 模型嵌入件。这四个斑块分别来自水体(左)、树冠和灌木(中左)、低矮植被(中右)和不透水表面(右)。请注意,这些是从原始的 1 x n 嵌入调整的,以便将它们可视化为矩形面片。

让我们看看数据是如何在已学习的特征空间中可视化呈现的。为此,我们将对 n 个图像嵌入的集合执行 PCA,以将它们转换到二维空间。然后用分类标签着色绘制。

图三。BEiT Vision Transformer 嵌入的前两个主要组件,将数据很好地聚类到不同的组中。请注意水、低矮的植被和树冠之间的强烈分隔。虽然看起来有点困难,但较浅的绿色点和黄色点(贫瘠和不透水的表面)与地块下部中心的低植被重叠。理想情况下,这些应该与狼群的其他部分分开。因此,在下面的模型中,我们预计这些类的性能会比其他类差。

图 4。conv next 嵌入的前两个主要组成部分,它对数据的聚类几乎与上面的 BEiT Vision Transformer 嵌入一样好。请注意低矮的植被和树冠之间的强烈分离。然而,与图 3 相比,这里的水类差别更小。

see: transformers.BeitModel/ConvNextModel

建模

如果你去看 Kaggle 竞赛笔记本,你会发现今天图像分类最常见的做法是使用预先训练好的神经网络进行迁移学习和微调。在此设置中,首先将权重加载到网络中(迁移学习),然后在感兴趣的新数据集上更新它(微调)。后一步通常运行几个时期,并且具有小的学习权重,以便不会偏离原始权重太远。然而,与使用相同的模型作为特征提取器相比,迁移学习和微调过程通常涉及更多的时间和更多的计算。

下面的前半部分模型是用学习功能和 scikit-learn 模型训练的。我使用了以下软件包来促进整个流程:从特征提取(transformers)到模型训练(sklearn)再到超参数优化(optuna)。对于超参数优化,我搜索了各种逻辑回归和前向神经网络(FFNN ),进行了 10 次随机试验,结果表明,一个隐藏状态的维数为 175-200 的 FFNN 通常是最佳选择。

然后训练迁移学习和微调的神经网络,以便与这些学习的特征模型进行比较,这些特征模型包括模型的第二部分。我使用 transformers 包来微调 BEiT 和 ConvNext 基本模型,检查点与上面的完全相同。使用相同的预训练权重,以便在实验中更接近地比较“苹果与苹果”。

此处抱抱脸关于图像分类的优秀教程。

see: optuna, sklearn, transformers

模型评估

对于模型评估,我选择在保留的测试集上检查平衡精度、单个类精度和混淆矩阵。混淆矩阵显示了模型出错的地方,有助于解释。每行代表已知存在于给定类中的示例(基本事实),而每列代表由模型分类的示例(预测)。行加起来是事实的数量,列加起来是预测的数量。

—x — x — x—

模型 1,拜特嵌入+ sklearn FFNN:

平衡精度… 79.6%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |   ** 64** |      0 |          2 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     1 |   **1987** |          3 |      1 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      3 |        **457** |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     2 |      0 |         14 |      **5** |       3 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          6 |      2 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:97.0%,树冠和树木:99.7%,低植被:99.1%,贫瘠:20.8%,不透水表面:81.4%。

Beit embeddings 模型总体表现第三好

— x — x — x —

模型 2 ,ConvNext 嵌入+ sklearn FFNN:

平衡精度… 78.1%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **62** |      0 |          4 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     2 |   **1982** |          6 |      2 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      3 |        **457** |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     1 |      1 |         17 |      **4** |       1 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          8 |      0 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:93.9%,树冠和树木:99.5%,低植被:99.1%,贫瘠:16.6%,不透水表面:81.4%。

ConvNext 嵌入模型总体表现最差

— x — x — x —

模型 3 ,微调 BEiT 神经网络:

平衡准确度… 82.9%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **64** |      0 |          2 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     0 |   **1986** |          5 |      1 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     2 |      3 |        **455** |      0 |       1 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     0 |      0 |         13 |      **9** |       2 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     1 |      0 |          6 |      1 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:97.0%,树冠和树木:99.7%,低植被:98.7%,贫瘠:37.5%,不透水表面:81.4%。

经过微调的 BEiT 型号总体表现次优

— x — x — x —

模型 4 ,微调的 ConvNext 神经网络:

  • 平衡精度… 84.4%
+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **65** |      0 |          1 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     0 |   **1978** |         12 |      2 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      2 |        **457** |      0 |       1 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     0 |      0 |         13 |     **11** |       0 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          7 |      2 |      **34** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:98.5%,树冠和树木:99.3%,低植被:99.1%,贫瘠:45.8%,不透水表面:79.1%。

微调后的 ConvNext 型号总体表现最佳🚀🤖

— x — x — x —

建模流程/限制的改进

建模管道可以在许多方面得到改进,包括接下来讨论的那些。模型在贫瘠的例子的分类中遭受最大的痛苦,所以如果我能做一个改变的话,我会从增加更多这种类型的类开始。实际上,模型更像 4 路分类器,因为空类的性能很差。另一个改进可能是在超参数优化中使用交叉验证;然而,交叉验证将需要更长的时间来运行,并且对于这个实验来说感觉是多余的。

输出模型的一般化限制包括对其他类别类型、其他分辨率和其他条件(新对象、新结构、新类别等)的影像的分类性能恶化。).我已经将经过微调的 ConvNextBEiT 推送到拥抱脸进行托管推理,在这里,人们可以通过加载图像和/或运行每个图像中配置的默认设置来测试模型的可推广性。

项目学习

  1. Python 包领域的知识至关重要。请参见下面的灰色块,了解这里使用的各种库。
  2. 可视化图像特征之间的变化有助于更深入地理解数据集中的信号。
  3. 预训练的嵌入与更简单的模型配对可以表现得几乎和微调的神经网络一样好。
  4. 拥抱脸不仅对自然语言处理很棒,对计算机视觉也很神奇!

结论

这些结果和学习对于今天 CV 中神经网络的状态暗示了什么?让我们倒回去。2017 年,特斯拉自动驾驶部门的前总监 Andrej Karpath 写了一篇现在很有名的博文,内容是关于从旧学校工程到新学校深度学习的转变,他将其称为“软件 2.0”【5】。从这个角度来看,神经网络不是“你机器学习工具箱中的另一个工具”。相反,它们代表了我们开发软件方式的转变。我相信今天的软件 2.0 和五年前一样重要。只要看看 NLP、CV 中可用的开源神经网络的数量,以及来自 AI/ML 顶级研究实验室的数量。对于从业者来说,这是令人兴奋的事情…

引文

[1] K. Kranen (2022 年),21 世纪 20 年代看起来像是在 ML 、LinkedIn 中实现的表征学习承诺的时代。

2切萨皮克湾项目办公室(2022)。2017/18 年切萨皮克湾流域 1 米分辨率土地覆盖数据集。由佛蒙特大学空间分析实验室、切萨皮克保护区和美国地质调查局开发。[2022 年 11 月 15 日],[ 网址

  • 数据集许可:此处使用的数据集对所有人公开,没有限制。更多信息见这里这里

[3]鲍,董,李,魏等(2021).贝特:图像转换器的预训练。更正,abs/2106.08254。https://arxiv.org/abs/2106.08254

[4]刘,赵,毛,h,吴春英,费希滕霍费尔,t .达雷尔,谢,S. (2022)。21 世纪 20 年代的 ConvNet。更正,abs/2201.03545。https://arxiv.org/abs/2201.03545

[5] A. Karpath (2017),软件 2.0 ,中等。

[6]南尼,l .,吉多尼,s .,&布拉纳姆,S. (2017)。用于计算机视觉分类的手工与非手工特征。模式识别71 ,158–172。doi:10.1016/j . pat cog . 2017 . 05 . 025

— x — x — x —

附录

非习得特征

CV 中的非学习特征可以被认为是那些由图像手工制作的特征[6]。给定问题的最佳非学习特征通常依赖于区分信号在数据集中位置的知识。在提取非学习特征之前,让我们先在 RGB 空间中绘制一些随机补丁。

图 6。数据集中三个斑块的可见光谱 RGB 特征:不透水表面 id 3(左)、树冠和灌木 id 4(中)和低植被 id 5(右)。

我们将研究主成分分析(PCA)作为第一个非学习特征。PCA 是一种降维技术——在这里,我们使用它从 128 x 128 x 4 的图像移动到 1 x n 的向量。PCA 变换数据集的大小 n 是用户指定的,并且可以是比数据的原始维度小的任何数字。在幕后,该算法使用特征向量(数据中的传播方向)和特征值(方向的相对重要性)来寻找与原始图像保持最大差异的一组基。一旦计算完成,PCA 可以用来将新图像转换到低维空间和/或在二维或三维空间中可视化图像(见图 3 和图 4)。

在下面的例子中,保持 n =3000 维的 PCA 导致 95%的维数节省,同时保留了来自原始图像的几乎所有信号。为了使 PCA 可视化,我颠倒了操作,并将示例绘制成 128 x 128 像素的图像。

图 7。数据集中三个斑块的 PCA 特征(保留 3000 个维度):不透水表面 id3(左)、树冠和灌木 id4(中)和低植被 id 5(右)。

让我们看看另一个老派的特征:梯度直方图(猪)。为了计算 HOG,首先计算图像的梯度(变化强度)和方向。然后,图像被分割成多个单元,其中方向被分层成直方图仓。然后,在一个单元中的每个像素处,我们查找它的方向,在直方图中找到相应的 bin,并将给定的值加到它上面。然后在图像的各个单元上重复这个过程。然后,

看看 HOG 在这里做的很酷的事情:

图 8。数据集中三个斑块的 HOG 特征:不透水表面 id 3(左)、树冠和灌木 id 4(中)和低植被 id 5(右)。

虽然这些手工制作的特征作为数据集的第一遍可视化进行检查是有趣的,但事实证明它们对于我们的监督建模目的来说并不太好。这里,最初的测试表明,从 HOG 和 PCA 特征构建的模型相对于从下面探索的学习特征训练的模型在平衡准确性方面发生了显著下降(PCA 下降 35%,HOG 下降 50%)。

see: skimage.feature.hog, sklearn.decomposition.PCA

定义数据规程中的角色分类

原文:https://towardsdatascience.com/defining-a-taxonomy-of-roles-in-the-data-discipline-5c801c5d9d7

他们的工作和主要职责

布鲁克·卡吉尔在 Unsplash 上的照片

我们生成的数据量呈指数级增长,这为许多机会打开了大门。“数据是新的石油,”你会经常在现代商业活动中听到,而企业已经在行动了。几个工作角色已经崛起,推动各行各业的数据革命。

对许多人来说,数据学科的头衔并没有你想象的那么重要。不同公司职位相同的人的日常工作流程可能会有很大不同——但我不会完全忽略他们。我们慢慢地但肯定地认识到,数据领域不存在独角兽,没有一个人能够在组织中有效地使用数据。

因此,公司已经将责任分解成更专业化的角色。在你职业生涯的开始阶段,了解每个角色的一般职责是非常重要的,因为这将让你深入了解为适应某个角色而必须掌握的必要工具和技能。

说了这么多,让我们来看看涉及的一些主要角色:

数据分析师

数据分析的最终目标是提出一个商业问题的解决方案:他们通过发现可用于制定战略决策的数据模式来寻求提高组织的效率和绩效。因此,数据分析师使用数据来讲述故事,帮助企业基于数据做出更明智的决策。

数据分析师还应具备出色的跨各种媒介(包括视觉、书面和口头)的沟通技能,因为这是报告其结论所必需的。

关键职责

  • 与其他团队成员合作,改进数据收集过程和质量。
  • 创建仪表板和报告。
  • 执行数据分析并报告可以改进的领域的结论,以提高组织或项目的效率。
  • 构建和维护自动化数据流程。
  • 生成并跟踪业务 KPI。
  • 进行数据审计。

数据科学家

数据科学的最终目标是从数据中产生商业洞察力:当前数据用于发现机会。因此,数据科学家应该对企业面临的挑战有很好的理解,并能够基于数据驱动的方法提供解决方案。

由于他们的跨学科专业知识,他们很可能使用机器学习、统计学和数据挖掘衍生的工具和技术处理项目的所有方面,包括数据采集、分析和不同类型数据(即结构化或非结构化数据)的解释。

主要职责

  • 与主题专家(SME)密切合作,确定问题并使用数据提出解决方案。
  • 利用机器学习工具和各种统计技术来解决问题。
  • 数据清理。
  • 源数据来解决业务问题。
  • 跨业务团队、工程团队和产品团队等多个团队的协作。

学习资源:Data camp 上有Python R 职业轨迹的数据科学家是一个很好的起点。

数据工程师

数据工程师构建数据管道来准备和转换原始和非结构化数据。管道通常由数据的收集(可能来自各种来源)、处理和存储组成。他们的大部分时间都花在了确保这些管道足够健壮、可靠和值得信赖来输送。

数据工程的最终目标是使数据可访问。换句话说,他们获得了使数据科学和机器学习成为可能的商品:有些人甚至认为他们是数据团队中最重要的成员。

主要职责

  • 设计、开发和维护数据系统和管道。
  • 数据采集。
  • 分析和组织原始数据。
  • 提高数据可靠性和质量。

学习资源:Data camp 上的数据工程与Python职业轨迹是一个很好的起点。你可能还想通过 Coursera 上的 IBM 数据工程师职业 证书来扩展你的学习。

数据架构师

"数据架构师 设计并构建数据模型,以满足首席数据架构师 定义的业务战略数据需求。在这一级别,您将:根据数据策略,为数据的升级、管理、退役和归档进行设计、支持并提供指导。 " [ 来源 : GOV.UK

  • 确定数据源(内部和外部)并提出数据管理计划
  • 开发和实施整体组织数据策略。
  • 与跨职能团队和利益相关者合作,确保数据系统的平稳运行。
  • 管理端到端数据架构。
  • 审核数据管理系统,并在必要时对其进行改进。

机器学习工程师

机器学习工程的最终目标是将数据转化为产品。这个角色的产生是因为需要在数据科学家的工作(例如,分析和建模)和软件产品的世界(例如,健壮的系统工程)之间架起一座桥梁。

因此,机器学习工程通常被认为是软件工程的一个子领域:除了机器学习要求之外,机器学习工程师和软件工程师的生活方式非常相似——这意味着他们应该是熟悉 ide、GitHub 和 Docker 等工具的熟练程序员。

主要职责

  • 设计和构建机器学习系统。
  • 构建自动化管道来部署机器学习模型。
  • 适当测试机器学习系统并监控其性能。
  • 与数据工程师之类的人一起构建数据和管道模型。

学习资源 : 如何成为机器学习工程师 可以作为学习轨迹参考。

MLOps 工程师

MLOps 是关于将 DevOps 原理应用于机器学习系统的新热潮。因此,MLOps 工程师的重点通常更多地是将机器学习模型部署到生产中,而不是构建它们。

它们以类似于 DevOps 支持软件工程师的方式支持机器学习工程师:工程师将创建软件,Ops 将提供基础设施并确保软件可靠运行。因此,我们可以说 MLOps 工程师负责构建机器学习模型时发生的所有活动。

主要职责

  • 建立和维护 MLOps 管道。
  • 设计和实施云解决方案。
  • 确保机器学习应用程序可以通过 Docker 和 Kubernetes 等工具进行扩展。

我们暂时就此打住。

:你会在各个公司发现基于数据的其他几个角色(即数据说书人、机器学习研究员、 机器学习科学家 等)。).我推荐你使用 Linkedin Jobs、的确、Glassdoor、data camp等求职板进行调研。

所提供的工作清单并不全面,只能作为指南。读者应该利用这些信息,着手研究每个角色所需的技术组合,并建立一个投资组合。要记住的一件关键事情是,不同的公司以不同的方式组织他们的团队:不同的技术术语可能被用来描述两家不同公司的同一份工作。

感谢阅读。

联系我:
LinkedIn
Twitter
insta gram

如果你喜欢阅读这样的故事,并希望支持我的写作,可以考虑成为一名灵媒。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。如果你使用我的注册链接,我会收到一小笔佣金。

已经是会员了?订阅在我发布时得到通知。

https://kurtispykes.medium.com/subscribe

在 Python 中定义空变量和数据结构

原文:https://towardsdatascience.com/defining-empty-variables-and-data-structures-in-python-501995458577

定义缺失值、默认函数参数和初始化数据结构

图片由像素上拍摄制作

在处理大量数据集合时,经常会遇到缺少值的情况。缺失值可能对应于空变量、空列表、空字典、列中缺失的元素、空数据帧甚至无效值。能够定义空变量或对象对于许多软件应用程序来说非常重要,尤其是在处理缺失和无效的数据值时。这对于变量初始化、类型检查和指定函数默认参数等任务非常重要。

根据不同的用例,有几个选项可以指定空变量。最常见的方法是使用关键字 None 存储空值。这很有用,因为它清楚地表明变量值缺失或无效。虽然这有助于处理丢失的值,但在需要计算的情况下这是没有用的。例如,对于分类值、浮点值和整数值,将缺失值 None 转换为安南值通常很有用。安南值对应于“不是一个数字”,它是一种标记缺失值的有用方法,同时仍然能够执行有用的计算。例如,我们可以使用 python 中的 pandas 方法,用均值、中值或众数等统计数据替换 NaN 值。

除了指定 None 和 NaN 类型之外,指定空数据结构也非常有用。例如,如果在 python 中填充一个列表,通常需要定义一个初始化的空列表。此外,您可能有一个函数需要一个列表作为输入,以便无论列表是填充的还是空的都可以正常运行。在填充字典方面也可以进行类似的论证。定义一个空字典,然后用对应于它们各自值的键填充字典的逻辑通常是有用的。与列表类似,您可以定义一个函数,该函数需要列出一个空字典才能成功运行。同样的逻辑也适用于数据帧。

最后,定义空变量和数据结构对于类型检查和设置默认参数等任务非常有用。在类型检查方面,空变量和数据结构可以用来通知一些控制流逻辑。例如,如果呈现一个空数据结构,则执行“X”逻辑来填充该数据结构。在类型检查和设置默认参数方面,可能会有这样的情况,空数据结构的实例应该启动一些逻辑,从而允许函数调用在意外情况下成功。例如,如果您定义了一个函数,它在不同的浮点数列表上被调用多次,并计算平均值,只要该函数提供了一个数字列表,它就会工作。相反,如果函数提供了一个空列表,它将失败,因为它将无法计算空列表的平均值。然后,可以使用类型检查和默认参数来尝试计算并返回平均值,如果失败,则返回默认值。

用 None 和 NaN 定义一个空变量

在 python 中定义空变量很简单。如果希望为不会用于计算的缺失值定义一个占位符,可以使用 None 关键字定义一个空变量。例如,假设我们有包含年龄、收入(美元)、姓名和老年公民身份值的人口统计数据:

age1 = 35
name1 = "Fred Philips"
income1= 55250.15
senior_citizen1 = Falseage2 = 42
name2 = "Josh Rogers"
income2=65240.25
senior_citizen2 = Falseage3 = 28
name3 = "Bill Hanson"
income3=79250.65
senior_citizen3 = False

对于每个人,我们都有年龄、姓名、收入和高级公民身份的有效值。可能存在某些信息缺失或包含无效值的情况。例如,我们可能会收到包含无效值的数据,如年龄的字符或字符串,或姓名的浮点或整数。使用自由文本用户输入框的 web 应用程序尤其会出现这种情况。如果应用程序无法检测到无效的输入值并提醒用户,它会将无效的值包含在其数据库中。考虑下面的例子:

age4 = "#"
name4 = 100
income4 = 45250.65
senior_citizen4 = "Unknown"

对于这个人,我们的年龄值为“#”,这显然是无效的。进一步说,输入的名字是 100 的整数值,同样没有意义。最后,对于我们的老年公民变量,我们有“未知”。如果我们对保留这些数据感兴趣,因为收入是有效的,所以最好使用 None 关键字将年龄、姓名和 senior_citizen 定义为空变量。

age4 = None
name4 = None
income4 = 45250.65
senior_citizen4 = None

通过这种方式,任何查看数据的开发人员都会清楚地了解到缺少年龄、姓名和老年人的有效值。此外,收入值仍然可以与所有其他有效数据值一起用于计算统计数据。None 关键字的一个限制是它不能用于计算。例如,假设我们想要计算我们定义的四个实例的平均年龄:

avg_age = (age1 + age2 + age3 + age4)/4

如果我们尝试运行我们的脚本,它将抛出以下错误:

作者截图

这是一个类型错误,说明我们无法在整数和 None 值之间使用“+”运算符(加法)。

我们可以通过使用 numpy 中的 NaN(非数字)值作为缺失值占位符来解决这个问题:

age4 = np.nan
name4 = np.nan
income4 = 45250.65
senior_citizen4 = np.nan
avg_age = (age1 + age2 + age3 + age4)/4

现在它将能够成功运行。因为我们的计算中有一个 NaN,所以结果也是 NaN。这非常有用,因为代码能够成功运行。此外,这在处理数据结构(如数据帧)时尤其有用,因为 python 中有一些方法允许您直接处理 NaN 值。

除了定义空变量之外,在变量中存储空数据结构通常也很有用。这有许多用途,但我们将讨论如何使用默认的空数据结构进行类型检查。

为初始化定义空列表

在变量中存储空列表的最简单的应用是初始化将要填充的列表。例如,我们可以为先前定义的每个属性初始化一个列表(年龄、姓名、收入、高级状态):

ages = []
names = []
incomes = []
senior_citizen = []

然后可以使用 append 方法填充这些空列表:

ages.append(age1)
ages.append(age2)
ages.append(age3)
ages.append(age4)
print("List of ages: ", ages)

对于姓名、收入和高级地位,我们也可以这样做:

names.append(name1)
names.append(name2)
names.append(name3)
names.append(name4)
print("List of names: ", names)incomes.append(income1)
incomes.append(income2)
incomes.append(income3)
incomes.append(income4)
print("List of incomes: ", incomes)senior_citizen.append(income1)
senior_citizen.append(income2)
senior_citizen.append(income3)
senior_citizen.append(income4)
print("List of senior citizen status: ", senior_citizen)

作者截图

为初始化定义空字典

我们也可以使用空字典进行初始化:

demo_dict = {}

并使用我们之前填充的列表来填充字典:

demo_dict['age'] = ages
demo_dict['name'] = names
demo_dict['income'] = incomes
demo_dict['senior_citizen'] = senior_citizen
print("Demographics Dictionary")
print(demo_dict)

作者截图

为初始化定义空数据帧

我们也可以对数据帧做类似的事情:

import pandas as pddemo_df = pd.DataFrame()
demo_df['age'] = ages
demo_df['name'] = names
demo_df['income'] = incomes
demo_df['senior_citizen'] = senior_citizen
print("Demographics Dataframe")
print(demo_df)

作者截图

请注意,填充字典和数据框的逻辑是相似的。您使用哪种数据结构取决于您作为工程师、分析师或数据科学家的需求。例如,如果您喜欢生成 JSON 文件并且不需要数组长度相等,则字典更有用,而数据帧对于生成 CSV 文件更有用。

NaN 默认函数参数

定义空变量和数据结构的另一个用途是用于默认函数参数。

例如,考虑一个计算联邦税后收入的函数。到目前为止,我们定义的收入范围的税率约为 22%。我们可以将我们的函数定义如下:

def income_after_tax(income):
    after_tax = income — 0.22*income
    return after_tax

如果我们用 income 调用我们的函数并打印结果,我们得到如下结果:

after_tax1 = income_after_tax(income1)
print("Before: ", income1)
print("After: ", after_tax1)

作者截图

对于这个例子来说,这很好,但是如果我们有一个无效的收入值,比如一个空字符串,该怎么办呢?让我们传入一个空字符串,并尝试调用我们的函数:

after_tax_invalid = income_after_tax(‘’)

作者截图

我们得到一个 TypeError,说明我们可以将一个空字符串乘以一个非整数类型的 float。函数调用失败,after_tax 实际上从未被定义。理想情况下,我们希望保证该函数适用于任何收入值,并且 after_tax 至少用某个默认值来定义。为此,我们可以为 after_tax 定义一个默认的 NaN 参数,并键入 check the income。如果收入是浮动的,我们只计算税后,否则,税后是 NaN:

def income_after_tax(income, after_tax = np.nan):
    if income is float:
        after_tax = income — 0.22*income
    return after_tax

然后我们可以传递任何无效的有效收入,我们仍然能够成功地运行我们的代码:

after_tax_invalid1 = income_after_tax('')
after_tax_invalid2 = income_after_tax(None)
after_tax_invalid3 = income_after_tax("income")
after_tax_invalid4 = income_after_tax(True)
after_tax_invalid5 = income_after_tax({})print("after_tax_invalid1: ", after_tax_invalid1)
print("after_tax_invalid2: ", after_tax_invalid2)
print("after_tax_invalid3: ", after_tax_invalid3)
print("after_tax_invalid4: ", after_tax_invalid4)
print("after_tax_invalid5: ", after_tax_invalid5)

作者截图

读者可能想知道为什么一开始就把一个无效值传递给一个函数。实际上,函数调用通常是针对成千上万的用户输入进行的。如果用户输入是自由文本响应,而不是下拉菜单,则很难保证数据类型是正确的,除非应用程序明确强制执行。因此,我们希望能够在应用程序不崩溃或失败的情况下处理有效和无效的输入。

空列表默认函数参数

将空数据结构定义为默认参数也很有用。让我们考虑一个函数,它获取我们的收入列表并计算税后收入。

def get_after_tax_list(input_list):
    out_list = [x — 0.22*x for x in input_list]
    print("After Tax Incomes: ", out_list)

如果我们把这个和我们的收入清单联系起来,我们会得到:

get_after_tax_list(incomes)

作者截图

现在,如果我们用一个不是列表的值调用它,例如一个整数,我们得到:

get_after_tax_list(5)

作者截图

现在,如果我们包含一个空列表作为输出列表的默认值,我们的脚本将成功运行:

get_after_tax_list(5)

作者截图

空字典默认函数参数

与将默认参数定义为空列表类似,用空字典默认值定义函数也很有用。让我们定义一个接受输入字典的函数,我们将使用我们之前定义的 demo_dict,它返回一个包含平均收入的新字典

def get_income_truth_values(input_dict):
    output_dict= {'avg_income': np.mean(input_dict['income'])}
    print(output_dict)
    return output_dict

让我们用 demo_dict 调用我们的函数

get_income_truth_values(demo_dict)

作者截图

现在让我们尝试为 input_dict 传入一个无效值。让我们传递整数值 10000:

get_income_truth_values(10000)

作者截图

我们得到一个类型错误,指出整数对象 1000 是不可订阅的。我们可以通过检查输入的类型是否是字典,检查字典中是否有适当的键,并为输出字典设置一个默认参数来纠正这一点,如果不满足前两个条件,将返回该参数。这样,如果条件不满足,我们仍然可以成功地运行我们的代码,而不会出现错误。对于我们的默认参数,我们将简单地为 output_dict 指定一个空字典

def get_income_truth_values(input_dict, output_dict={}):
    if type(input_dict) is dict and ‘income’ in input_dict:
        output_dict= {‘avg_income’: np.mean(input_dict[‘income’])}
    print(output_dict)
    return output_dict

我们可以成功地调用相同的函数

get_income_truth_values(10000)

我们还可以为“avg_income”定义一个带有安南值的默认字典。这样,我们将保证我们有一个包含预期键的字典,即使我们用无效的输入调用我们的函数:

def get_income_truth_values(input_dict, output_dict={'avg_income': np.nan}):
    if type(input_dict) is dict and ‘income’ in input_dict:
        output_dict= {'avg_income': np.mean(input_dict['income'])}
    print(output_dict)
    return output_dictget_income_truth_values(demo_dict)
get_income_truth_values(10000)

作者截图

空数据框默认函数参数

与我们的列表和字典示例类似,带有默认空数据框的默认函数非常有用。让我们修改我们定义的数据框,以包含每个人的居民状态:

demo_df['state'] = ['NY', 'MA', 'NY', 'CA']

让我们也使用平均值估算年龄和收入的缺失值:

demo_df['age'].fillna(demo_df['age'].mean(), inplace=True)
demo_df['income'].fillna(demo_df['income'].mean(), inplace=True)

接下来,让我们定义一个函数,该函数对各州执行 groupby,并计算年龄和收入字段的平均值。结果将使用每个州的平均年龄和收入:

def income_age_groupby(input_df):
    output_df = input_df.groupby(['state'])['age', 'income'].mean().reset_index()
    print(output_df)
    return output_dfincome_age_groupby(demo_df)

作者截图

你应该已经猜到了,如果我们用一个不是 dataframe 的数据类型调用我们的函数,我们会得到一个错误。如果我们传递一个列表,我们会得到一个 AttributeError,说明列表对象没有属性“groupby”。这是有意义的,因为 groupby 方法属于 dataframe 对象:

income_age_groupby([1,2,3])

作者截图

我们可以为每个预期字段定义一个包含 nan 的默认数据框,并检查必要的列是否存在:

def income_age_groupby(input_df, output_df = pd.DataFrame({'state': [np.nan], 'age': [np.nan], 'income':[np.nan]})):
    if type(input_df) is type(pd.DataFrame()) and set(['age', 'income', 'state']).issubset(input_df.columns):
        output_df = input_df.groupby(['state'])['age', 'income'].mean().reset_index()
        print(output_df)
    return output_dfincome_age_groupby([1,2,3])

作者截图

我们看到我们的代码在无效数据值的情况下成功运行。虽然我们考虑了我们制作的数据示例,但是这些方法可以扩展到各种数据处理任务,无论是软件工程、数据科学还是机器学习。我鼓励您在自己的数据处理代码中尝试应用这些技术!

这篇文章中的代码可以在 GitHub 上找到。

结论

定义空变量和数据结构是处理缺失或无效值的重要部分。对于浮点、整数、布尔和字符串等变量,无效类型通常会导致代码失败或出错。这可能导致程序在大型处理任务中途崩溃,从而导致时间和计算资源的巨大浪费。考虑到处理无效和丢失的数据是数据处理的一个重要部分,理解如何将空变量和数据结构定义为函数默认值可以省去工程师或数据科学家的许多麻烦。能够用合理的默认值定义函数,使它们返回一致的、预期的无错误输出,这是每个程序员的基本技能。

本帖原载于 内置博客 。原片可以在 这里找到

定义可信的人工智能

原文:https://towardsdatascience.com/defining-trustworthy-ai-234a97c39035

播客

Beena Ammanath 谈到公司可以采取哪些步骤来建立更可靠的系统

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

值得信赖的人工智能是当今最流行的流行语之一。但是,尽管每个人似乎都同意我们希望人工智能是值得信任的,但值得信任的定义往往是模糊的或不充分的。也许这不应该令人惊讶:很难提出一套标准来衡量“可信度”,并像自动驾驶汽车一样适用于网飞电影推荐。

因此,也许值得信赖的人工智能需要以一种更微妙的方式来思考——一种反映个体人工智能用例复杂性的方式。如果这是真的,那么新的问题就来了:谁来定义可信度,当缺乏可信度导致像人工智能事故或不良偏见这样的伤害时,谁来承担责任?

从这个角度来看,可信度不仅是算法的问题,也是组织的问题。这正是 Beena Ammanath 在她即将出版的新书 Trustworthy AI 中提出的情况,该书从实用的角度探索了人工智能的可信度,研究了公司可以采取哪些具体步骤来使他们的内部人工智能工作得更安全、更好、更可靠。在这一集的 TDS 播客中,Beena 和我一起谈论了人工智能中的可信度、可解释性和鲁棒性的定义,以及人工智能监管和自我监管的未来。

以下是我在对话中最喜欢的一些观点:

  • 对 Beena 来说,人工智能的可信度意味着避免不必要的副作用。这些可能包括对用户的身体伤害,如人工智能事故,或者更无形的问题,如偏见。这是一个总括术语,它综合了许多其他概念,每个概念都需要以自己的方式处理,有些概念对一个应用程序或公司来说比另一个更重要。
  • Beena 认为可信度不是人工智能系统的属性,而是一个组织(可能包括人工智能系统)的属性。她的思想的一个重要方面是,只有当组织内的人类行为者被分配了对各种人工智能相关风险的明确所有权和责任时,可信性才是可能的。如果一辆自动驾驶汽车撞死了一名行人,谁负责?这辆车的主人?开发其视觉或决策算法的工程师?他们的经理或董事——或者公司的首席执行官呢?管理一个可信的人工智能过程意味着澄清那种所有权。
  • 在 Beena 看来,没有一套定义可信人工智能的程序。但所有公司都应该坚持一些原则:例如,公司应该有明确的程序,鼓励对人工智能开发的风险进行思考。像模型车或算法影响评估这样的工具在这里可以有所帮助,像红队这样的练习也可以,在红队中,参与人工智能系统开发的员工积极地试图让他们更好地了解自己的弱点。
  • Beena 担心,随着人工智能能力继续加速,监管机构可能难以跟上。因此,企业似乎有可能不得不认真对待自我监管的过程,以避免造成大规模伤害,这有可能导致品牌损害或过度的监管膝跳反应,从而使行业倒退。
  • Beena 的书《值得信赖的 AI》现在已经出版,在各地的网上书店都可以买到:)

你可以在 Twitter 上关注 Beena 这里,或者 me 这里

章节:

  • 0:00 介绍
  • 1:55 背景和值得信赖的人工智能
  • 7:30 激励员工努力提高能力
  • 13:40 应用领域级别的法规
  • 16:45 弥合差距
  • 23:30 认知水平转移到人工智能
  • 25:45 什么是值得信赖的 AI?
  • 34:00 鲁棒性故障示例
  • 36:45 团队多元化
  • 40:15 较小的公司
  • 最佳做法的应用
  • 46:30 总结

使用 AWS 和 Python 在云上创建 SQL 数据库的权威指南

原文:https://towardsdatascience.com/definitive-guide-to-create-an-sql-database-on-cloud-with-aws-and-python-c818c7270af2

关于使用 Amazon RDS、MySQL Workbench 和 PyMySQL 在云中为 Python 应用程序构建和部署数据库的简单易懂的综合指南

照片由卢卡斯劳Unsplash

我们经常会从我们在数据科学工作或个人项目中构建的 Python 应用程序中生成和收集有价值的数据。

因此,拥有一个可伸缩且高性能的附带数据库是至关重要的,这样就可以有效地存储、组织和查询数据。

幸运的是,现代云服务让我们可以轻松地在云上创建经济高效、可调整大小和完全托管的数据库。

这个简单的分步指南解释了如何集成亚马逊 RDSMySQL Workbench、PyMySQL 来开始免费构建和部署云数据库

内容

(1)工具概述(2)设置亚马逊 RDS(3)设置 MySQL 服务器创建数据库模式和表设置 PyMySQL【7】使用 PyMySQL 读**

所有图片和截图均由作者提供,除非另有说明

(1)工具概述

(一)MySQL

MySQL 徽标|根据知识共享 署名-共享 3.0 许可使用的图像

MySQL 是基于 SQL 的开源关系数据库管理系统(RDBMS),被广泛认为是世界上最流行的 RDBMS。

它允许我们轻松地创建、存储、访问和修改数据库中的关系数据。

(二)亚马逊 RDS

AWS 标志| AWS 标志是 Amazon.com 公司或其附属公司在美国和其他国家的商标,来源: AWS 联合营销

亚马逊关系数据库服务(RDS) 是一种云服务,可以轻松在云端设置、操作和扩展关系数据库。

使用 Amazon RDS 的好处包括:

  • 经济高效且可扩展的硬件容量
  • 自动化耗时的数据库任务,如扩展、监控和软件修补
  • 出色的功能,如快速性能、高可用性、兼容性和安全性。
  • 只需点击几下鼠标,即可在几分钟内快速部署服务器

Amazon RDS 支持六个数据库引擎,我们将使用一个用于 MySQL

(三)PyMySQL

PyMySQL 是一个纯 Python 的 MySQL 客户端库,允许我们访问和修改 MySQL 数据库(例如 CRUD 操作)。

这个包是 Python 应用程序和 MySQL 数据库之间的重要链接。

(2)设置亚马逊 RDS

步骤 1 —创建数据库

登录 AWS 管理控制台后,在顶部搜索栏输入' rds ',点击第一个显示 RDS 的搜索结果。

点击数据库进入左侧菜单栏中的数据库部分。从那里,点击橙色按钮' Create database '来创建我们在 AWS 上的第一个关系数据库。**

步骤 2-修改实例设置

从现在开始,我们将修改数据库实例的设置。首先,我们选择 MySQL 作为我们的数据库引擎。

我们选择“自由层”模板选项,开始为RDS 自由层上的自由层提供托管云数据库服务。**

此外,决定数据库实例的名称(在 DB 实例标识符下)。我将它命名为'客户端数据库',但是我们总是可以定制它。**

由于我们是从本地机器开始这个项目,我们想通过公共互联网连接到数据库。因此,我们为公共访问选择了****

因此,对于 生产 实例来说,保持谨慎是至关重要的,在这些实例中,我们可能希望在没有公共访问的情况下限制与锁定 VPC 的连接。

对于上面没有提到的其他设置,我们可以保留的默认值。**

对于凭证* 设置,选择一个安全易记的用户名和密码。这一部分非常重要,因为稍后需要凭证来建立到数据库的认证连接。***

完成所有必要的更改后,滚动到底部并单击橙色的“创建数据库按钮。**

在我们的实例出现在数据库部分之前,数据库创建需要几分钟时间。**

步骤 3 —编辑安全组配置

为了确保成功连接到数据库,我们必须修改我们的虚拟私有云(VPC) 上的安全* 组配置,以允许公共互联网访问。***

通过首先单击我们刚刚创建的数据库链接(即 DB identifier 下的**client-database** )来完成修改。在随后的页面中,点击 VPC 安全组下的**default** 链接。

接下来,选择入站子选项卡,并单击右下角的“编辑入站规则”按钮。**

我们单击“ Add rule ”添加一个新的入站规则,允许我们在任何地方访问数据库,只要我们有正确的密码验证。

新规则应该具有'类型所有流量,以及' Anywhere-IPv4。最后,点击橙色的'保存规则'按钮保存新规则。**

完成以上工作后,我们已经成功地在 AWS RDS 上建立了第一个 MySQL 数据库 实例

3)设置 MySQL 服务器

步骤 1 —下载和安装

在使用 MySQL 之前,我们先在本地机器上安装两个 MySQL 软件——MySQL社区服务器和 MySQL 工作台**

(I)MySQL社区服务器* 是免费使用的 RDBMS 服务器,支持 MySQL 的查询和连接功能。我们可以把它想象成安装在本地机器上的 MySQL 数据库引擎。***

【ii】MySQLwork bench是使用 MySQL 服务器和数据库的统一图形工具。这个可视化软件使得设计、建模、生成和管理我们的数据库变得容易。**

我们可以在 MySQL 下载 页面找到安装程序链接,下面圈出了两个工具的正确链接。

单击该链接后,它会提示我们根据我们的操作系统(OS)选择特定的安装程序。

不需要为安装创建 Oracle Web 帐户,所以我们可以直接点击“不,谢谢,开始下载吧**

注:如果你用的是 Windows OS ,我强烈推荐下载MySQL Installer for Windows(如推荐下载下的横幅所示当你选择微软 Windows )。然后我们可以使用 MySQL 安装程序来下载 MySQL 社区服务器和 MySQL 工作台。**

步骤 2—运行安装

下载安装程序后,我们运行它们并遵循后续的安装步骤。

在安装 MySQL 服务器时,我们会遇到多种服务器配置选项。好消息是我们可以保留所有的默认值。

关键的配置步骤是设置(并记住)一个强 MySQL 根密码

(4) 将 RDS 数据库实例连接到 MySQL 工作台

到目前为止,安装非常成功!我们现在准备连接到第 3 节中创建的 RDS 数据库实例。

我们首先启动 MySQL Workbench ,在那里我们会看到欢迎页面。要建立一个新的MySQL 连接,点击 MySQL 连接标题旁边的 按钮。****

在弹出的对话框中,我们需要修改几个连接参数。

注意:以下参数的值可以在 RDS 控制台的数据库部分的数据库实例(即**client-database**)中找到(参见第 3 节)。

  • 连接名称:输入连接的自定义名称,如**rds_connection_1**
  • 主机名:输入 RDS 端点。该信息可在连接和安全选项卡中找到。

  • 端口:输入端口号(默认值应该已经是 3360 )。该信息可在连接和安全选项卡中找到。
  • 用户名:输入主用户名。该信息可在配置选项卡中找到。

  • 密码:点击存储到金库,输入 RDS 实例凭证设置的密码(参见第 2 节的步骤 2)。**

完成的参数如下所示:

接下来,点击'测试连接'验证配置。如果我们正确执行了这些步骤,我们应该会看到一个弹出窗口,指示连接成功:**

最后,从设置新连接窗口点击确定保存连接。**

(5)创建数据库模式和表

步骤 1 —创建模式

在第 4 节之后,我们应该在工作台欢迎页面上看到新的连接。

点击新建框(即 rds_connection_1 )以打开连接并访问数据库。**

在创建新表之前,我们首先定义一个模式。单击顶部菜单中的'创建新模式'按钮(下面用绿色圈出),并为模式命名(如**schema1**)。**

然后,我们在随后的屏幕上单击“应用按钮,我们将看到我们的新模式出现在左侧菜单的模式中。**

步骤 2 —创建表格

要创建一个新表,单击新模式旁边的箭头展开子菜单,右键单击选项,并选择“创建表”。**

假设我们要创建一个表,在以下列中存储站点访问信息:

我们可以添加列并设置相应的选项来构建一个新的表(我们可以相应地命名它,例如 tblClients )。**

在接下来的几个屏幕中单击' Apply ,执行创建表的 SQL 命令,我们的新表将出现在 SCHEMAS 菜单中。

我们还可以运行一个简单的 SQL 查询来确认成功创建。

(6)设置 PyMySQL

为了将 RDS MySQL 实例链接到后续的 Python 脚本,我们可以使用 PyMySQL。我们可以用 pip 安装它:

*pip install PyMySQL*

(7)使用 PyMySQL 对数据库进行读写

我们已经到了最后一个阶段,开始使用 Python 访问 MySQL 数据库中的数据。

步骤 1 —下载 SSL 证书包

为了加密在本地客户机和 RDS 数据库实例之间移动的数据,我们需要实现一个安全套接字层(SSL)连接

我们可以从:https://S3 . Amazon AWS . com/rds-downloads/rds-combined-ca-bundle . PEM下载 SSL 证书,然后放置下载的。 pem 在指定的项目文件夹中捆绑文件,例如**ssl/**rds-combined-ca-bundle.pem

步骤 2-设置连接参数

然后我们创建一个 config.py 文件来存储数据库连接的参数。

大多数参数都可以在 Amazon RDS 控制台中找到,我们现在应该很熟悉了。

SSL_CA 变量应该指向我们的 SSL 证书包的路径,并且 CURSORCLASS 变量应该设置为pymysql.cursors.DictCursor

重要:安全地存储密码凭证永远不要让它们在公共 Python 文件中打开。****

步骤 3—启动 RDS 连接

我们用包含 PyMySQL 的 Python 代码启动到 RDS 数据库实例的连接。

步骤 4 —运行 CRUD 操作

一旦建立了连接,我们就可以编写函数来执行 CRUD(即创建、读取、更新、删除)SQL 操作。

例如,下面的函数将单个记录插入到我们之前创建的表tblClients中。****

****提示:我们甚至可以通过 PyMySQL 而不是 MySQL Workbench 直接创建表格。更多信息参见此示例

步骤 5 —验证数据库更改

有两种方法可以检查所执行的 SQL 操作是否在数据库中成功注册。

(i) 直接在 MySQL Workbench 中运行 SQL 查询

(ii) 通过 PyMySQL 在 Python 中运行 SQL 查询

输出将显示我们刚刚插入到表中的记录:

包装它

至此,我们已经完成了在云中使用 AWS RDS 和 PyMySQL 创建 MySQL 数据库的演练。

你可以在这个 GitHub repo 中找到样例代码和配置文件。

在你走之前

欢迎您加入我的数据科学学习之旅!点击此媒体页面,查看我的 GitHub ,了解更多精彩的教育数据科学内容。同时,享受在云上创建 SQL 数据库的乐趣吧!****

使用 Pandas drop()从数据帧中删除行和列

原文:https://towardsdatascience.com/delete-rows-and-columns-from-a-dataframe-using-pandas-drop-d2533cf7b4bd

掌握熊猫滴 9 招()加速你的数据分析

照片由伯纳德·赫尔曼Unsplash 上拍摄

数据操作指的是调整数据以使其有组织且更易于阅读的过程。通常,有些数据是不可用的,会干扰重要的数据。应该清理和删除不必要或不准确的数据。

来源于 solvexia.com[1]

从 Pandas 数据帧中删除一个或多个行/列可以通过多种方式实现。其中,最常见的就是drop()法。这种方法似乎很容易使用,但是仍然有一些技巧你应该知道,以加快你的数据分析。

在本文中,您将学习熊猫drop()处理以下用例的技巧:

  1. 删除单行
  2. 删除多行
  3. 基于行位置和自定义范围删除行
  4. 删除单个列
  5. 删除多列
  6. 基于列位置和自定义范围删除列
  7. 使用多索引数据框架
  8. inplace=True做操作到位
  9. error='ignore'抑制错误

请查看笔记本获取源代码。更多教程可从 Github Repo 获得。

1.删除单行

默认情况下,熊猫drop()会根据它们的索引值删除该行。通常,每行的索引值是一个从 0 开始的整数值。指定行索引会删除它,例如删除索引值为1的行。:

df.**drop(1)**# It's equivalent to
df.**drop(labels=1)**

使用 Pandas drop()删除一行(图片由作者提供)

注意,删除行必须将参数axis设置为0(在 Pandas drop()中,axis默认为0,所以可以省略)。如果指定了axis=1,它将删除列。

或者,从 DataFrame 中删除一行的更直观的方法是使用index参数。

# A more intuitive way
df.drop(**index=1**)

使用 Pandas drop()删除一行(图片由作者提供)

2.删除多行

熊猫drop()可以带列表删除多行:

df.drop(**[1,2]**)# It's equivalent to
df.drop(**labels=[1,2]**)

使用 Pandas drop()删除多行(图片由作者提供)

类似地,删除多行的更直观的方法是将一个列表传递给index参数:

# A more intuitive way
df.drop(**index=[1,2]**)

使用 Pandas drop()删除多行(图片由作者提供)

3.基于行位置和自定义范围删除行

数据帧索引值可能不是升序,有时它们可以是任何其他值,例如日期时间或字符串标签。对于这些情况,我们可以根据行的位置删除行,例如,删除第二行,我们可以调用df.index[**1**]并将其传递给index参数:

df.drop(index=**df.index[1]**)

根据行位置删除行(作者图片)

要删除最后一行,我们可以使用快捷方式,如标识最后一个索引的-1:

df.drop(index=**df.index[-1]**)

根据行位置删除行(作者图片)

例如,我们还可以使用切片技术来选择一系列行

  • 删除最后 2 行df.drop(index=df.index[**-2:**])
  • 删除每隔一行df.drop(index=df.index[**::2**])

根据行位置删除行(作者图片)

如果您想了解关于切片技术以及如何使用行索引来选择数据的更多信息,可以查看本文:

4.删除单个列

类似于删除行,Pandas drop()可以通过将axis参数指定给1来删除列:

df.drop(**'math', axis=1**)# It's equivalent to
df.drop(**labels='math', axis=1**)

使用 Pandas drop()删除单个列(图片由作者提供)

从 DataFrame 中删除列的更直观的方法是使用columns参数。

# A more intuitive way
df.drop(**columns='math'**)

使用 Pandas drop()删除单个列(图片由作者提供)

5.删除多列

类似地,我们可以通过一个列表来删除多个列:

df.drop(**['math', 'physics']**, **axis=1**)# It's equivalent to
df.drop(**labels=['math', 'physics']**, **axis=1**)

使用 Pandas drop()删除多个列(图片由作者提供)

删除多列的一个更直观的方法是将一个列表传递给columns参数:

# A more intuitive way
df.drop(**columns=['math', 'physics']**)

使用 Pandas drop()删除多个列(图片由作者提供)

6.基于列位置和自定义范围删除列

我们可以根据列的位置删除一列,例如,删除第二列,我们可以调用df.**column**[**1**]并将其传递给columns参数:

df.drop(**columns=df.columns[1]**)

根据列的位置删除列(图片由作者提供)

要删除最后一列,我们可以使用快捷方式,如标识最后一个索引的-1:

df.drop(columns=**df.columns[-1]**)

根据列的位置删除列(图片由作者提供)

类似地,我们也可以使用切片技术来选择一系列列,例如

  • 删除最后 2 列df.drop(columns=df.columns[**-2:**])
  • 删除每隔一栏df.drop(columns=df.columns[**::2**])

根据列的位置删除列(图片由作者提供)

7.使用多索引

一个 MultiIndex (也称为层次索引)数据帧允许我们将多列作为一个行标识符,将多行作为一个标题标识符:

(图片由作者提供)

当在多索引数据帧上调用 Pandas drop()时,默认情况下,它将删除 0 级索引和列。

# Delete all Oxford rows
df.drop(index='Oxford')# Delete all Day columns
df.drop(columns='Day')

多指数中的熊猫下降()

要指定要删除的级别,我们可以设置level参数:

# remove all 2019-07-04 row at level 1
df.drop(index='2019-07-04', **level=1**)# Drop all Weather column at level 1
df.drop(columns='Weather', **level=1**)

多指数中的熊猫下降()

在某些情况下,我们希望删除特定的索引或列组合。为此,我们可以将一个元组传递给indexcolumns参数:

# drop the index combination 'Oxford' and '2019-07-04'
df.drop(**index=('Oxford', '2019-07-04')**)# drop the column combination 'Day' and 'Weather'
df.drop(**columns=('Day', 'Weather')**)

多指数中的熊猫下降()

如果您想了解更多关于在多索引数据框架中访问数据的信息,请查阅本文:

[## 在熊猫的多索引数据框架中访问数据

towardsdatascience.com](/accessing-data-in-a-multiindex-dataframe-in-pandas-569e8767201d)

8.用inplace=True进行就地操作

默认情况下,Pandas drop()返回结果的副本,而不会影响给定的数据帧。我们可以设置参数inplace=True来就地执行操作,以避免额外的重新分配并减少内存使用。

9.用error='ignore'抑制错误

您可能会注意到,当给定的行或列不存在时,熊猫drop()会抛出一个错误。我们可以设置参数error='ignore'来抑制错误。

结论

在本文中,我们介绍了 9 个使用熊猫drop()删除行和列的用例。该方法本身使用起来非常简单,是数据预处理中操作数据的最受欢迎的方法之一。

感谢阅读。请查看笔记本获取源代码,如果您对机器学习的实用方面感兴趣,请继续关注。更多教程可从 Github Repo 获得。

参考

[1] 数据操作的 5 个技巧

使用 Streamlit 演示您的模型

原文:https://towardsdatascience.com/demo-your-model-with-streamlit-a76011467dfb

如何在没有任何后端或前端知识的情况下快速部署您的计算机视觉模型

蒂姆·高在 Unsplash 上拍摄的照片

任务

假设你需要展示你在开发一个很酷的计算机视觉模型上取得的进展。您正在处理的模型还没有准备好,因此将它部署到适当的开发或生产环境中需要时间和大量的工作。另一方面,开发一个特定的 web 界面来与您的模型进行交互可能是一项单调乏味的任务,并且需要一套对于数据科学家来说并不常见的技能。

营救

Streamlit ,是一个开源的 Python 库,它使得创建和共享用于机器学习和数据科学的 web 应用变得容易。在向你的团队展示项目进展、获得并与你的经理分享见解,甚至从客户那里获得反馈时,它会非常有用。

它不能取代需要监控、日志记录等的适当的生产部署。然而,它能让你在几个小时内创建“有用的东西”,而不需要任何 web 开发的先验知识,甚至不知道如何使用 flaskDjango 作为后端。

让我们看看这个魔术是如何运作的。

装置

要安装 Streamlit run:

pip install streamlit

如果你使用的是诗歌那么改为运行:

poetry add streamlit

要验证所有的工作是否正常,您可以运行这一行并查看一些解释和演示:

streamlit hello

您的第一款应用——上传图片

让我们从从本地存储加载一个图像并显示它开始:

要运行该应用程序,只需输入:

streamlit run image_upload.py

然后它会在你的浏览器中打开应用程序。它应该是这样的:

图片上传演示应用,图片作者

你可以浏览你的电脑,上传任何你喜欢的图片。我选择上传一些可爱企鹅的图片。

图片上传演示应用。作者提供的应用程序图片, Paul CarrollUnsplash 上传的照片

部署预先训练的模型

有时,为了有一个好的基线,查看预训练模型如何工作是有用的。现在,您将在一个 Streamlit 应用程序中部署其中一个模型,在本例中为 ResNet18。

要使用 PyTorch 做到这一点,您需要加载模型并下载 ImageNet 标签:

为了在图像上运行模型,应该修改您之前使用的 load_image 函数来返回图像。您需要将 image_data 转换为 PIL 图像对象,以便将其输入到模型中:

下面的代码是对 PyTorch ResNet 教程的修改版本,它运行一个模型预测并呈现图像的前 5 个类别:

当然,这也需要相应地修改主函数:

首先需要改的是标题。然后,您将加载模型、标签和图像。您还应该添加一个按钮,在加载的图像上触发模型推理。

这是应用程序在我之前加载的图像上运行时的样子:

带有示例输出的预训练 ResNet18 模型应用程序。作者提供的应用程序图片,保罗·卡罗尔Unsplash 上传的照片

该模型以 1.0 的概率将图像分类为“国王企鹅”。其他 4 个顶级类的概率可以忽略不计。

这个例子的完整代码在 Github 中。

部署自定义影像分类模型

现在您已经准备好部署一个定制模型了。您将使用 PyTorch 模型,该模型根据企鹅类型对图像进行分类。创建使用自定义模型的 web 应用程序的过程与涉及预训练模型的过程非常相似,只是有一些修改。代码可以在这里找到。

关于模型

通常,预先训练的模型不足以满足我们的需求,因为手头的任务更加具体,并且不在此类模型的覆盖范围内。例如,ImageNet 有一个“国王企鹅”类,但实际上还有更多企鹅类型。因此,为了根据企鹅类型对图像进行分类,我必须训练自己的模型。我用这个笔记本进行训练。

如果你想了解更多关于这个模型是如何建立的,你可以在我之前的文章中阅读。

获取模型

为模型相关文件创建一个目录:

mkdir custom_model

要直接下载模型文件,您可以使用链接。将其复制到新目录。

将此常量定义添加到代码中:

MODEL_PATH = 'custom_model/model.pt'

创建标签文件

用分类类别的名称创建一个文本文件,每个类别一行。确保课程的顺序与培训中使用的顺序相匹配。

例如,要发现 penguin 数据集的顺序,您可以在培训笔记本中查找以下行,并在其后添加一张图片:

class_names = image_datasets['train'].classesprint(class_names)

这将为您提供输出:

[‘Adelie Penguin’, ‘Chinstrap Penguin’, ‘Emperor Penguin’, ‘Gentoo Penguin’]

因此,您应该将以下类名复制到名为 model_classes.txt 的文件中,并将其放在 custom_model 目录中:

Adelie Penguin
Chinstrap Penguin
Emperor Penguin
Gentoo Penguin

将新标签文件作为常量添加到代码中:

LABELS_PATH = 'custom_model/model_classes.txt

加载模型和标签

模型和标签加载变得更简单,因为它们现在是本地的:

更多的修复

与具有许多标签的 ResNet18 不同,该定制模型只有 4 个标签。因此,您应该修改预测代码以输出所有现有类的概率:

更改主函数,使应用程序具有不同的标题,并传递模型和标签的本地路径:

最后,应用程序应该是这样的:

自定义影像分类模型结果。作者上传的应用程序图片和照片

为了测试这款应用,我上传了一张巴布亚企鹅的图片。该模型以 0.91 的概率将该图像分类为巴布亚企鹅的图像。其他企鹅类型的概率要低得多。随着应用程序运行良好的确认,我得到了模型正确性的另一个证明。很可爱,对吧?

结论

开发一个运行良好的计算机视觉模型需要花费大量的时间和精力。在这个过程中,您可能需要部署您的模型,并向您的经理、队友和客户展示它的能力。Streamlit 是一个强大且易于使用的工具,即使您没有适当的内部工具或前端知识,也可以让您实现这一目标。

我展示了 Streamlit 在图像分类中的潜在用途。然而,它可以用于其他类型的计算机视觉任务,以及可视化,数据分析等等。我鼓励你浏览一下 Streamlit 网站上的例子,亲自试用一下这个工具,看看它是如何融入你的日常工作的。

360°球面数据的民主化几何人工智能

原文:https://towardsdatascience.com/democratizing-geometric-ai-for-360-spherical-data-eec9a53606b6

360°球形数据解锁 AI

Josue Aguazia 在 Unsplash 上拍摄的照片

虽然人工智能现在对于标准类型的数据来说很常见,如结构化、序列和图像数据,但人工智能在其他更复杂形式的数据中的应用受到了严重限制。这些更复杂的数据集通常表现出非同寻常的几何特征。

几何人工智能(geometric AI)或几何深度学习(geometric deep learning)的领域已经出现,将人工智能的显著优势扩展到这些更复杂的几何数据集1。然而,几何人工智能技术的使用仍处于初级阶段,因为构建和部署几何人工智能模型仍然很困难。

民主化几何人工智能

一些优秀的几何人工智能库和框架已经存在,主要用于图形几何人工智能技术(如本文中所讨论的)。然而,这些库相对来说是低级的,在部署到生产环境之前,需要具有几何人工智能专业知识的专家机器学习工程师来构建和训练模型。对于其他形式的几何人工智能,许多进展是在尖端研究领域,在这些领域还没有通用的库。

我们正在用哥白尼为几何人工智能开发一个低代码平台,这样非专家也可以很容易地用几何人工智能方法解决他们自己的问题。我们在这个方向上的第一步是使 360°球形数据的几何人工智能模型可用。

360°球面数据的几何人工智能

球形数据实际上非常普遍,出现在许多领域。例如,当在球面上的每个点进行观测时,例如在地球的地形图上,会产生球面数据。然而,当在方向上进行观察时也会出现这种情况,例如 360°摄像机拍摄的全景照片和视频,例如用于虚拟现实、监控或自动驾驶汽车。其他应用包括宇宙学中的大爆炸残余光分析或医学中的扩散磁共振成像,以及许多其他应用。

球形数据的例子。【作者创作的 360 照片;CMB 图像来源;地球图片来源于维基百科;dMRI 图片来源于维基百科。]

Kagenova 我们正在努力解开深度学习在这些问题和其他涉及复杂几何数据(如球体)方面的巨大成功。对于 360 球形数据,标准人工智能技术是无效的,我们需要专门为数据的球形几何设计的几何人工智能。

在开发球形人工智能技术以解锁这些应用方面已经取得了很多进展(正如我们以前的文章这里这里中所讨论的)。一个重大的挑战是开发计算效率高并且能够处理高分辨率数据(如高分辨率 360°图像)的技术。

在我们的研究中,我们在这方面取得了很大进展,首先开发了通用高效组件2,然后引入了支持高分辨率输入数据的方法[3]。虽然对高分辨率输入数据的支持开启了许多应用程序,例如分类问题,但在许多设置中,必须支持高分辨率输出数据。我们目前正在致力于支持高分辨率输出数据,并将很快发布一篇相关论文,这将在 360°图像理解方面开辟一系列新的应用,如语义分割

现在,我们用于球形数据的几何人工智能技术正在成熟,我们计划将我们的方法和模型公开给任何人使用。

图像分类的概念 360

虽然我们的哥白尼平台的工作仍在进行中,但在此期间,我们计划通过 AWS 人工智能市场推出一些球形人工智能模型。

我们刚刚发布了我们的第一个模型,用于 360°图像的分类。

这是一个简单的模型,使用基于 inception 的架构[4]来执行 ImageNet 分类,即使用 1,000 个 ImageNet 类对 360 个图像进行分类。ImageNet 可能无法为 360°分类提供最佳类别标签集,因为 360°图像比标准 2D 平面图像捕获更多内容;然而,它提供了一组熟悉的类标签来开始。在这个模型中,我们没有利用我们最新的研发成果,而是提供了一个非常简单的模型,让任何人都可以立即开始 360°图像分类。

你可以在 AWS AI Marketplace 这里找到免费的 Inception360。演示如何使用 Inception360 的笔记本可在此处获得。

下面我们举例说明在(样本外)360°图像上的 360°概念分类。考虑下面一个码头和帆船的 360 度图像。

由 Inception360 分类的示例(样本外)图像。返回以下排序的前 5 个分类:dock, boathouse, gondola, schooner, yawl. [Original image [source](https://pixexid.com/image/oif1n7u-360-image-fishing).]

Inception360 返回该图像的以下(排序前 5 名)分类,这些分类与该图像非常匹配:

dock, boathouse, gondola, schooner, yawl

未来(更多型号即将推出!)

虽然几何人工智能技术可以释放人工智能对于复杂几何数据的巨大潜力,但由于当前在构建、训练和应用几何人工智能模型方面的困难,它们尚未得到广泛利用。

有了哥白尼的平台,我们打算将几何人工智能大众化,以广泛应用于解决现有人工智能技术不适用的许多问题。我们在这个方向上的第一步是在 AWS AI Marketplace 上发布球形 360°数据的几何 AI 模型,从 Inception360 开始对 360°图像进行分类。请关注此空间,因为我们计划很快发布更多型号!

参考

[1]布朗斯坦,布鲁纳,科恩,维利科维奇,几何深度学习:网格,群,图,测地线,和量规 (2021), arXix:2104.13478

2科布,沃利斯,马沃-帕克,马利涅尔,普莱斯,达韦扎克,麦克尤恩,高效广义球形 CNN,ICLR (2021), arXiv:2010.11661

[3] McEwen,Wallis,Mavor-Parker,可扩展和旋转等变球形 CNN 的球上散射网络,ICLR (2022), arXiv:2102.02828

[4] Szegedy 等人,深入研究卷积,IEEE CCVPR(2015)arXiv:1409.4842

揭开 Python 中迭代器和生成器的神秘面纱

原文:https://towardsdatascience.com/demystify-iterators-and-generators-in-python-f21878c9897

了解处理大型数据集的有效方法

图片由 Pixabay 中的设计完成

当您有一个大型数据集时,比如一个大的 CSV 文件或一个大的 SQL 表,将所有数据加载到内存中是低效的,甚至是不可能的。你的电脑会卡住,你的程序会崩溃。调试起来既费时又令人沮丧。幸运的是,迭代器和生成器是解决这类问题的好工具。此外,理解生成器有助于学习更高级的特性,比如目前越来越流行的 asyncio。

Python 中的范围

在开始之前,让我们看一下特殊的range函数,该函数返回一个产生整数序列的可迭代的。Iterable,顾名思义,就是可以迭代的东西。或者你可以把它理解为可以在一个for循环中使用的东西。让我们用一些简单的代码来验证一下:

从这个简单的代码片段中,我们可以知道:

  • 从技术上讲,range是一个类,尽管它以非 Pythonic 化的小写字母开头。range返回的对象属于range类型。
  • 一个range对象是可迭代的并且可以被迭代。
  • 然而,range对象不是迭代器。我们需要使用iter函数将一个可迭代转换成一个迭代器

迭代器

迭代器是一个实现神奇的__next__方法的对象,因此可以在next函数中用来产生数据流的下一个元素,如上所示。为了理解迭代器是如何工作的,让我们创建一个模仿range函数行为的类。

我们需要一些代码来模拟range的位置参数的行为。重要的是,我们需要一个状态变量counter来记录自定义迭代器处于哪个状态以及下次生成哪个值。

让我们用next函数试试定制迭代器:

是的,它像预期的那样工作。现在,让我们试着在一个for循环中使用它,看看会发生什么。

嗯,有点奇怪,不是吗?MyRangeIter是迭代器,但不是可迭代的。如果迭代器只能在next函数中使用,而不能在for循环中使用,那么它就没有用。实际上,要使一个迭代器可迭代,我们需要实现__iter__魔法方法,这使得它可迭代,并且可以与上面演示的iter函数一起使用。如果你现在在r_iter上使用iter,你也会看到一个错误,说它是不可迭代的。现在让我们添加__iter__方法:

我们已经知道,iter函数调用底层类的__iter__方法并返回一个迭代器。在这个例子中,返回的迭代器是它自己。是的,很奇怪,但事情就是这样。实际上,如果您意识到要使类能够使用nextiter函数,必须分别实现神奇的__next____iter__方法,那么理解起来就不会那么困难了。

现在该变量可以在for循环中使用。你可以自己尝试一下。

发电机

如上所述,创建迭代器需要相当多的样板代码。我们需要创建一个类并实现神奇的__next____iter__方法。在 Python 中有一种更好的方法,这就是生成器的亮点。

要创建一个生成器,我们不需要创建一个类并实现神奇的__next____iter__方法。生成器简单地由生成器函数定义:

生成器函数的所有魔力都在于关键字**yield**,它将数据和控制返回给调用者,但保留函数的状态。当它再次迭代时,函数被恢复,并基于最新状态产生一个新值。在这个例子中,状态是用简单的计数器实现的。如果你把yield改成return,那么它就是一个常规函数,只会返回一个值。实际上,如果没有yield关键字,它根本不是一个生成器,不能被迭代。

让我们试试我们的发电机:

类似于迭代器,当生成器耗尽时会引发StopIteration异常。该异常由for循环自动处理。

此外,应该注意的是,生成器函数中的return关键字会引发StopIteration异常,返回值将被用作异常的消息。这对于理解生成器的类型注释是很重要的,我们将很快介绍这一点。

生成器理解

在我们学习更高级的生成器的send方法之前,让我们先学习一些简单而方便的东西。与列表理解类似,我们可以使用生成器理解用一行代码创建一个生成器。唯一的区别是我们需要将括号改为圆括号:

正如我们所见,生成器理解与列表理解非常相似。你只需要把括号换成圆括号。但是,使用 list comprehension,所有数据都被加载到内存中,这可以通过创建的变量的大小反映出来。另一方面,它不是生成器理解的情况,这使得它更加节省内存。

了解发电机的send方法

大多数情况下,不需要使用生成器的send方法。在用 Python 编码的这些年里,我从来没有机会使用它。然而,理解它是如何工作的有助于为生成器添加类型注释。此外,如果你想了解 Python 中的 asyncio 库是如何工作的,这也很重要,因为协程是由幕后的生成器实现的。

让我们更新生成器,让它接受用户发送的值。该值将用于更改生成器函数中的stop变量,这样我们可以生成更多的值:

注意,发送给生成器函数的值是由yield语句接收的。您可以将yield语句的返回值赋给一个变量,并相应地使用它。要向生成器发送一个值,只需调用生成器上的send()方法:

请注意,您只能在生成器已经产生某些东西之后向它发送数据,否则,您将看到一个TypeError:

生成器的类型注释

最后,让我们向上面创建的生成器函数添加类型注释。在函数中添加类型注释可以让你的代码更健壮,更容易理解。不用读函数体就能知道返回类型。

如果一个生成器函数既包含了yieldreturn关键字,也可以接受外部发送的值,那么我们需要使用泛型Generator[YieldType, SendType, ReturnType]:

注意对于*args,我们只需要为一个参数添加类型注释。更多细节请参考本讨论

在大多数情况下,我们的生成器只会产生值。在这种情况下,我们可以将SendTypeReturnType设置为None:

或者,当一个生成器只产生值时,我们可以将返回类型标注为Iterable[YieldType]Iterator[YieldType],这对于那些不理解生成器的send方法的人来说更简洁,更容易混淆:

在这篇文章中,我们介绍了 iterables、iterators 和 generators 的技术细节,它们可以让您高效地处理需要大量资源(尤其是内存)的大型数据集。提供了一些简单的代码片段,可以帮助您理解迭代器和生成器的神奇方法,您通常将它们用作黑盒。发电机更受关注,因为它更简单,应用更广泛。我们已经揭开了send方法及其类型注释的神秘面纱。有了这些知识,您也可以理解更高级的特性,比如 Python 中的 asyncio。

相关文章:

揭开机器学习模型选择的神秘面纱,逐步指南

原文:https://towardsdatascience.com/demystify-machine-learning-model-selection-e3f913bab7e7

利用交叉验证、性能指标和总运行时间来确定最适合您的数据的模型

弗拉季斯拉夫·巴比延科在 Unsplash 上的照片

什么是选型?

机器学习中的模型选择就是为你的数据选择最好的模型。不同的模型在不同的数据集上会有不同的表现,而且差距可能很大。如今,梯度增强树是表格数据的最佳执行模型,例如 XGBoost ,或者 SciKit Learn 中的实现,这是很常见的。但是,不要总是默认使用 XGBoost 之类的模型,重要的是要评估不同算法的性能,看看哪种算法最适合您。

此外,不同的模型也有一些优势。例如,逻辑回归可以告诉你模型的系数,让你解释每个特征对最终预测的影响。像 RandomForest 这样的袋装树模型可以告诉你模型中每一列的特征重要性,类似于 Logistic 回归的系数。

让我们来看看如何在您选择的评分标准和训练速度之间选择最佳模型。

入门指南

对于我们今天的演示,我们将使用银行营销 UCI 数据集,您可以在 Kaggle 上找到该数据集。该数据集包含有关营销活动中银行客户的信息,并且包含一个可以在分类模型中使用的目标变量。该数据集在 CC0: public domain 下的 Public Domain 中,可以使用。

有关构建分类模型的更多信息,请查看:构建令人惊叹的二进制分类器所需要知道的一切超越了具有多类和多标签模型的二进制分类

我们将从导入必要的库和加载数据开始。我们今天将利用 Scikit-Learn 进行分析。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from timeit import timeit

import warnings
warnings.filterwarnings('ignore')

from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.compose import ColumnTransformer

from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC

from sklearn.compose import make_column_selector as selector
from sklearn.pipeline import Pipeline

接下来,我们将数据加载到一个熊猫数据帧中,并观察它的形状。

df = pd.read_csv("bank.csv", delimiter=";")
df.shape
(4521, 17)

数据清理

我们有4500 行17 列的数据,包括目标变量。在执行我们的模型选择之前,我们将对数据进行一些简单的清理,首先寻找空值,实际上没有空值,并删除任何重复的

# check for nan/null
df.isnull().values.any()
# drop duplicates
len(df.drop_duplicates())

接下来,在这个特定的数据集中,我们需要删除持续时间列。如文件中所述,该列对目标变量的结果有很大影响,因此,应从培训中排除。

****持续时间:最后一次联系的持续时间,单位为秒(数字)。重要注意事项:该属性对输出目标有很大影响(例如,如果 duration=0,则 y='no ')。然而,在执行呼叫之前,持续时间是未知的。还有,结束通话后 y 显然是已知的。因此,该输入应仅用于基准测试目的,如果目的是获得现实的预测模型,则应丢弃。

df.drop(columns='duration', inplace=True)

数据准备

接下来,让我们利用简单的 python 切片将数据分成Xy两个集合。因为我们的目标变量是最后一列,所以我们可以只取最后一列作为我们的X数据,只取最后一列作为我们的y数据。

X = df.iloc[:, :-1]
y = df.iloc[:,-1]

我们的y列是带有yesno值的二进制列。最好利用 Skikit-Learn 中的LabelEncoder将这些编码到10中。

enc = LabelEncoder()
enc.fit(y)
y = enc.transform(y)

接下来,我们将利用列转换器将我们的数据转换成机器学习可接受的格式。每当我为可重复性构建模型时,我更喜欢使用管道。关于它们的更多信息,请查看我的文章:停止一步一步地构建你的模型。利用管道实现流程自动化!

对于我们的转换,我们为数字特征选择了MinMaxScaler,为分类特征选择了OneHotEncode (OHE)。OHE 将分类数据转换为二进制表示形式,防止模型预测序数值之间的值。更多关于 OHE 的信息,请查看:一个热门编码

column_trans = ColumnTransformer(transformers=
        [('num', MinMaxScaler(), selector(dtype_exclude="object")),
        ('cat', OneHotEncoder(), selector(dtype_include="object"))],
        remainder='drop')

为模型选择创建模型列表

现在我们要用我们不同的模型建立一个字典。字典中的每个条目由作为型号名称和作为管道组成。

模型选择的想法是挑选性能最佳的模型,而不是调整模型以获得最佳性能。这就是所谓的超参数调整,您可以在这里了解更多信息:【HalvingGridSearch 使超参数调整速度提高了 5 到 10 倍。

因此,我们将用默认参数实例化每个模型。一个例外是,我总是倾向于使用可用的class_weight='balanced'参数。这是一种简单的方法来抵消不平衡数据带来的问题。在这里阅读更多关于处理不平衡数据的内容:在构建你的 ML 模型的时候不要陷入不平衡数据的陷阱

def get_models():
    models = dict()

    models['Logistic Regression'] = Pipeline([('prep', column_trans), 
        ('model', LogisticRegression(random_state=42, max_iter=1000, class_weight='balanced'))])

    models['Decision Tree'] = Pipeline([('prep', column_trans), 
        ('model', DecisionTreeClassifier(random_state=42, class_weight='balanced'))])

    models['Random Forest'] = Pipeline([('prep', column_trans), 
        ('model', RandomForestClassifier(random_state=42, class_weight='balanced'))])

    models['Extra Trees'] = Pipeline([('prep', column_trans), 
        ('model', ExtraTreesClassifier(random_state=42, class_weight='balanced'))])

    models['Gradient Boosting'] = Pipeline([('prep', column_trans), 
        ('model', GradientBoostingClassifier(random_state=42))])

    models['Hist Gradient Boosting'] = Pipeline([('prep', column_trans), 
        ('model', HistGradientBoostingClassifier(random_state=42))])

    models['AdaBoost'] = Pipeline([('prep', column_trans), 
        ('model', AdaBoostClassifier(random_state=42))]) 

    models['SGD'] = Pipeline([('prep', column_trans), 
        ('model', SGDClassifier(random_state=42, class_weight='balanced'))])

    models['SVC'] = Pipeline([('prep', column_trans), 
        ('model', SVC(class_weight='balanced', random_state=42))])

    models['Nearest Neighbor'] = Pipeline([('prep', column_trans), 
        ('model', KNeighborsClassifier(3))])

    models['Perceptron'] = Pipeline([('prep', column_trans), 
        ('model', Perceptron(random_state=42))])

    return models

交叉验证

在训练模型时,不要让模型过度适应您的数据或允许它一次看到所有数据,这一点很重要。通常你会对你的数据进行训练测试分割;然而,在这种情况下,我们将使用交叉验证方法,利用RepeatedStratifiedKFold方法找到最佳模型,以处理将数据划分到多个训练和测试集。

****分层采样确保相对类别频率在每个训练和验证折叠中大致保持不变,对于不平衡数据至关重要。关于这种方法的更多信息,请查看:交叉验证:评估评估者的表现

我们将构建一个可重用的函数,允许我们测试存储在字典中的不同模型。根据数据集的大小,这里有几个参数可以使用。您可以确定分割重复的次数。如果您有一个像本例这样的较小数据集,请尽量不要多次分割数据,否则您将没有足够的样本来进行训练和测试。

此外,您需要指定想要使用的评分标准。Scikit-Learn 支持许多不同的工具,您可以在他们的文档中看到如何引用它们。对于这个例子,我选择了 ROC-AUC 作为我的指标。有关选择最佳度量的更多信息,请查看:停止使用准确性来评估您的分类模型

# evaluate a give model using cross-validation
def evaluate_model(model, X, y):
    cv = RepeatedStratifiedKFold(n_splits=5, 
                                 n_repeats=10, 
                                 random_state=1)
    scores = cross_val_score(model, X, y, 
                             scoring='roc_auc', 
                             cv=cv, n_jobs=-1)
    return scores

评估模型

现在我们可以进行评估了。我们将调用evaluate_model函数遍历字典,并将结果存储在一个列表中。我们将对模型的名称做同样的处理,以便于我们绘图。

每次评估模型时,我们还会使用神奇的命令%time检查模型的速度,它会打印出评估模型所花费的时间,帮助我们进行选择。我们还打印出十次重复的平均分数标准偏差分数。

最后,我们将利用分数的盒须图在单个图上绘制结果。

# get the models to evaluate
models = get_models()

# evaluate the models and store results
results, names = list(), list()
for name, model in models.items():
    %time scores = evaluate_model(model, X, y)
    results.append(scores)
    names.append(name)
    print('* %s Score = %.3f StdDev = (%.3f)' % (name, np.mean(scores), np.std(scores)), '\n')

# plot model performance for comparison
plt.figure(figsize=(10,8))
plt.boxplot(results, labels=names, showmeans=True)
plt.xticks(rotation=45)
290 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Logistic Regression Score = 0.721 StdDev = (0.025) 

204 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Decision Tree Score = 0.573 StdDev = (0.021) 

1.61 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Random Forest Score = 0.730 StdDev = (0.024) 

1.68 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Extra Trees Score = 0.701 StdDev = (0.021) 

2.75 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Gradient Boosting Score = 0.756 StdDev = (0.021) 

2.04 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Hist Gradient Boosting Score = 0.728 StdDev = (0.021) 

886 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* AdaBoost Score = 0.733 StdDev = (0.023) 

212 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* SGD Score = 0.690 StdDev = (0.031) 

4.01 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* SVC Score = 0.715 StdDev = (0.027) 

660 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Nearest Neighbor Score = 0.608 StdDev = (0.022) 

127 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Perceptron Score = 0.639 StdDev = (0.043)

作者图片

在这里,我们可以很好地看到每个型号的性能。某些算法表现不佳,我们可以在这个用例中丢弃它们,例如简单的决策树、最近邻分类器感知器分类器。这些都是列表中一些比较简单的模型,它们的表现比其他的差并不奇怪。梯度提升树是表现最好的分类器,ROC-AUC 得分为0.756,名副其实。AdaBoost** 和 RandomForest 紧随其后,分别获得0.7330.730的分数。**

我们还可以看看运行所用的时间。在这些模型中,梯度增强树2.75秒时表现最慢,而 AdaBoost886毫秒时表现最好。看逻辑回归;然而,它在0.721时表现相当好,但在290毫秒时非常快,这可能会影响我们的选择过程。通过利用其系数,逻辑回归具有高解释能力的优点,并且在梯度增强树的大约 10%的时间内执行。

最终的选择取决于你,但是这些方法应该给你一个强大的基线来为你的用例选择最好的模型!

本文的所有代码都可以在 GitHub 上获得

结论

****模型选择是你建立机器学习模型的关键一步。选择正确的模型会极大地影响机器学习模型的性能,而选择错误的模型会给你留下无法接受的结果。我们通过利用管道来实现一致性,完成了准备数据的过程。然后,我们建立了一个模型列表,我们希望评估他们的表现。我们使用交叉验证在各种数据切片上测试每个模型,并最终绘制出结果。利用这一过程是为您的应用选择正确型号的快速而有效的方法!

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果您使用我的链接 注册,我会为您赚取一小笔佣金,无需额外费用。

揭开高效自我关注的神秘面纱

原文:https://towardsdatascience.com/demystifying-efficient-self-attention-b3de61b9b0fb

实用概述

图片作者。人工智能-使用达尔-E-2 生成

介绍

Transformer 架构[1]对于近年来深度学习领域的一些最大突破至关重要。特别是在自然语言处理(NLP)领域,预训练的自动编码模型(如 BERT 2)和自回归模型(如 GPT-3 [3])一直在努力超越最先进的技术,并达到类似人类的文本生成水平。Transformer 最重要的创新之一是使用关注层作为路由信息的主要方式。

顾名思义,注意力的目标是让模型关注输入的重要部分。从人类的角度来看,这是有意义的:当我们看一个输入(例如,一个图像或一个文本)时,有些部分对我们的理解来说比其他部分更重要。我们可以将输入的某些部分相互联系起来,并理解长期的背景。这些对于我们的理解都是必不可少的,注意力机制允许变形金刚模型以类似的方式学习。虽然这已被证明是非常有效的,但注意机制有一个实际问题:它们与输入长度成二次方关系。幸运的是,有很多研究致力于提高注意力的效率。

这篇博文旨在通过直观的解释,对不同类型的高效注意力提供一个全面的概述。这并不是对已经撰写的每篇论文的完整概述,而是对底层方法和技术的覆盖,并带有深入的示例。

注意力入门

在深入研究具体方法之前,让我们先回顾一下自我关注机制的基础知识,并定义一些将在这篇博文中重复使用的术语。

自我关注是一种特殊类型的关注。常规注意和自我注意的区别在于,自我注意关注的是一个单一的序列,而不是将输入与输出序列联系起来。它允许模型让序列学习关于它自己的信息。举个例子,就拿“那个人走到河边,他吃了一个三明治”这句话来说吧。与以前的嵌入方法(如 TF-IDF 和 word2vec [4])相比,自我关注允许模型学习“河岸”不同于“金融银行”(上下文相关)。此外,它允许模型学习“他”指的是“那个人”(可以学习依赖性)。

自我关注可以解释如下。假设我们有一个长度为 n 的序列 x,x 中的每个元素都用一个 d 维向量表示。在 NLP 的情况下,x 将是句子的单词嵌入。x 通过三个(训练的)权重矩阵 WQ、WK 和 WV 被投影,输出三个矩阵:Q、K 和 V,所有维度都是 n*d。自我关注可以被定义为下面的一般公式:

方程式 1: 广义注意力

最常用的得分函数是 softmax。如2所述,取 softmax 并应用一个比例因子会导致比例点积注意(SDP):

方程式 2: 缩放-点积注意

在这里,输入 x 的关注度是通过将 Q 乘以 KT(将每个项目与每个其他项目相关联)、应用缩放因子、获取逐行 softmax(归一化每个行)、将 V 中的每个值乘以其计算出的关注度来计算的,因此我们的输出再次是 n*d。因此,Q 和 K 用于将每个元素与每个其他元素相关联,而 V 用于将 softmax 的输出分配回每个单独的元素。

作者认为,对于较大的 d_k 值,点积变得非常大,这反过来将 softmax 函数推到梯度非常小的区域。因此,点积通过除以√(d_k)来缩放。请注意,这对计算的复杂性没有影响,因为这是由 softmax(QK)计算决定的。由于这个原因,它将被排除在一般公式之外。

正如你可能已经看到的,这个公式有一个问题:Q 和 K 相乘得到一个 nn 矩阵。取 nn 矩阵的行方式 softmax 具有 O(n)的复杂度。这对于运行时和内存使用都是有问题的,因为 n 可能非常大。对于多页文档,很快就变得无法计算完整输入的自我关注,这意味着输入必须被截断或分块。这两种方法都去除了自我关注的一个主要好处:长期背景。这种类型的注意力,其中每个项目都与其他项目相乘,被称为“整体注意力”,可以形象化如下:

图一:全球瞩目。图片作者。

在这里,对角线中的每个项目(深蓝色)查看其行和列中的所有其他项目(以浅蓝色突出显示)。

为简单起见,以下定义将在下文中使用:

等式 3: P 和 A 的定义

在这里,P 指的是 Q 和 K 相乘的结果 n*n 矩阵,A(自我关注矩阵)指的是 P 的 softmax,注意大多数论文使用自己的定义,这可能会有点混乱。

关于注意力的更详细的解释,我鼓励你去读读插图变压器

可供选择的事物

降低 SDP 复杂性的主要假设是,并非输入的所有部分都同等重要,并且一些记号不需要关注其他特定记号。

为了避免计算全球注意力,有几种选择:

  • 稀疏注意力:稀疏注意力方法稀疏化全局注意力矩阵,以减少必须相互关注的标记的数量
  • 矩阵分解:矩阵分解方法的工作原理是注意力矩阵是低秩的,可以用低秩矩阵进行分解和近似,而不会丢失太多信息。
  • 位置敏感哈希:位置敏感哈希提供了一种快速计算最近邻搜索的方法。这可以直接应用于关注矩阵,以选择哪些令牌应该相互关注。
  • 内核关注:内核关注方法将 softmax 函数解释为内核,并使用它来更有效地计算自我关注矩阵。

所有这些选择都以牺牲一些性能为代价,降低了计算复杂度。注意,所有这些方法都试图降低序列长度 n 的复杂度。为此,所有复杂度都降低到依赖于 n 的部分。

稀疏的注意力

稀疏注意力方法通过仅考虑 n*n 自我注意力矩阵 p 中的计算子集来降低复杂性。其思想是记号不需要注意每一个其他记号,而是可以关注更重要的记号而忽略其他记号。那么问题就变成了:我们如何挑选要关注的令牌?

局部注意 O(n*W)

局部注意,也称为窗口式 / 滑动注意,是一种简单而有效的稀疏化自我注意矩阵的方法。在局部注意中,标记只关注它们的局部邻域或窗口 w。因此,不再计算全局注意。通过只考虑 W 中的令牌,它将复杂度从 nn 降低到 nW,这可以如图 2 所示。

随机注意 O(n*R)

在随机注意中,标记只注意随机的其他标记。复杂度取决于所选随机记号的数量(R),它是所有记号的比率。这可以从图 2 中看到。

图 2 :局部注意(左)和随机注意(右)。图片作者。

稀疏变压器 O(n√n)

稀疏变压器[5]是减少自我关注复杂性的第一个尝试。作者提出了两种稀疏注意模式:步进注意和固定注意,这两种模式都将复杂度降低到 O(n√n)。他们的两种注意力类型可以使用以下函数来定义:

  • 跨步注意:如果
    (i+s) > j > ( i-s)或(i-j) mod s = 0 ,则第 I 个位置可以关注第 j 个位置
  • 固定注意:如果 floor(j/s) = floor(i/s)或(j mod s) ≥ (s-c) ,第 I 个位置可以注意到第 j 个位置

其中 s 是步幅(设置为√n ), c 是超参数。这些算法的复杂性为 O(n*s),当 s 设置为√n 时,这导致 O(n√n)。跨步注意力类似于具有跨步的局部注意力,作者认为这对于从具有周期性结构的数据(如图像或音乐)中学习非常重要。但是,对于没有周期性结构的数据(如文本),这种模式可能无法将信息路由到远处的项目。固定注意力是解决这个问题的方法。它让一些项目关注整个列,并创建一个“摘要”传播给其他项目。两种不同模式的可视化如图 3 所示。

图 3 :跨步注意(左)和固定注意(右)。图片作者。

龙前 O(n)

Longformer [6]使用了滑动(或局部)、扩张滑动和全局注意的组合。扩张滑动注意是基于扩张 CNN 的想法。扩大滑动注意的目标是逐渐增加每一层的感受野。作者提出在较低层使用局部注意,窗口 W 较小(可以看作是间隙 d 为 0 的扩张滑动窗口注意),在较高层增加 W 和 d。

仅针对特定令牌添加全局注意。将哪些令牌设为全局的选择由用户决定。分类的一个合理选择是使[CLS]标记全局化,而对于 QA 任务,所有问号标记都可以全局化。他们算法的复杂度是(nW + sn),它与序列长度 n 成线性比例,因此简化为 O(n)。

请注意,Longformer 的实现需要一个定制的 CUDA 内核,因为现代 GPU 针对密集矩阵乘法进行了优化。作者提供了一个定制的 CUDA 内核,允许在 PyTorch 和 Tensorflow 中的 GPU 上有效计算他们提出的稀疏矩阵乘法。

图 3 :局部注意(左)、扩张滑动注意(中)、全局注意(右)。图片作者。

矩阵分解

在矩阵分解(或分解)方法中,矩阵 P 被假定为低秩的,这意味着矩阵中的所有项并不是彼此独立的。所以可以用更小的矩阵来分解和近似。这样,nn 矩阵可以简化为 nk(其中 k< n), which allows us to compute A (the result of the softmax) much more efficiently.

l 前一个 O(n)

Linformer [7]的作者提出使用注意力矩阵的低秩分解来达到 O(n)的复杂度。作者首先从经验上表明,当应用奇异值分解(SVD)时,A 可以从它的前几个最大奇异值中恢复,这表明它是低秩的。然后,他们使用约翰逊-林登斯特劳斯引理(JL)证明 A 可以近似为低秩矩阵γ,误差非常小,该引理表示:

高维空间中的一组点可以被投影到低维空间中,同时(几乎)保持点之间的距离。

作者指出,计算每个自我关注矩阵的奇异值分解增加了额外的复杂性。相反,作者在 V 和 K 之后添加了两个线性投影矩阵,这有效地将原始(nd)矩阵投影到更低(kd)维矩阵,其中 K 是降低的维度。这可以被形象化,如图 4 所示:

图 4 :标准立正(上)和非标准立正(下)。图片作者。

他们提出的新的注意力公式如等式 3 所示:

等式 3: 前注意函数

这里,Ei 和 Fi 是两个线性投影矩阵。请注意,要将 A 从 nn 减少到°( n * K ),只需将 K 投影到维度 K。由于 V 仍然是 nd,因此 V 也被投影到维度 K,以确保最终的输出矩阵是 n*d(这是下一层的预期维度)。

这实际上是通过线性投影减少了序列长度 n。这对于 NLP 是有意义的,因为一个句子中的所有单词并不是(同等地)相关的。

最后一步是选择 k 的值。作者表明 dlog(d)的值对于 k 是足够的,这导致 O(nk)的复杂度。因为 d 相对于输入长度 n 不增加,所以自我注意机制的复杂度变为 O(n)。

纽约变压器

ny strm former[8]使用 ny strm 方法来近似自我注意矩阵。想法是将矩阵 P 重写为一个由四部分组成的矩阵:B 是 m*m,其中 m 是某个数字< n), C, D, and E. This is shown on the left in Figure 5:

图 5 :矩阵近似的 Nystrom 方法解释。图片作者。

根据 nyströ方法,p 可以近似为 P̃,方法是用 DB⁺C 代替 e(其中 B⁺是 b 的摩尔-彭罗斯伪逆),然后可以进一步简化,如图 5 所示。原始的 nn 矩阵现在被分解为两个 nm 矩阵和一个 m*m 矩阵的乘积。这极大地减少了计算量,因为只有选定的 K 行和 Q 列需要相乘来创建这个分解(而不是所有的行和列)。

为了更好地理解这是如何工作的,让我们使用 nyströ方法近似子矩阵 e 中的单个元素 eᵢ,ⱼ。假设 P 是一个 5*5 的矩阵,我们选择 B 作为我们的第一个单元格(1,1),C 作为第一行(2,1 到 5,1),D 作为第一列(1,2 到 1,5)。这可以被形象化,如图 6 所示。假设我们想知道 e₃,₃的值,它是 c₃,₃和 d₃,₃的乘积(在 SDP 中,这将是 q 和 k 中单个值的乘积)。在 P̃,我们不再有 c₃,₃和 d₃,₃的实际乘积,但是我们知道 c₃,₁and d₁,₃(as 的值,这些值在我们选择的行和列中。为了逼近 e₃,₃,我们将 c₃,₁and d₁,₃的值乘以 b 的倒数。正如您所看到的,我们可以用 q 中的一行和 k 中的一列的结果来逼近 e 中的任何值

图 6 :矩阵近似的 Nyströ方法示例。图片作者。

虽然本例中选择了 Q 和 K 的第一行和第一列,但也可以对多行和多列进行采样,称为“界标”,本文中就是这么做的。使用分段平均值选择这些界标,这类似于局部平均池(将输入分成段并取每个段的平均值)。

然而,仍然存在一个问题:为了计算关注矩阵 a,需要首先计算 p,因为 softmax 运算通过取 p 中整行的内容来归一化 a 的元素。由于目标是避免计算 p,所以作者提出了一个变通方法:他们对 P̃的三个子矩阵进行 softmax 运算,并将它们相乘,如等式 3 所示:

方程 3: 纽斯特罗姆注意力函数

这里,Z*是 B⁺.的近似值

虽然这在技术上是不允许的,因为 softmax 是一个非线性操作,但作者表明,这种方法提供的近似仍然是足够的。

局部敏感散列法

位置敏感哈希(LSH)是一种可用于高效近似最近邻搜索的技术。LSH 的思想是可以选择哈希函数,使得对于高维空间 p 和 q 中的任意两点,如果 p 接近 q,那么 hash(p) == hash(q)。使用该属性,所有点都可以被划分到散列桶中。这使得更有效地找到任何点的最近邻居成为可能,因为只需要计算到相同散列桶中的点的距离。在自我关注的情况下,这可以用于通过对 Q 和 K 应用 LSH 来加速 P 的计算,并且在应用 LSH 之后仅将彼此接近的项相乘,而不是执行完整的计算 QK。

重整器 O(nlog(n))

《改革家》[9]的作者是第一个提出利用 LSH 进行有效的自我关注的人。他们注意到,由于 softmax 由最大的元素支配,所以对于 Q 中的每个查询 qi,qi 只需要关注 K 中最接近 qi 的键(或者在相同的散列桶中)。

为了更好地理解这是如何工作的,我们来看一个例子。假设我们有一个包含许多点的二维空间,如图 7 左侧所示。在自我关注的情况下,这些点就是 p 中的项目。颜色代表靠近在一起的点。为了将项目划分到散列桶中,通过原点绘制了许多随机超平面,如图 7 中的右侧所示。在这种情况下,绘制了两个超平面,称为 H1 和 H2。任何超平面都有一个正边(1)和一个负边(0)。然后,根据项目出现在每个超平面的哪一侧,将它们放入散列桶(在本例中为 4 个)。因此,哈希桶的数量由绘制的超平面的数量来定义。在这样做之后,项目只需要计算到它们自己的散列桶内的项目的距离(或者,在自我关注的上下文中,关注相同散列桶内的项目)。

图 7:LSH 的例子。图片作者。

正如在图 7 右侧的结果散列桶中可以看到的那样,很接近的项目可能仍然在不同的散列桶中结束。为了减轻这种情况,可以执行多轮散列,并将每个值分配给最常出现的散列。然而,这确实增加了算法的复杂性。作者表明,通过 8 轮散列,该模型达到了类似于全局注意力模型的性能。

作者使用了一种称为角度 LSH [10]的变体,它使用余弦距离来计算任意两点之间的距离。它们表明,两个非常接近的点很有可能会在同一个桶中结束。

将点分成桶后,按桶对点进行排序。但是,有些桶可能比其他桶大。最大的存储桶仍将主导内存需求,这是一个问题。出于这个原因,作者将桶分成固定的块,因此内存需求取决于块的大小。请注意,项目可能不会与桶中的其他项目在同一个块中结束。这些项目可以处理它们应该结束的块中的所有项目,但是不能处理它们自己(这给复杂性增加了一个小的恒定成本),如图 7 的最后一行所示:

图 7:LSH 注意事项说明。图片来源:[9]。

这有效地将复杂度降低到 O(n log n)。需要注意的重要一点是,由于不依赖于 n 而从复杂度中去除了 8 轮散列,因此引入了较大的 12⁸常数,这实际上导致重整器仅在输入序列非常长(> 2048)时变得更高效。

核心注意力

核是这样一种函数,它以某个低维空间中的两个向量 x 和 y 的点积作为输入,并返回某个高维空间中点积的结果。这可以概括为一个函数 K(x,y) =φ(x)ᵀφ(y),其中 k 是核函数,φ是从低维到高维空间的映射。在机器学习的背景下,支持向量机(SVM)是一个众所周知的例子。特别是对于有效的自我关注,内核方法的工作原理是 Softmax 可以被解释为内核并被重写,这样我们就可以避免显式计算关注矩阵 a。

表演者 O(n)

执行者[11]是基于一种叫做的机制,通过正正交随机特征(或 FAVOR+)。这个想法是,我们可以使用核方法来近似 softmax 函数。

通常,当应用核方法时,我们希望在高维空间中计算点积。这可以通过使用适当的核函数 K 来实现(例如我们在核 SVM 中所做的)。然而,执行者做相反的事情:我们已经知道我们的函数 k 是什么(非线性的 softmax),我们想要找到φ,以便我们可以计算φ(x)ᵀφ(y(它是线性的)。我们可以将它形象化,如图 8 所示。在左手边,我们看到我们的 LL 矩阵 A 乘以 V(注意,这只是公式 softmax(QKT)V),作者将序列长度称为 L 而不是 n)。相反,所提出的方法使用φ来直接计算φ(Q)= Q’和φ(K)= K’,这允许我们首先将 K 和 V 相乘,并且避免了矩阵 a 的高成本计算

图 8 :好感+关注的说明。图片来源:[11]。

主要的研究问题变成:我们如何找到φ?这个想法是基于随机傅立叶特征[12]。作者指出,大多数内核可以使用通用函数建模,如等式 4 所示:

等式 4: 用于内核建模的通用函数

这里,h 是 x 的某个函数,m 是定义近似精度的参数,ω₁…ωₘ是从某个分布 d 中抽取的随机向量(因此随机部分更倾向于+),而 f₁…fₗ是确定性函数。m 越高,近似值越好,因为它定义了绘制的ω数。

作者证明了 Softmax 核可以通过选择等式 5 中所示的值来近似:

等式 5: 用于逼近 Softmax 的函数

现在,我们可以通过φ函数传递我们的 Q 和 K,并得到结果矩阵的点积。这样做的结果就好像我们先将它们相乘,然后取 softmax。由于我们可以通过φ独立地传递 Q 和 K,我们现在可以先将 K 和 V 相乘。

然而,还有一个问题。与 softmax 不同,sin 和 cos 可以具有负值,这导致当 softmax 的实际值接近 0 时,近似值的方差变大。由于很多自我关注值接近 0,这是个问题。为此,作者建议使用不同的函数,即等式 6 中所示的函数,这些函数仅输出正值(因此有利于+的正部分)

等式 6: 用于逼近 Softmax (FAVOR+ version)的函数

最后,作者解释说,确保ωs 正交会导致更小的方差(因此正交部分更有利于+)。

自我关注的替代品

很明显,有很多研究致力于提高点产品注意力的效率。然而,还有另一种选择:完全不使用自我关注,而是使用一种更简单的方法在我们的令牌之间共享信息。最近有多篇论文提出了这个想法([13]、[14]、[15])。我们将讨论一个,因为所有这些论文的总体思路都非常相似。

FNet O(n)

FNet [15]是一种替代的变换器架构,它用离散傅里叶变换(DFT)完全取代了自关注模块。因此,除了前馈层之外,不再有可学习的参数。DFT 将信号分解成其组成频率。其定义如公式 7 所示:

等式 7: DFT 函数

其中 N 是组件的数量。当 N 为无穷大时,我们可以精确地创建原始信号。在 NLP 的上下文中,我们的信号是一个令牌序列。实际上,每个组件 n 都包含一些关于输入序列中每个标记的信息。

他们方法的有趣之处不在于他们使用 DFT,而在于他们应用线性变换来混合他们的令牌。他们还尝试了一种线性编码器,这与合成器模型的工作方式非常相似,甚至是一种完全随机的编码器。虽然线性编码器的性能稍高,但它有可学习的参数,因此比 FNet 慢。BERT-Base 在 GLUE 上的平均分仍然高得多,但是他们报告说训练时间提高了大约 7 倍。因为有许多可能的线性变换,所以有一个有趣的开放式研究问题,即什么是最适合变压器的。

基准

虽然本帖中讨论的所有论文都报告了它们关于输入序列长度 n 的理论复杂性,但在实践中,由于较大的常数(如 Reformer)或低效的实现,一些论文可能仍然不切实际。为此, Xformers 被用于计算许多方法的不同序列长度的内存使用和运行时间。注意,并不是所有讨论的方法都在 Xformers 中实现,不幸的是,BlockSparse 在我的 GPU(RTX 3090)上不工作。

长序列长度(512,1024,2048):

图 9 :各种高效注意力方法的长序列长度的内存使用和运行时使用。图片作者。

短序列长度(128,256):

图 10 :各种高效注意方法的短序列长度的内存使用和运行时使用。图片作者。

显然,对于更长的序列,所有方法都明显比 SDP 更有效。虽然这里比较的所有注意机制(除了 SDP)都与序列长度成线性比例关系,但有趣的是,由于常数和其他比例因子,这些机制之间仍然存在明显的差异。值得注意的是,Linformer 的伸缩性不如其他方法。另一个有趣的结果是 Nystromformer 的内存使用和运行时间。虽然它的伸缩性很好,如序列长度(512、1024、2048)的图表所示,但对于短序列(128 和 256),它实际上是最低效的方法。这可能是由于所选标志的数量,如[8]中所建议的,其值保持在 64。

有趣的是,对于最长 512 的序列长度,SDP 的性能与其他方法非常相似。唯一明显比其他方法更有效的方法是 FNet(傅立叶混合注意)。它几乎完全独立于序列长度,同时没有重要的常数需要考虑。

结论

考虑到基于变压器的模型日益增长的相关性,有效的自我关注仍然是一个活跃的研究领域。虽然它们看起来令人望而生畏,但大多数技术实际上可以追溯到您可能已经熟悉的更一般的数学概念。希望这篇博文既是对大多数相关技术的介绍,也是对它们的解释,帮助你更深入地了解这个领域。

参考

[1] Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan N. Gomez、Lukasz Kaiser 和 Illia Polosukhin。你只需要关注,2017。

2 Devlin,j .,Chang,m .,Lee,k .,& Toutanova,K. BERT:用于语言理解的深度双向转换器的预训练,2018 年。

[3] Brown,T. B .,Mann,b .,Ryder,n .,Subbiah,m .,Kaplan,j .,Dhariwal,p .,Neelakantan,a .,Shyam,p .,Sastry,g .,Askell,a .,Agarwal,s .,Krueger,g .,Henighan,t .,Child,r .,Ramesh,a .,Ziegler,D. M .,Wu,j .,Winter,c .,Hesse,c .。。阿莫代伊博士。语言模型是很少出手的学习者,2020。

[4]托马斯·米科洛夫、程凯、格雷戈·科拉多和杰弗里·迪恩。向量空间中单词表示的有效估计,2013。

[5]蔡尔德,r .,格雷,s .,拉德福德,a .,& Sutskever,I .用稀疏变压器生成长序列,2019。

[6] Iz Beltagy,Matthew E. Peters 和 Arman Cohan。Longformer:长文档转换器,2020

[7]王,李,B. Z,Khabsa,m .,方,h .,,马,h .林前:自我注意与线性复杂性,2020 .

[8] Xiong,y .,Zeng,z .,Chakraborty,r .,Tan,m .,Fung,g .,Li,y .,& Singh,V. Nystromformer:一种基于 Nystrom 的自我注意近似算法.2021.

[9]尼基塔·基塔耶夫、祖卡斯·凯泽和安塞尔姆·列夫斯卡娅。改革者:高效的变压器,2020。

[10]亚历山大·巴甫洛夫·安多尼、彼得·因迪克、蒂伊斯·拉霍文、伊利亚·拉赞施泰因和路德维希·施密特。角距离的实用和最佳 lsh,2015。

[11] Choromanski,k .,Likhosherstov,v .,Dohan,d .,Song,x .,Gane,a .,Sarlos,t .,Hawkins,p .,Davis,j .,Mohiuddin,a .,Kaiser,l .,Belanger,d .,Colwell,l .,& Weller,a .,重新思考表演者的注意力,2020 年。

[12]阿里·拉希米,本杰明·雷希特。大规模内核机器的随机特性,2007。

[13] Tay,y .,Bahri,d .,Metzler,d .,Juan,d .,Zhao,z .,& Zheng,c .合成器:在变形金刚模型中反思自我注意。2020.

[14]托尔斯提欣、霍尔斯比、科列斯尼科夫、拜尔、李、翟、安特辛纳、杨、施泰纳、基泽斯、乌兹科雷特、卢契奇、多索维茨基、《混合建筑:全建筑展望》,2021 年。

[15] Ainslie,j .,Eckstein,I .,& Ontanon,S. FNet:用傅立叶变换混合令牌,2021 年。

揭开评估的神秘面纱:基础

原文:https://towardsdatascience.com/demystifying-estimation-the-basics-5206532b6378

更多地了解人口

罗马法师在 Unsplash 上拍摄的照片

目录

  1. 简介
  2. 正态分布
  3. 中心极限定理
  4. 需要了解基础知识
  5. 结论

介绍

统计学是涉及数据的多个领域的重要基础。有两种主要的统计类型:

  1. 描述性统计:这种类型的统计有助于使用图形描述、总结和可视化数据。描述性统计的两个主要元素是集中趋势的度量,这完全是关于数据样本的中心位置,如均值、中值和众数。第二个是变化的度量,它是关于数据样本的分布,比如方差和标准差。但是,它不能帮助我们做出超越数据的结论。
  2. 推断统计:在大多数情况下,对人口做出结论是不可能的,因为这是昂贵的,需要时间和资源。例如,一位研究人员想知道一个青少年花在学习上的平均时间。收集地球上每个青少年的数据并得出结论是不可能的。所以剩下的唯一方法就是从样本中得出关于总体的结论。这就是所谓的推断统计学。推断统计学有两个主要元素:
  • 估计:它包括考虑样本参数并对总体参数做出结论。例如,如果随机选择的 10 名学生样本的平均值为 52,则计算总体参数(在本例中为青少年的平均学习时间)的取值范围。
  • 假设检验:它包括检查关于人口参数的声明的可信度。例如,一名研究人员声称,一名青少年平均每天学习 4 小时,10 名学生的平均学习时间为 3.2 小时。然后,假设检验有助于检查关于总体均值的主张是否正确。

在本文中,我们将深入探讨评估的概念。在此之前,我将解释一些支持性和基础性的概念,这将帮助您更好地理解评估的概念。

正态分布

正态分布|图片来自维基百科

存在许多可能的数据分布,例如偏斜的、双峰的、均匀的等等。正态分布是一种具有以下性质的分布:

  1. 钟形的
  2. 均值、中值、众数是一样的,都在中心。
  3. 平均对称
  4. 它是单峰的,即只有一种模式
  5. 曲线下的总面积为 100%

正态分布有一种变体,称为标准正态分布。它是一种均值为零,标准差为 1 的类型。z 得分是特定值偏离数据平均值的标准偏差数。使用 Z 分数,任何正态分布都可以转换为标准正态分布。

z 评分公式|来源:作者图片

对于正态分布,曲线下的值如下:

  • 1 标准偏差有 68%
  • 2 标准偏差有 95%
  • 3 标准偏差有 99.7%

中心极限定理

样本均值的抽样分布是一种使用从总体中抽取的特定大小的所有可能随机样本计算出的均值的分布。抽样误差是样本参数和总体参数(例如,样本均值和总体均值)之间的差值,因为样本不是总体的完美代表。

当从总体中提取特定大小的所有可能样本而不替换时,那么,

  • 样本平均值等于总体平均值。
  • 样本均值的标准差等于总体的标准差除以样本大小的平方根。

中心极限定理指出,随着样本大小 n 的增加,从总体中取出的样本均值的分布形状将接近正态分布。

中心极限定理是重要的,因为它是两个主要推断统计元素的基础:估计和假设检验。中心极限定理可以用来回答与样本均值相关的问题。有两个条件:

  1. 如果总体是正态分布,那么样本均值的分布将是任何样本大小的正态分布。
  2. 如果总体不是正态分布,样本量必须大于 30。

纵观全局,可以得出结论,68%的样本均值位于总体均值的一个标准差范围内。类似地,95%和 99.7%的样本均值位于总体均值的 2 和 3 个标准偏差内。为了找到标准偏差的确切数目,样本平均值远离总体平均值,计算 Z 得分,

样本分布的 z 分数|来源:作者图片

这里 X '是样本均值,mu 是样本均值的均值,sigma 是样本均值的标准差。它与前面的 z 得分公式相同,只是在抽样分布方面有所不同。

需要了解基础知识?

正如我们所了解的,估计就是在考虑样本的情况下寻找总体参数。正如上面在抽样分布和中心极限定理中提到的,任何样本均值都在总体均值的 X 标准差处。例如,95%的样本平均值落在总体平均值的两个标准偏差内,那么样本平均值 X’落在,

样本均值区间|来源:作者图片

颠倒上面的公式,对于任何 X ',下面提到的区间包含总体均值的概率为 95%,

人口平均值的区间|来源:作者图片

这个关系用于求总体均值的区间估计,只有事先了解基础知识才能理解。

结论

在本文中,我们学习了理解评估的基本概念。本文是评估系列的第一部分。我将发布一些其他的文章来解释评估的类型和进行评估的测试。

就这些了,伙计们。我希望你喜欢它!!

揭开 Python 中模块和包的神秘面纱

原文:https://towardsdatascience.com/demystifying-modules-and-packages-in-python-968c13b3b990

模块和包是任何大型项目的核心。我将展示一些涉及它们的技巧,比如如何组织包和创建名称空间。

照片由阿尔瓦罗·雷耶斯从 Unsplash 拍摄

当我在 Github 上检查复杂的项目时,我通常会发现自己迷失在无数的文件夹和源文件之间。这些报告的作者认为他们的定制对他们自己来说非常明显,这是完全可以理解的;可悲的是,我努力让自己对构建不同的文件夹和源文件有同样的感觉,但却没有用。

揭秘一些处理包和模块的常见反应如何?

在这个快速教程中,我花时间模拟了一个具有以下全局结构的项目:

作者图片

具体来说,我们项目的树状结构可以概括为:

我们最感兴趣的是superlibrary的内容:

Dataloader 处理所有类型的数据加载器。

Learners包含按学习类型划分的模型。

Preprocessor拥有熊猫、NumPy 和 Scikit-Learn 的各种预处理器模块。

Tests是您集中所有测试的地方。

Utils通常包含各种执行优化或充当成本函数的函数。

每个函数只是一个简单的打印函数,也可以作为一个整体实现。

我们将进行简单快速的实验来强调一些概念,并举例回答如下问题:

包装是如何制作的?

如何控制进口包装?

如何执行一个包作为主入口函数?

如何利用名称空间包?

实验

包装是如何制作的?

如果你检查我们的模拟项目的结构,你会注意到在我们的文件夹的不同层次传播__init__.py

包含此类文件的每个文件夹都被视为一个包。__init__.py 文件的目的是包含可选的初始化代码,这些代码在遇到不同级别的包时运行。

例如,让我们将自己定位在preprocessor文件夹中。我们将编写几行代码进行一些导入。

让我们在preprocessor/__init__.py 文件中写下以下几行:

让我们返回到预处理器目录的上一级(因此我们将查看preprocessorlearnerdataloadertestsutils目录),打开 Python 解释器并执行以下操作:

>>> from preprocessor import numpy_prep, pd_prep, sk_prep
>>> numpy_prep()
This is the numpy implementation for the preprocessor.
>>> pd_prep()
This is the Pandas implementation for the preprocessor.
>>> sk_prep()
This is the sklearn implementation for the preprocessor.
>>> ...

了解这里做了什么很重要;preprocessor目录中的__init__.py文件将我们需要在更高层次调用的所有函数粘在了一起。

通过这样做,我们为preprocessor包提供了额外的逻辑功能,节省了您的时间和更复杂的导入行。

如何控制进口包?

比方说,使用与前面相同的层次结构配置,您想要控制某个行为,该行为包括导入所有带有星号的内容。

你会写:

from module import * .

让我们通过添加一个新的__all__属性来修改preprocessor/__init__.py 文件:

__all__正在接收一个受限的属性列表。

看看如果我们在preprocessor目录之上运行解释器会发生什么:

>>> from preprocessor import *
>>> numpy_prep()
This is the numpy implementation for the preprocessor.
>>> pd_prep()
This is the Pandas implementation for the preprocessor.
>>> sk_prep()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sk_prep' is not defined
>>> ...

sk_prep函数被省略,不包含在__all__属性中。这保护了加载sk_prep的原始包免受任何意外行为的影响。

尽管一般不鼓励使用from module import *,但它还是经常在声明大量名称的模块中使用。

默认情况下,此导入将导出所有不以下划线开头的名称。但是,如果指定了__all__,则只导出明确说明的名称。

如果__all__被定义为空列表,则不会导出任何内容。如果__all__包含未定义的名称,导入时会抛出AttributeError

如何执行一个包作为主入口函数?

毫无疑问,你对写这样的东西很熟悉:

If __name__ == "__main__" :
...

把它带到另一个水平怎么样?可以将learner包作为主模块运行吗?

让我们转到learner文件夹:

接下来我们要做的是确保在导入三个包中的每一个时,我们同时导入与每种学习类型相关的函数。

对于clustering包,我们在clustering/__init__.py文件中写下几行代码:

同样为supervised_learning 包装:

对于reinforcement_learning包:

我们现在能够将所有必要的功能直接加载到学员目录中的新实现中。

我们创建一个新文件,命名为__main__.py:

我们返回到主superlibrary目录,因此我们将在learner目录的上一级,并运行以下内容:

Username@Host current_directory % python learner 
Methods for clustering :
This is the implementation for model1.
This is the implementation for model2.
Methods for reinforcement learning :
This is the implementation for model1.
This is the implementation for model2.
Methods for supervised learning :
This is the implementation for model1.
This is the implementation for model2.

万岁!属性似乎比你所习惯的做得更多。它可以超越一个简单的文件,控制整个包,因此您可以导入或运行它。

如何利用名称空间包?

在我们的最后一节中,假设您稍微修改了 utils 文件夹的内容:

所以在每个subpackage中有一个名为modules 的新模块,它没有__init__.py初始化文件。

也许我们应该从一开始就谈论你可能遇到的这种类型的场景,以及其行为非常值得怀疑。没有__init__文件的文件夹的行为几乎类似于一个包,但并不被视为包。

更技术上来说,它被认为是一个namespace 包。事实证明,我们可以用它来实现有趣的事情。

您可以创建一些包含在每个backend_connectorscustom_functions包中的公共名称空间,这样modules将作为一个single模块..

还不够信服?让我们在解释器中写一些高于前面提到的两个包的东西:

>>> import sys
>>> sys.path.extend(['backend_connectors','custom_functions'])
>>> from modules import cloud_connectors, optimizers
>>> cloud_connectors
<module 'modules.cloud_connectors' from 'path_to_current_directory/cloud_connectors.py'>
>>> optimizers
<module 'modules.optimizers' from 'path_to_current_directory/optimizers.py'>
>>> ...

通过在模块搜索路径中添加这两个包,我们从一种特殊的包中获益,这种包是为在一个公共名称空间下将不同的代码目录合并在一起而设计的,如图所示。对于大型框架,这可能非常有用。

这里有一个关于 Python 在导入时如何感知modules的技巧。

>>> import modules
>>> modules.__path__
_NamespacePath(['backend_connectors/modules','custom_functions/modules'])
>>> modules.__file__
>>> modules
<module 'modules' (namespace)>
>>> ...

您观察到一个联合的名称空间路径,一个缺失的__file__属性(如果它有一个的话,这个属性应该与它的__init__文件的路径相关联),以及它的类型的一个清楚的指示:名称空间。

为了充分利用名称空间包,特别是在这种情况下,您不能在任何一个modules目录中包含任何__init__.py文件。假设您真的添加了它们:

仔细观察当你试图合并modules目录时会发生什么:

>>> import sys
>>> sys.path.extend(['backend_connectors','custom_functions'])
>>> from modules import cloud_connectors, optimizers
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'optimizers' from 'modules' (path_to_project_directory/superlibrary/utils/backend_connectors/modules/__init__.py)
...

正如所料,当您将普通包视为名称空间包时,Python 无法加载您的函数。

结论

我希望这些内容能让你更清楚地了解一个适度复杂的项目的结构。这是一个热门话题,尽管它在社区中缺乏分享和写作的热情。关于 Python 如何处理包和模块,有太多东西需要学习。

目前的材料旨在让读者意识到将一个人的项目放在一个可读的层次结构中的重要性,以便其他每个用户都有信心使用它。

谢谢你的时间。

通过示例揭开 PyTorch 加权随机抽样器的神秘面纱

原文:https://towardsdatascience.com/demystifying-pytorchs-weightedrandomsampler-by-example-a68aceccb452

一种处理不平衡数据集的简单方法

最近,我发现自己在熟悉的情况下工作,处理一个非常不平衡的数据集,这影响了我的 CNN 模型在计算机视觉任务上的训练。虽然有各种方法可以实现这一点,但的研究发现,在不同数据集上训练 CNN 模型时处理类别不平衡得出结论,在几乎所有情况下,最佳策略是对少数类别进行过采样;增加了模型在训练期间看到这些类别的图像的频率。

然而,尽管这个想法看起来很简单,但在 PyTorch 中实现它通常需要与有点神秘的WeightedRandomSampler进行交互。关于WeightedRandomSampler的文档很少,既包括它是如何工作的,也包括如何设置参数以确保它按照我们预期的方式运行。尽管在过去已经使用了很多次,但是当我在很长一段时间没有使用它的时候,我经常发现自己在各种论坛和 StackOverflow 帖子中搜索,以确保我的设置是正确的。虽然有一篇很棒的博客文章提供了它如何在幕后实现的数学上的严格分析,但众所周知,我们人类往往不太理解概率论,因此很难仅从这一点获得直觉。

在这篇文章中,我将采取务实的方法来理解WeightedRandomSampler的行为,目的是回答以下问题:

  • 如何计算用于平衡数据集的权重?
  • 由于这种方法是基于概率的,我能确定这将以我想要的方式平衡数据集吗?
  • 在训练期间,我的所有数据集都会被看到吗?
  • 如果我不想均衡地平衡数据集,而是达到某个其他比率,该怎么办?

我们将通过在处理真实数据集的上下文中与WeightedRandomSampler对象进行交互来实现这一点,然后运行一个简单的实验来确定平衡数据集是否会为我们的简单问题带来任何性能改进。

Tl;dr: 如果你只是想看到一些可以直接使用的工作代码,复制这篇文章所需的所有代码都可以在这里GitHub gist中找到。

创建不平衡的数据集

首先,让我们下载一些数据作为例子。这里,我使用的是牛津宠物数据集,它包含了 37 种不同类别的猫和狗。在 Linux 上,我们可以用以下命令下载它:

wget [https://thor.robots.ox.ac.uk/~vgg/data/pets/images.tar.gz](https://thor.robots.ox.ac.uk/~vgg/data/pets/images.tar.gz) -P data/pets
ls data/pets
tar -xzf data/pets/images.tar.gz -C data/pets
rm data/pets/images.tar.gz

现在我们有了一些数据,我经常发现探索这个问题的一个好方法是创建一个熊猫数据框架。我们可以使用标准库中的pathlib来快速获得所有图像路径的列表,由于类名包含在每个文件名中,我们可以在同一个步骤中提取这些路径。

我们现在可以使用这些工具轻松创建一个数据框架,用于快速检查我们的标签分布情况:

由此,我们可以看到数据集非常均衡,几乎所有的类都有大约 200 张图片。虽然这通常是一件好事,但为了使数据集适合我们的目的,我们可以定义一个函数来提取不平衡的子集:

选择其中两个类别,我们可以用它来创建不平衡数据集,如下所示:

让我们用整数值对我们的类标签进行编码,并创建一个在我们的数据集中使用的查找。

在这里,我选择了暹罗猫伯尔曼猫品种,因为根据数据集网站上的预览图片,它们具有短暂的相似性。我们可以通过随机检查一些图像来确认这一点。

基于这一点,这些类似乎是一个简单但重要的任务的合适选择。

可视化批量分配

现在,我们已经定义了数据集,让我们通过检查模型在单个训练时期将看到的每一批的分布来探索类不平衡的影响。

因为此时我们不需要加载图像,所以让我们从每个图像的标签和索引创建一个张量数据集,这样我们就可以快速迭代。

我们可以定义一个函数来实现这一点,方法是跟踪每个批处理期间看到的类和索引,并绘制它们,如下所示:

我们现在可以用它来研究我们的数据是什么样子的。

由此,我们可以清楚地观察到不平衡的影响,因为一些批次根本不包含任何来自我们少数类的图像!此外,我们可以看到,数据集中的每幅图像都会在训练过程中出现。当我们对所有批次取平均值时,我们观察到与数据集中相同的比例,这是我们所期望的。

加权随机采样器平衡我们的数据集

现在,让我们看看如何使用WeightedRandomSampler来平衡数据集。

我们需要做的第一件事是计算将用于对每个图像进行采样的weights;从文档中,我们可以看到我们需要为数据集中的每幅图像设定一个权重。在我看来,最令人困惑的是这些权重的总和不一定是 1。实际上,这些权重代表了图像被选中的概率,PyTorch 只是为了方便起见,在幕后将这些权重缩放到[0,1]范围内。

现在我们已经了解了我们需要什么,让我们看看如何计算这些权重。首先,我们需要计算有多少图像属于我们的每个类,使用 Pandas 我们可以这样做,如下所示:

现在,我们有了类计数,我们可以通过取计数的倒数来计算每个类的权重。这将确保具有较高代表性的类将具有较小的权重。

现在,我们只需要根据类别为每个样本分配适当的权重。在实践中,我们可以直接从类计数中完成,如下所示:

接下来,我们可以创建我们的采样器和数据加载器:

在这里,我们可以看到我们已经提供了计算的样本权重作为参数,并将replacement设置为 True 否则,我们根本无法进行过采样。现在,我们只是将样本的数量设置为数据集的长度,但是我们将在后面对此进行更多的讨论。

再次,我们可以可视化我们的数据加载器批次的分布,这次使用WeightedRandomSampler

由此可见,我们的批次相当均衡!查看所看到的图像数量,我们可以看到采样器通过对少数类进行过采样而对多数类进行欠采样来实现这一点。

为了更详细地了解选择了哪些图像,我们可以创建一个数据帧,其中包含每个图像在这个时期被看到的次数。

为了使这些数据更容易解释,我们可以使用下面的代码片段将其表示为经验累积分布函数图:

由此可见,要达到我们想要的比例,我们少数民族班的每张图片至少被看了 5 次,有的多达 13 次!相比之下,我们多数班的很多图像根本就没看到!

你可能会注意到,我们多数班的一些图像被多次看到,而另一些根本没有看到,这似乎很奇怪,这似乎并不理想。不幸的是,这是基于概率的采样方法的一个折衷。虽然我们可以将WeightedRandomSampler设置为无替换采样,但这也会阻止我们过采样;所以对我们这里没用!

下一个合乎逻辑的问题是,我们能否确保在训练过程中看到每一张图像。

训练期间什么时候可以看到我的所有图像?

调整每个时期的样本数量

如上所述,来自我们多数班的 97 幅图像在训练期间不会被看到。原因在于我们在创建WeightedRandomSampler实例时定义的num_samples参数。由于我们指定样本数等于原始不平衡数据集中的图像总数,因此我们的采样器必须忽略一些图像才能对少数类进行过采样。

调整这个参数,将原始数据集的大小增加一倍,我们可以看到,在一个时期内,我们可以看到更多的图像。

然而,这确实给纪元的确切含义带来了一些混乱。在机器学习中,我们将一个时期定义为单次通过整个数据集。当每个样本只被看到一次时,这个定义使得准确地理解模型到训练中的当前点已经看到了什么变得非常清楚;当以不同的频率对图像进行采样时,这一点变得不太清楚。

由于历元的概念主要是为了帮助我们跟踪训练过程中的进展,而与模型本身没有关系——它只看到一个恒定的图像流——我更喜欢将num_samples设置为数据集的长度,并相信随着我们训练更多的历元,所有图像都会在某个点被看到。

在模型看到所有图像之前需要多少个历元?

将我们的采样器重置回其原始参数:

让我们探索一下在一次训练运行中需要多长时间才能看到我们的所有数据集。

为了做到这一点,我们可以建立一个简单的实验,我们跟踪在多个时期内看到的所有图像,并绘制每个时期结束时到目前为止的唯一图像的数量。我们可以使用下面的代码片段来做到这一点:

在这里,我们可以看到,直到所有图像至少被看到一次,需要 10 个历元。然而,由于这取决于概率,这个数字很可能会改变!

为了得到一个更稳健的估计,让我们从蒙特卡罗方法中得到启发,多次运行这个实验并观察分布。为了更容易解释,我们可以将它表示为核密度估计图

由此我们可以看出,在大多数情况下,大约需要 9-10 个历元才能确信所有数据都会被看到。

当然,这个估计高度依赖于底层数据集中不平衡的比例。似乎合乎逻辑的建议是,随着不平衡比率的增加,将需要更多的时代。我们可以通过对不同程度的不平衡重复我们的试验来证实这一点,如下所示:

从图中,我们可以看出我们的直觉是正确的。然而,有趣的是,当在平衡数据集上使用WeightedRandomSampler时,需要大约 5 个时期才能看到所有数据;这表明在这种情况下这并不理想!

获取不平衡的数据集比例

既然我们已经探索了如何使用 WeightedRandomSampler 来平衡我们的训练集,那么让我们简单地研究一下如何调整我们的类权重来达到我们想要的任何比例。

这可能有用的一个例子是在对象检测中,我们希望我们的大部分训练集中在包含我们希望检测的项目的图像上,但是可用的数据集通常包含大量的背景图像。

作为一个例子,让我们研究一下如何以另一种方式使数据集严重失衡;这样我们的少数阶级就占了主导地位。

同样,首先我们需要计算每个类别的样本总数:

接下来,我们可以为每个类别定义目标比例:

最后,为了计算我们的样品重量,我们只需将每个重量乘以我们相应的目标比例,如下所示:

像以前一样,让我们将这些重量传递到我们的采样器,并可视化我们的批次。

由此可以看出,我们已经获得了我们正在寻找的分布。

过采样能提高性能吗?

希望在这一点上,我们已经对WeightedRandomSampler如何工作有了一个直觉。然而,你可能会想,只是更频繁地向网络显示相同的图像真的会有所不同吗?让我们设置一个小实验来研究一下。

让我们在不平衡数据集上训练一个图像分类器。当然,结果在很大程度上取决于许多因素,例如使用的模型和数据集,但这只是一个简单的例子。在这里,我根据过去一直对我有效的训练食谱选择了以下内容:

  • 型号:ResNet-RS50
  • 优化器:AdamW
  • LR 调度程序:带预热的余弦衰减
  • 图像大小调整为 224

由于我们的数据集非常小,为了进一步简化,让我们只训练在我们的架构中用于分类的最终线性层;由于我们的图像非常类似于模型已经过预训练的 ImageNet 图像,因此可以安全地假设主干中学习到的特性在这里应该足够好。

为了进行评估,我们可以使用我们之前创建的验证集,它包含了在训练中没有看到的图像的平衡样本。

所有训练都是使用单个 NVIDIA V100 GPU 进行的。为了处理训练循环,我使用了 PyTorch 加速库。然而,由于 PyTorch-accelerated 处理所有分布式训练问题,相同的代码可以在多个 GPU 上使用——无需将WeightedRandomSampler更改为分布式采样器——只需通过定义一个配置文件,如这里所述

我们可以定义一个脚本来进行这个实验,如下所示:

使用的包:

通过运行以下命令,在训练 10 个时期后选择最佳指标:

我得到了以下结果:

哦,不,这看起来像过采样实际上使事情变得更糟!

然而,就我们实验的设置方式而言,这并不十分令人惊讶,因为我们将模型的曝光限制在来自多数群体的新图像,而倾向于重复显示少数群体的少量图像。如果有一种方法可以从 Birman 图像的小集合中获取更多信息就好了…

添加数据扩充

为了尝试并帮助模型从我们的图像中学习更多,我们可以在训练期间使用数据增强来生成每个图像的稍微修改的版本。

在这种情况下,我决定使用 timm 的预定义 RandAugment 策略,因为这需要最小的超参数调整;timm 的 RandAugment 实现是这里详细描述

将 RandAugment 应用于来自我们训练数据集的图像

让我们再次运行这个实验,这一次使用数据扩充:

结果如下所示:

这一次,我们可以看到数据扩充和过采样的结合导致了性能的显著提高!

结论

希望这已经提供了如何开始使用WeightedRandomSampler的全面概述,并有助于说明如何使用它。

复制这篇文章所需的所有代码都可以从 GitHub gist 中获得。

克里斯·休斯上的是 领英

参考

使用的数据集

揭开 ROC 和精确回忆曲线的神秘面纱

原文:https://towardsdatascience.com/demystifying-roc-and-precision-recall-curves-d30f3fad2cbf

揭穿关于二元分类的 ROC 曲线/ AUC 和精确召回曲线/ AUPRC 的神话,关注不平衡数据

受试者工作特征(ROC)曲线和精确召回率(PR)曲线是比较二元分类器的两种可视化工具。与此相关的是,ROC 曲线下面积(AUC,又名 AUROC)和 precision-recall 曲线下面积(AUPRC,又名 average precision)是用单个数字概括 ROC 和 PR 曲线的度量。在本文中,我们对这些工具进行了一些阐述,并把它们与不平衡数据(1 比 0 多)进行了比较。特别是,我们提出的论点是,对于不平衡数据,民间传说"PR 曲线优于 ROC 曲线,因为 ROC 可能会误导人或不提供信息"包含的真理比通常假设的要少。这是否正确取决于具体的应用环境,尤其是如何应用这些工具。更重要的是,PR 曲线同样可以很好地掩盖预测准确性的重要方面,并且在存在阶级不平衡时具有误导性。

混淆矩阵和两类错误

ROC 和 PR 曲线都是基于混淆矩阵。假设我们有一个二元分类器(算法或模型),样本大小为 n 的测试数据,我们使用分类器对测试数据进行预测。测试数据中的每个数据点要么是 0(“负”),要么是 1(“正”)。这是事实。此外,每个数据点被分类器预测为 0 或 1。这给出了四种组合:

  • 真正的否定(TN)是被正确预测为 0 的 0
  • 假阳性(FP)是被错误地预测为 1 的 0
  • 假阴性(FN)是被错误预测为 0 的 1
  • 真正的肯定(TP)是被正确预测为 1 的 1

混淆矩阵是一个(偶发事件)表,将测试数据中的所有实例分为以下四类:

图 1:混淆矩阵——作者图片

分类器会产生两种类型的错误:假阳性(当事实上是 0 时预测为 1)和假阴性(当事实上是 1 时预测为 0)。根据应用程序的不同,这两种类型的错误可能同等重要,或者这两种错误中的一种可能比另一种更严重。然而,人们通常不报告这两类误差的绝对数字,而是报告相对数字。这主要是因为相对数字更容易解释和比较。问题是:“相对于什么?”如果假阳性和假阴性同等重要,并且不想区分它们,可以简单地计算误差率= (FP+FN)/n,即误差总数 FP+FN 除以样本总数 n

如果要区分假阳性和假阴性,问题是这些数字应该与哪个基线量进行比较?可以说,最自然的参考量是 0(= TN+FP)和 1(= TP+FN)的总数。一种方法是考虑假阳性率 = FP / (TN + FP)和真阳性率 = TP / (TP + FN)。假阳性率是所有真 0 中错误预测 1 的比例。真阳性率也称为召回,是所有真 1 中正确预测 1 的比例。图 2 说明了这一点。

图 2:真阳性率(召回)和假阳性率——作者图片

将假阳性的数量与 0 的总数进行比较的另一种方法是使用所谓的精度将它们与预测的 1 的总数(= FP + TP)进行比较。精度 = TP / (FP + TP)是正确预测的 1 在所有预测的 1 中所占的比例。总之,假阳性率和精度之间的主要区别在于假阳性的数量与哪个参考量进行比较:真实 0 的数量或预测的 1 的数量。请注意,严格来说,精度将真实阳性与预测的 1 的总数进行比较。但这只是 1 - TP / (FP + TP) = FP 的另一面真实正利率也是如此。

图 3:precision——作者图片

图 4:混淆矩阵示例——作者图片

图 4 显示了一个混淆矩阵的例子。在本例中,错误率为 0.2,真阳性率为 0.2,假阳性率为 0.1,精度为 0.25。

假阳性率还是精度?具有不平衡数据的玩具示例

图 5:不平衡数据的两个分类器的混淆矩阵,易于分类——图片由作者提供

ROC 曲线和 PR 曲线的主要区别在于前者考虑了假阳性率,而后者基于精确度。这就是为什么我们首先仔细研究不平衡数据的这两个概念。当(真)0 的数量比假阳性的数量大得多时,假阳性率可能是一个小数字,这取决于应用。如果解读错误,这么小的数字可能会隐藏重要的见解。作为一个简单的例子,考虑图 5 中的两个混淆矩阵,其中两个分类器应用于具有 1'000'000 个点的数据集,其中 1'000 个点是 1。这两个分类器的真实阳性率分别为 0.8 和 0.85。此外,分类器 I 具有 500 个假阳性,而分类器 II 具有 2000 个假阳性。这意味着这两个分类器具有非常小的假阳性率,大约为。0.0005 和 0.002。就绝对值而言,这两个假阳性率非常接近,尽管事实上分类器 II 具有四倍多的假阳性。这是数据分类不平衡的结果数据相对容易分类的事实(= 可能同时具有高的真阳性率和低的假阳性率)。然而,分类器 I 的精度大约为。0.62,而分类器 II 的精度约为。0.3.即,就精度而言,分类器 I 明显优于分类器 II。小的假阳性率有时可以掩盖不平衡数据的分类器之间的差异,这一事实是支持 PR 曲线优于 ROC 曲线的论点的根源。我们将在本文的后面回到这个问题。

图 6:难以分类的不平衡数据的两个分类器的混淆矩阵—图片由作者提供

接下来,考虑一个相似的数据集,但是它更难分类。两个分类器的混淆矩阵如图 6 所示。两个分类器再次分别具有 0.8 和 0.85 的真实阳性率。此外,分类器 I 的假阳性率大约为。0.4,而分类器 II 的值约为 0.4。0.45.然而,两个分类器的精度现在几乎相等,大约。0.002.两个分类器的精度如此之小的原因是存在类别不平衡数据相对难以分类的事实。这表明,对于类别不平衡的数据,精度也可以掩盖分类器之间的重要差异 这个小例子的结论是,精度或假阳性率是否更能提供信息取决于具体的应用,而不仅仅取决于是否存在类别不平衡。

ROC 和 PR 曲线

为什么首先是曲线?

我们可以在这里停下来,简单地比较错误率、假阳性率、真阳性率、精确度或任何其他基于混淆矩阵的总结性度量。然而,在大多数情况下,这不是一个好主意。为什么不呢?我们必须后退一步,理解混淆矩阵是如何产生的。首先,分类器通常为每个测试点计算预测分数 p。通常,这是一个介于 0 和 1 之间的数字,有时这也可以解释为一个概率。第二,选择决策阈值δ,并预测 p > δ的所有实例为 1,所有其他实例为 0。这种阈值的一个例子是δ=0.5。然而,在许多应用中,对于使用δ=0.5 没有严格的论证,并且使用其他δ可以获得更好的结果。其中,潜在的原因是(I)分类器经常没有被校准(即,即使我们可能认为输出 p 是概率,但是这个概率 p 与事件实现的实际概率不匹配)以及(ii)在与假阳性和假阴性相关联的损失中存在不对称性。

由于这些原因,人们针对几个或所有可能的阈值δ来比较分类器。ROC 和 PR 曲线就是这么做的。较低的(较高的)设置阈值δ,较高的(较低的)是假阳性的数量,较低的(较高的)是假阴性的数量。即,在假阳性和假阴性之间存在权衡。

ROC 曲线和 AUC

接收器工作特性(ROC)曲线绘制了所有可能阈值δ的真阳性率与假阳性率的关系图,从而使上述权衡可视化。阈值δ越低,真阳性率越高,但假阳性率也越高。ROC 曲线越靠近左上角越好,斜线代表随机猜测。此外,ROC 曲线下的面积(AUC, aka AUROC ) 用一个数字概括了该曲线。AUC 越大越好。AUC 有这样的解释,例如,0.8 的 AUC 意味着分类器以 80%的概率正确地排列两个随机选择的测试数据点。

精确召回曲线和 AUPRC

精确度-召回率(PR)曲线绘制了所有可能阈值δ的精确度与召回率(=真阳性率)的关系。目标是同时具有高召回率和高精确度。类似地,在高精度和高召回率之间也有一个权衡:阈值δ越低,召回率越高,但是精度也越低。此外,精确度-召回曲线(AUPRC,又名平均精确度 ) 下的区域用单个数字概括了该曲线。AUPRC 越高越好。除此之外,与 AUC 相反,AUPRC 没有直观的解释。

“对于不平衡的数据,精确召回曲线将优于 ROC 曲线”——有这么简单吗?

有一个常见的民间传说是,对于不平衡的数据,PR 曲线和 AUPRC 应优先于 ROC 曲线和 AUC,因为 ROC 和 AUC 可能会误导或不提供信息 " (例如,见 此处 此处 此处 )但就这么简单吗?在下文中,我们将阐明这一点。

模拟数据实验

我们将使用模拟数据来详细探讨这一论点。具体来说,我们模拟了 2,000,000 个数据点,其中 1%的数据为 1(当使用另一个类别不平衡比率时,所有结果在性质上保持不变,例如 0.1% 1)。数据首先以相对容易获得良好预测结果的方式生成。我们用一半的数据进行训练,另一半进行测试。为简单起见,我们考虑两个分类器,它们都是使用不同预测变量子集的逻辑回归模型。复制本文模拟实验的代码可以在这里找到。

图 7 显示了 ROC 和 PR 曲线以及 AUC 和 AUPRCs。根据该图,分类器 1 的 AUC 较高,而分类器 2 的 AUPRC 较高。ROC 和 PR 曲线的情况类似:根据 ROC 曲线,一个可能得到分类器 1 更好的印象,但是 PR 曲线讲述了相反的故事。但是哪个分类器“更好”呢?

图 7:易于预测的不平衡数据的 ROC 和 PR 曲线示例—作者图片

有一种观点认为分类器 2 确实更好,因此 AUC 具有误导性。论点如下。在 ROC 图中,两条曲线相对较快地达到高的真阳性率,同时具有低的假阳性率。很可能,我们感兴趣的是假阳性率小的区域,比如说低于 0.2,在图 7 中用绿色矩形突出显示。为什么只有这个区域?当进一步降低决策阈值时,真阳性率将仅略微增加,而假阳性率将显著增加。这是阶级不平衡和数据容易分类的结果。对于对应于小假阳性率的决策阈值,当考虑 ROC 曲线时,分类器 2 确实也更好。

对于不平衡的数据,PR 曲线和 AUPRC 自动倾向于更多地关注假阳性率小的区域,即相对高的阈值δ。这就是为什么根据 PR 曲线和 AUPRC,分类器 2 更好。然而,假设我们先验地知道我们对小的假阳性率感兴趣,我们也可以通过仅关注小的阳性率来相应地解释 ROC 曲线,即,通过“放大”到具有小的假阳性率的区域。该区域的 ROC 曲线不再具有误导性。

另一方面,为了达到非常高的真阳性率,我们可能事实上愿意接受高的假阳性率。如果是这种情况,分类器 1,而不是分类器 2 更好,ROC 曲线和 AUC 都不会产生误导。在这种情况下,PR 曲线和 AUPRC 具有误导性。总之,对于完全相同的数据,AUC 和 AUPRC 都可能产生误导,哪一个给出的情况更好取决于具体的应用。

接下来,让我们看看相同的不平衡数据,唯一的区别是有更多的标签噪声,这意味着获得准确的预测更加困难。结果如图 8 所示。当观察 ROC 曲线和 AUC 时,很明显,对于该数据,分类器 1 优于分类器 2。然而,两个分类器的 PR 曲线和 AUPRC 几乎是不可区分的。由于数据难以分类,假阳性的数量很快变得相对较大。这与存在类别不平衡的事实一起是 PR 曲线和 AUPRC 未能发现两个分类器之间重要差异的原因。

图 8:难以预测的不平衡数据的 ROC 和 PR 曲线示例—图片由作者提供

比较 ROC 和 PR 曲线时的其他问题

1。重复检查预测阳性会有成本吗?

除了上面提到的事实之外,在实践中要考虑的另一件事是,有两种类型的成本可能发生:由于假阳性本身引起的成本和由于双重检查预测阳性引起的额外成本。在具有假阳性时发生成本而所有预测阳性中的假阳性的频率并不重要的情况下,因为例如当预测阳性时没有额外的成本发生,假阳性率比精度更重要。但是,也有假阳性率与精确度相比不太重要的应用,因为当预测阳性时会产生成本,因为例如所有预测的阳性都需要被双重检查。

垃圾邮件检测就是一个应用程序的例子,其中预测的阳性结果不会产生额外的成本,因为这是一个完全自动化的任务,无需人工干预。另一方面,在欺诈检测中,当分类器预测为“肯定”时,人们通常会进行额外的检查,这些检查通常涉及人工交互。在这种情况下,假阳性在所有预测阳性中所占的比例(=精确度)可以说是非常重要的,因为每个预测 1 都会直接导致成本。

2.AUC vs. AUPRC:可解释性重要吗?

在决定是使用 AUC 还是 AUPRC 时,还需要回答的一个问题是可解释性是否重要?如果没有,人们可以“盲目地”使用这两个度量来比较不同的分类器,并挑选具有最高数量的一个。如果一个人关心解释,情况就不同了。首先,除了 AUPRC 越高越好这一事实之外,AUPRC 没有 AUC 那样的直观解释(见上文)。第二,PR 曲线和 AUPRC 忽略了真正的负值(例如,见图 3)。这意味着不同数据集的 AUPRC 无法进行比较,因为 AUPRC 取决于数据中 0 和 1 之间的基本速率比。AUC 不是这样的,不同数据集的 AUC 是可比较的。

3.损失可以量化吗?

如果当有假阳性和假阴性时发生的损失可以量化,则可以使用统计决策理论来确定最佳决策阈值δ。当只有一个阈值时,事情就简化了:不需要使用 ROC 和 PR 曲线等曲线,而是可以使用误差率、假阳性率、真阳性率或精确度等度量。不幸的是,在许多情况下,这两类损失无法量化。

4.ROC 曲线和 PR 曲线可以得出相同的结论

如果一个分类器的 ROC 曲线总是在另一个分类器的 ROC 曲线之上,这同样适用于 PR 曲线,反之亦然(参见,例如,此处对此的证明)。在这种情况下,对于 ROC 和 PR 空间中的所有阈值,一个分类器比另一个更好,并且使用 ROC 曲线/ AUC 还是 PR 曲线/ AUPRC 来比较两个分类器通常并不重要。然而,一般来说,较高的 AUC 并不意味着较高的 AUPRC,反之亦然。

结论

说 ROC 曲线和 AUC 对于不平衡数据是误导性的或无信息的,意味着只有所有决策阈值的某个子集是感兴趣的:假阳性率小的那些。是否确实如此取决于具体的应用。如果是这种情况,AUC 确实会误导容易预测的不平衡数据,但 ROC 可以通过放大感兴趣的区域来简单地调整。此外,PR 曲线和 AUPRC 也可能是误导性的或无信息性的,当存在阶级不平衡且反应变量难以预测时,如上文所示。

本文的重点是比较 ROC 曲线/ AUC 和 PR 曲线/ AUPRC,以评估二元分类器。但是,由此得出的结论不应该是只使用这些工具中的一种。ROC 和 PR 曲线显示了不同的方面,并且很少有反对手头问题的额外观点的争论(除了当不同的观点不一致时做出关于哪个分类器更好的决定可能变得更加困难的事实)。还要记住的是,AUC 和 AUPCR 都考虑所有可能的决策阈值δ,同时给这些阈值的不同子集不同的权重。然而,对于一些应用,考虑所有阈值δ可能是不现实的,因为一些阈值可能被先验地排除。最后,注意 ROC 曲线和 PR 曲线“仅”考虑分类器正确排列不同样本的区分能力。有时,校准(=“预测的概率确实对应于预测事件实现的概率”)也很重要。如果是这种情况,需要额外考虑其他指标。

揭秘深度学习中的彩票假说

原文:https://towardsdatascience.com/demystifying-the-lottery-ticket-hypothesis-in-deep-learning-158570b62674

为什么彩票是训练神经网络的下一件大事

训练神经网络是昂贵的。据计算,OpenAI 的 GPT-3 使用市场上成本最低的云 GPU 的培训成本为 460 万美元。难怪弗兰克尔和卡宾的 2019 年彩票假说开始了一场研究淘金热,引起了脸书和微软等顶级学术头脑和科技巨头的关注。在论文中,他们证明了中奖(彩票)的存在:一个神经网络的子网络可以被训练产生与原始网络一样好的性能,并且规模小得多。在这篇文章中,我将阐述这是如何工作的,为什么它是革命性的,以及研究的现状。

为什么是彩票?

传统观点认为,神经网络最好在训练后修剪,而不是在开始时。通过修剪权重、神经元或其他组件,得到的神经网络更小、更快,并且在推理过程中消耗更少的资源。如果处理得当,网络规模可能会大幅缩小,但精度不会受到影响。

通过颠倒传统智慧,我们可以考虑 我们是否可以在训练 之前修剪网络并获得相同的结果。换句话说,来自删减组件的信息是网络学习所必需的吗,即使不代表它的学习?

彩票假说集中于修剪权重,并提供了经验证据,即某些修剪后的子网络可以从一开始就被训练,以实现与整个网络相似的性能。怎么会?迭代幅度修剪。

迭代幅度修剪

当历史上尝试过这样的任务时,修剪后的网络权重会随机重新初始化,性能会迅速下降。

这里的关键区别是权重被返回到它们的初始状态。当训练时,在相同的训练时间内,在高水平的修剪下,结果匹配原始性能。

照片由马克·泰格霍夫Unsplash 拍摄

这表明这些彩票作为特定子网和初始权重的交集而存在。可以说,他们“赢得了彩票”,因为该架构和这些权重的匹配表现得与整个网络一样好。这适用于更大的型号吗?

对于更大的模型,同样的方法并不适用。在观察对噪声的敏感度时,弗兰克尔和卡宾复制了经过修剪的网络,并对不同排序的数据进行了训练。当线性模式连通性存在时,IMP 成功,这是一种非常罕见的现象,其中多个网络收敛到相同的局部最小值。对于小型网络,这种情况很自然。对于大型网络来说,则不然。那怎么办呢?

以较小的学习速率开始导致 IMP 适用于大模型,因为对来自数据的初始噪声的敏感性降低了。学习率可以随着时间的推移而增加。另一个发现是,在稍后的训练迭代中,将我们修剪过的神经网络的权重恢复到它们的值,而不是第一次迭代,同样有效。例如,1000 次迭代训练中第 10 次迭代的权重。

这些结果在不同的架构中保持稳定,如变压器、LSTMs、CNN 和强化学习架构。

虽然这篇论文证明了这些彩票的存在,但是还没有提供识别它们的方法。因此,淘金热在寻找他们的属性和他们是否可以在训练前确定。他们也启发了早期修剪的启发式工作,因为我们当前的启发式工作集中在训练后的修剪。

当前研究

一票全赢 (2019)显示彩票编码信息对数据类型和优化器不变。他们能够成功地在基于不同数据类型(如 VGG 到 ImageNet)的网络之间传输彩票,并获得成功。

一个关键指标是网络训练数据的相对规模。如果彩票票源在比目的地网络更大的数据集上被训练,则其表现更好;否则,类似或更糟。

照片由莫里茨·金德勒Unsplash 上拍摄

抽早鸟票 (2019):本文旨在证明在训练中可以早早发现彩票。每次训练迭代,他们都会计算一个剪枝掩码。如果上一次迭代和这次迭代的掩码距离(使用汉明距离)低于某个阈值,网络停止修剪。

通过迭代保存突触流 (2020)在没有任何数据的情况下修剪神经网络:本文重点讨论在没有数据的情况下初始化时计算修剪。它在初始化时优于现有的最先进的剪枝算法。该技术侧重于最大化临界压缩在不影响性能的情况下可能发生的最大修剪。为了做到这一点,作者旨在防止整层被修剪。网络通过对保留层进行积极评分并在每次网络修剪时重新评估分数来实现这一点。**

结束语

在神经结构中存在小的子网络,这些子网络可以被训练得和整个神经网络一样好,这为有效的训练打开了一个可能性的世界。在这个过程中,研究人员正在学习很多关于神经网络如何学习以及学习需要什么的知识。谁知道呢?很快有一天,我们可能能够在 训练之前 修剪我们的网络,节省时间、计算和能量。

照片由埃米利亚诺·维托里奥西Unsplash 上拍摄

揭开拼花文件格式的神秘面纱

原文:https://towardsdatascience.com/demystifying-the-parquet-file-format-13adb0206705

任何数据科学工作流的默认文件格式

你在熊猫身上用过pd.read_csv()吗?嗯,如果您使用 parquet 而不是 CSV,那么这个命令的运行速度可以比 T2 快 50 倍。

照片由迈克·本纳Unsplash 拍摄

在本帖中,我们将讨论 apache parquet,这是一种非常高效且支持良好的文件格式。这篇文章是面向数据从业者(ML,DE,ds)的,所以我们将把重点放在高级概念上,并使用 SQL 来讨论核心概念,但更多资源的链接可以在整篇文章和评论中找到。

事不宜迟,我们开始吧!

技术 TLDR

Apache parquet 是一种开源文件格式,提供了高效的存储和快速的读取速度。它使用一种混合存储格式,按顺序存储列块,从而在选择和过滤数据时提供高性能。除了强大的压缩算法支持( snappy,gzip,LZO ),它还提供了一些减少文件扫描和编码重复变量的巧妙技巧。

如果你在乎速度,你应该考虑镶木地板。

但是,到底是怎么回事呢?

好吧,让我们慢一点,用简单的英语讨论拼花地板。

1 —数据存储问题

假设我们是数据工程师。我们希望创建一个数据架构,促进在线分析过程( OLAP ),这只是面向数据分析的选择性查询。在 OLAP 环境中优化的数据函数的一些例子是探索性数据分析或决策科学。

但是我们应该如何在磁盘上存储我们的数据呢?

图 1:将二维表格转换成二进制的例子。图片作者。

嗯,在考虑我们的转换是好是坏时有很多考虑因素,但是对于 OLAP 工作流,我们主要关心两个…

  • 读取速度:我们从二进制文件中访问和解码相关信息的速度
  • 磁盘大小:二进制格式的文件需要多少空间

请注意,文件压缩算法的成功还有其他衡量标准,比如写速度和元数据支持,但是我们现在只关注上面两个。

那么,相对于 CSV 文件,parquet 的性能如何呢?占用空间减少 87%,查询速度提高 34 倍 (1 TB 数据,s 3 存储)——src

2 —镶木地板核心特征

但是为什么 parquet 比 CSV 和其他流行的文件格式更有效呢?第一个答案是存储布局…

2.1 —混合存储布局

当我们将一个二维表转换成一系列 0 和 1 时,我们必须仔细考虑最佳结构。应该先写第一栏,再写第二栏,再写第三栏吗?还是应该顺序存储行?

传统上,有三种主要布局可以将我们的二维表格向下转换为 1:

  1. 基于行:顺序存储行(CSV)。
  2. 基于列:顺序存储列(ORC)。
  3. Hybrid-base: 顺序存储大块的列(拼花地板)。

我们可以在图 2 中看到每种格式的图形表示。

图 2:3 种主要的存储类型。图片作者。

现在,混合布局对于 OLAP 工作流非常有效,因为它们支持投影和谓词。

投影是选择列的过程——你可以把它想象成 SQL 查询中的SELECT语句。基于列的布局最能支持投影。例如,如果我们想使用基于列的布局读取表的第一列,我们可以只读取二进制文件中的前 n 个索引,将它们反序列化,然后呈现给用户。很有效率,对吧?

谓词是用于选择行的标准——您可以将其视为 SQL 查询中的WHERE子句。基于行的存储最能支持谓词。如果我们希望所有的行都符合某种标准,比如Int >= 2,我们可以通过Int(降序)对表进行排序,扫描直到不满足我们的标准,然后返回无效行之上的所有行。

在这两个场景中,我们都希望遍历尽可能少的文件。此外,由于数据科学通常需要对行和列进行子集化,基于混合的存储布局为我们提供了一个介于列和基于行的文件格式之间的中间地带。

在继续之前,需要注意的是,拼花地板通常被描述为柱状结构。然而,由于它存储大量的列,如图 2 底部所示,混合存储布局是更精确的描述。

太好了!因此,如果我们希望提取数据,我们可以只存储连续的列块,而通常会获得非常好的性能。但是这种方法有规模吗?

2.2 —拼花地板元数据

答案是响亮的“是”Parquet 利用元数据来跳过根据我们的谓词可以排除的数据部分。

图 3:基于混合的存储布局从二维到一维的转换。图片作者。

以图 3 中的示例表为例,我们可以看到我们的行组大小为 2,这意味着我们存储给定列的 2 行,下一列的 2 行,第三列的 2 行,依此类推。

当我们用完所有的列后,我们移动到下一组行。注意,上表中只有 3 行,所以最后一个行组只有 1 行。

现在,假设我们实际上在行组中存储了 100,000 个值,而不是 2 个。如果我们希望找到所有的行,其中我们的Int列有一个给定值(即一个等式谓词),最坏的情况是扫描表中的每一行。

图 parquet 如何处理行组谓词的例子。图片作者。

Parquet 通过存储每个行组的maxmin值智能地解决了这个问题,允许我们跳过整个行组,如图 4 所示。但这还不是全部!由于 parquet 经常将许多.parquet文件写入一个目录,我们可以在中查看整个文件的列元数据,并确定是否应该扫描它。

通过包含一些额外的数据,我们能够跳过大块的数据并显著提高查询速度。更多,看这里。

2.3 —拼花文件结构

好了,我们已经暗示了数据是如何从二维格式转换成一维格式的,但是整个文件系统是如何构造的呢?

如上所述,parquet 可以一次写很多.parquet文件。对于小型数据集,这是一个问题,您可能应该在写入前对数据进行重新分区。但是,对于较大的数据集,将数据子集化到多个文件中可以显著提高性能。

总的来说,镶木地板遵循以下结构。让我们依次看一看每一个…

根>拼花文件>行组>列>数据页

首先,我们的文件根目录,只是一个保存所有内容的目录。在根目录中,我们有许多单独的**.parquet** 文件,每个文件包含我们数据的一个分区。单个拼花文件由多个行组组成,单个行组包含多个。最后,在我们的列中有数据页面,它实际上保存了原始数据和一些相关的元数据。

我们可以在下面的图 4 中看到单个.parquet文件的简化表示。

图 4:按顺序排列的拼花地板的层次结构。图片作者。

如果你对细节感兴趣,这是文档。重复和定义级别对于充分理解数据页面如何工作也是必不可少的,但这些只是一些额外的好处。

3 —附加优化

自 2013 年问世以来,parquet 变得更加智能。基本结构与上面的格式相比基本没有变化,但是增加了许多很酷的特性,可以提高某些类型数据的性能。

让我们看一些例子…

3.1 —我的数据有大量重复值!

解决方案:游程编码(RLE)

假设我们有一个包含 10,000,000 个值的列,但是所有的值都是0。为了存储这些信息,我们只需要两个数字:010,000,000valuenumber of times it repeated

RLE 就是这么做的。当发现许多连续的重复项时,parquet 可以将这些信息编码为一个对应于值和计数的元组。在我们的例子中,这将使我们避免存储 9,999,998 个数字。

3.2 —我的数据有非常大的类型!

解决方案:字典编码位打包

假设我们有一个包含国家名称的列,其中一些非常长。如果我们想存储“刚果民主共和国”,我们需要一个至少可以处理 32 个字符的字符串列。

字典编码用一个小整数替换列中的每个值,并将映射存储在数据页面的元数据中。当在磁盘上时,我们的编码值是位打包的,以尽可能占用最少的空间,但是当我们读取数据时,我们仍然可以将列转换回它的原始值。

3.3 —我对我的数据使用了复杂的过滤器!

求解:投影和谓词下推

在 spark 环境中,我们可以避免通过投影和谓词下推将整个表读入内存。因为 spark 操作是延迟计算的,这意味着它们直到我们实际查询数据时才被执行,所以 spark 可以将最少的数据放入内存。

快速提醒一下,谓词子集行和投影子集列。因此,如果我们知道我们只需要几列和行的子集,我们实际上不必将整个表读入内存——它在读取过程中被过滤掉了。

3.4——我有很多不同的数据!

解决方案:三角洲湖泊

最后,Deltalake 是一个开源的“lakehouse”框架,它结合了数据湖的动态特性和数据仓库的结构。如果您计划使用 parquet 作为您的组织的数据仓库的基础,那么 ACID 保证和事务日志等附加特性确实是有益的。

如果你想了解更多,这里有一个很棒的适合初学者的资源。

摘要

对于现实世界的使用来说,Parquet 是一种非常有效的文件格式。它在最小化表扫描和将数据压缩到小尺寸方面非常有效。如果您是一名数据科学家,parquet 可能是您的首选文件类型。

感谢阅读!我将再写 11 篇文章,将学术研究引入 DS 行业。查看我的评论,链接到这篇文章的主要来源和一些有用的资源。

去噪自动编码器(DAE)——如何使用神经网络清理数据

原文:https://towardsdatascience.com/denoising-autoencoders-dae-how-to-use-neural-networks-to-clean-up-your-data-cd9c19bc6915

神经网络

使用 Tensorflow / Keras 库在 Python 中构建 DAE 的指南

去噪自动编码器(DAE)。图片由作者提供。

介绍

自动编码器提供了一种有效的方法来学习数据的表示,这有助于完成降维或特征提取等任务。你甚至可以训练一个自动编码器来识别并去除你数据中的噪音

本文将快速回顾自动编码器(AE ),并深入探讨一种称为去噪自动编码器(DAE)的特定类型。

如果你在找 AE 降维、特征提取的例子,可以参考我之前的文章:under complete Autodencoders

内容

  • 机器学习领域中的去噪自动编码器(DAE)
  • DAE 的结构
  • 如何使用 Keras/Tensorflow 在 Python 中构建 DAE

机器学习领域中的去噪自动编码器(DAE)

自动编码器不同于其他流行类型的神经网络(前馈递归卷积),因为它们不需要标记数据来训练它们。因此,我们可以称它们为无监督的,或者,如果我们想要非常精确的话,称它们为自监督的神经网络。

鉴于神经网络的多样性和它们对机器学习的独特方法,我觉得它们应该被单独归类。

通过点击打开并探索下面的交互式旭日图,看看你是否能找到去噪自动编码器(DAE)👇。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的邮件。如果你不是中等会员,可以在这里 加入

DAE 的结构

首先,让我们快速回顾一下自动编码器的高级结构。自动编码器的关键组件包括:

  • 输入层 —将输入数据传入网络
  • 隐藏层编码器解码器— 组成,通过应用权重、偏置和激活函数来处理信息
  • 输出层 —通常匹配输入神经元

以下是对上述总结的说明:

自动编码器神经网络中各层的高级图示。图片由作者提供。

最常见的自动编码器类型是欠完整自动编码器,它将数据压缩(编码)到更少的神经元(更低的维度)中,同时删除“不重要”的信息。它通过同时训练编码器和解码器来实现这一点,因此输出神经元尽可能地匹配输入。

以下是欠完整自动编码器的网络图示例:

欠完全自动编码器神经网络。图片由作者提供,使用 AlexNail 的 NN-SVG 工具创建。

去噪自动编码器(DAE)

DAE 的目的是消除噪音。你也可以把它想象成一个为你的数据定制的去噪算法。

注意对单词定制的强调。假设我们在一组特定的数据上训练 DAE,它将被优化以消除相似数据中的噪声。例如,如果我们训练它从一组图像中去除噪声,它将在相似的图像上工作得很好,但不适合清理文本数据。

与不完全 AE 不同,我们可以在隐藏层中使用相同或更多数量的神经元,使 DAE 过度完全

第二个区别来自于没有使用相同的输入和输出。相反,输出是原始数据(例如,图像),而输入包含带有一些添加噪声的数据。

去噪自动编码器架构。图片由作者提供。

上面的例子是一个 DAE 设置去噪 MNIST 手写数字。在下一节中,我将向您展示如何设置和训练这样的 DAE。

如何使用 Keras/Tensorflow 在 Python 中构建 DAE?

设置

我们需要以下数据和库:

让我们导入所有的库:

上面的代码打印了本例中使用的包版本:

Tensorflow/Keras: 2.7.0
numpy: 1.21.4
matplotlib: 3.5.1
graphviz: 0.19.1

接下来,我们加载 MNIST 手写数字数据并显示前十位数字。请注意,我们可以去掉标签,因为我们在模型中不使用它们,但我保留了它们,所以我们知道更难阅读的数字是什么。

这是我们运行上述代码得到的结果:

MNIST 数据集的前十位数字。图片由作者提供。

如您所见,我们在训练集中有 60,000 张图像,在测试集中有 10,000 张图像。请注意,它们的尺寸是 28 x 28 像素。

现在是时候给我们的图像添加一些噪声了。使用下面的代码,您可以指定噪声的级别,这当然会影响最终的模型。我们添加的噪声越多,模型就越难清理。

这就是我们嘈杂的图像的样子:

有附加噪声的 MNIST 数字。图片来自作者

在大多数情况下,这些数字仍然是可读的,但是很难说出它是什么数字。

构建 DAE 之前的最后一步是重塑我们的输入。展平我们的映像的原因是,在本例中,我们将制作一个标准 DAE,而不是卷积 DAE。

以下是我们的 MNIST 图像数据的新形状:

New shape of X_train:  (60000, 784)
New shape of X_test:  (10000, 784)
New shape of X_train_noisy:  (60000, 784)
New shape of X_test_noisy:  (10000, 784)

构建去噪自动编码器

现在,我们将组装和训练我们的 DAE 神经网络。我们可以使用 Keras 顺序模型或 Keras Functional API 来实现。在下面的例子中,我选择了后者。

请注意,我在每一层都保留了相同数量的神经元(784),并在中间层添加了 L1 正则化以控制过度拟合。然而,这个模型决不是最佳的,所以您应该仅将它作为试验不同结构和超参数的起点。

上面的代码打印了两项内容。第一个是模型总结:

去噪自动编码器模型概述。图片由作者提供。

第二部分是看待模型结构的一种略微不同的方式,有些人更喜欢这种方式:

去噪自动编码器模型图。图片由作者提供。

组装好模型后,让我们对其进行 20 个时期的训练,并绘制损失图。

通过历元去噪自动编码器模型损失。图片由作者提供。

最后,是时候对我们的模型进行可视化评估了。我们将使用测试数据集并显示来自原始噪声去噪集的十幅图像进行比较。

原文

请注意,我们必须将尺寸调整回 28 x 28。

来自测试数据集的十幅原始图像。图片由作者提供。

嘈杂

现在我们来看看添加了噪声的图像。

来自测试数据集的 10 个噪声图像。图片来自作者

去噪

最后,让我们使用 DAE 来处理有噪声的图像并显示输出。

应用 DAE 神经网络模型后,来自测试数据集的 10 个图像。图片由作者提供。

尽管我们没有试验不同的网络设置和超参数调整,但结果令人印象深刻。我相信您可以创建一个性能更好的 DAE。试一试,让我知道结果如何!

结束语

去噪自动编码器是神经网络的一个迷人应用,有现实生活中的用例。除了对图像去噪之外,还可以使用它们在模型管线中预处理数据。让我知道你如何在你的数据科学项目中使用它们!

为了您的方便,我在我的 GitHub 库中保存了一个 Jupyter 笔记本,其中包含了上述所有代码。在构建自己的去噪自动编码器时,可以随意使用它作为指南。

干杯!🤓
T3【索尔·多比拉斯】T4

通过以下我的个性化链接加入 Medium,继续您的数据科学之旅:

https://bit.ly/3J6StZI

图 ML 中去噪扩散生成模型

原文:https://towardsdatascience.com/denoising-diffusion-generative-models-in-graph-ml-c496af5811c5

Graph ML 有什么新特性?

去噪扩散就够了吗?

去噪扩散概率模型 (DDPM)的突破发生在大约 2 年前。从那时起,我们观察到生成任务的巨大改进: GLIDEDALL-E 2Imagen图像的稳定扩散、语言建模的 Diffusion-LM 、视频序列的扩散,甚至是强化学习的扩散

扩散可能是 GraphML 在 2022 年的最大趋势——特别是当应用于药物发现、分子和构象异构体生成以及一般的量子化学时。通常,它们与等变 GNNs 的最新进展配对。

分子生成。生成与稳定扩散 2

基础:扩散和图上的扩散

让我们以 Hoogeboom 等人的等变扩散论文为例,使用尽可能少的方程来概括扩散模型的基础知识😅

向前和向后扩散过程。正向过程 q(z|x,h)逐渐将噪声添加到图中,直到它变成高斯噪声的阶段。反向过程 p(x,h|z)从高斯噪声开始,并逐渐对图形去噪,直到它变成有效图形的阶段。来源: 胡格博姆、萨托拉斯、维格纳克、韦林

  • 输入:一个带有 N 个节点和 E 条边的图( N,E
  • 节点特征通常有两个部分:z =【x,h】其中 x ∈ R 是 3D 坐标, h ∈ R^d 是类似原子类型的分类特征
  • (可选)边特征是焊接类型
  • 输出:带有节点、边和相应特征的图( N,E )
  • 前向扩散过程 q(z_t | x,h) :在每个时间步 t ,向特征注入噪声,使得在最后一步 T 它们变成白噪声
  • 反向扩散过程 p(z_{t-1} | z_t) :在每个时间步 t-1,要求模型预测噪声并“减去”它从输入中减去,这样在最后一步 t=0 我们有一个新的有效生成图
  • 去噪神经网络学习预测注入噪声
  • 去噪扩散已知等价于基于分数的匹配宋和尔蒙(2019)宋等人(2021 ) )其中神经网络学习预测扩散数据的分数【∇_x log p _ t(x)。基于分数的观点用随机微分方程 (SDEs)和维纳过程描述正向/反向过程

Emiel Hoogeboom,Victor Garcia Satorras,Clément Vignac,Max Welling。三维分子生成的等变扩散。ICML 2022。 GitHub

该工作引入了用于分子生成的等变扩散模型( EDM ),其必须在原子坐标 x (关于旋转平移反射)上保持 E(3)等变,同时节点特征 h (例如原子类型)保持不变。重要的是,原子具有不同的特征形态:原子电荷是有序整数,原子类型是一次性分类特征,原子坐标是连续特征,因此作者设计了特定特征的噪声处理和损失函数,并缩放输入特征以训练稳定性。

EDM 采用等变 E(n) GNN 作为神经网络,根据输入特征和时间步长预测噪声。在推理时,我们首先对期望数量的原子 M 进行采样,然后我们可以根据期望的属性 c 对 EDM 进行调节,并要求 EDM 生成分子(由特征 xh 定义)作为 x,h ~ p(x,h | c,M)

在实验上,EDM 在实现负对数似然性、分子稳定性和独特性方面远远优于基于归一化流量和 VAE 的方法。烧蚀表明,等变 GNN 编码器至关重要,因为用标准 MPNN 代替它会导致性能显著下降。

基于扩散的生成可视化。来源:推特

题外话:图形生成的扩散

克莱门特·维格纳克、伊戈尔·克劳祖克、安托万·西劳丁、王博涵、沃尔坎·切弗赫、帕斯卡尔·弗罗萨德。题外话:用于图形生成的离散去噪扩散GitHub

扯远了由 Clemént Vignac、Igor Krawczuk 和 EPFL 团队提出的是无条件图形生成模型(尽管有可能整合一个基于分数的函数,用于调节像 energy MAE 这样的图形级特征)。decade 是一个离散扩散模型,也就是说,它对离散节点类型(如原子类型 C、N、O)和边缘类型(如单键/双键/三键)进行操作,其中向图中添加噪声对应于与从训练集中挖掘为边际概率的转移矩阵(从一种类型到另一种类型)相乘。去噪神经网络是一种改进的图变换器。decade 适用于许多图形家族——平面、SBM 和分子,代码可用,查看 LoGaG 阅读小组演示的视频

离题扩散过程。来源: 维格纳克、克劳祖克等人

地理 Diff 和扭转扩散:分子构象生成

拥有一个具有其原子的 3D 坐标的分子,构象异构体生成的任务是生成另一组有效的 3D 坐标,一个分子可以用这些坐标存在。最近,我们看到 GeoDiff 和 Torsional Diffusion 将扩散框架应用于这项任务。

许敏凯,俞兰涛,,陈策,斯特凡诺·埃尔蒙,。 GeoDiff:分子构象生成的几何扩散模型。ICLR 2022。 GitHub

GeoDiff 是 SE(3)-等变扩散模型,用于生成给定分子的构象异构体。扩散应用于 3D 坐标,逐渐转化为高斯噪声(正向过程)。相反的过程将随机样本去噪为一组有效的原子坐标。GeoDiff 在欧几里得空间中定义了等变扩散框架(假设可以添加哪种噪声),并应用等变 GNN 作为去噪模型。去噪 GNN,一个图场网络,是相当标准的 EGNNs 的扩展。geo diff 首次展示了扩散模型如何优于标准化流量和基于 VAE 的模型💪

地理论坛。来源: 徐等

鲍文京,加布里埃尔科尔索,,里贾纳巴兹莱,汤米亚科拉。分子构象体生成的扭转扩散。NeurIPS 2022。 GitHub

当 GeoDiff 扩散欧几里得空间中原子的 3D 坐标时,扭转扩散提出了一种扰乱分子自由旋转键中扭转角的简洁方法。由于这种可旋转键的数量总是比原子的数量少得多(在 GEOM 药物中,平均每个分子有 44 个原子对 8 个扭转角),生成可能会快得多。棘手的部分是扭转角并没有形成欧几里得空间,而是一个超环面(一个甜甜圈🍩),所以向坐标添加一些高斯噪声是行不通的——相反,作者设计了一种新颖的扰动内核,作为包裹的正态分布(来自真实空间,但由 2pi ) 扭转扩散将基于分数的观点应用于训练和生成,其中分数模型必须是 SE(3)- 不变量和 sign- 等变变量。分数模型是张量场网络的变体。

实验证明,扭转扩散确实工作得更快——与 GeoDiff 的 5000 步相比,它只需要 5-20 步,目前是 conformer 一代中的 SOTA🚀

扭转扩散。来源: 景、科尔索等人

DiffDock:分子对接扩散

加布里埃尔·科尔索、汉尼斯·斯特尔克、鲍文·京、里贾纳·巴兹莱、汤米·亚科拉。 DiffDock:扩散步骤,分子对接的曲折GitHub

DiffDock 是基于分数的生成模型,用于分子对接,例如,给定配体和蛋白质,预测配体如何结合目标蛋白质。DiffDock 在产品空间中的平移 T(3)、旋转 SO(3)和扭转角 SO(2)^m 上运行扩散过程:(1)配体相对于蛋白质的定位(通常称为结合口袋),口袋事先未知,因此它是盲对接,(2)定义配体的旋转方向,以及(3)定义构象的扭转角(参见上文的扭转扩散以供参考)。

DiffDock 训练 2 个模型:用于预测实际坐标的分数模型和用于估计生成预测的可能性的置信度模型。这两个模型都是点云上的 SE(3)-等变网络,但更大的分数模型(就参数计数而言)对来自α碳的蛋白质残基(从现在著名的 ESM2 蛋白质 LM 的初始化)起作用,而置信度模型使用细粒度的原子表示。初始配体结构由 RDKit 生成。DiffDock 极大地提高了预测质量,你甚至可以在 HuggingFace spaces 上的在线演示中上传自己的蛋白质(PDB)和配体(微笑)来测试它!

DiffDock 直觉。资料来源: 科尔索、斯特尔克、景等

DiffSBDD:用于产生新配体的扩散

阿恩·施耐因、袁琪·杜、查尔斯·哈里斯、阿里安·贾马斯、伊利亚·伊加绍夫、陶伟·杜、汤姆·布伦德尔、彼得罗·利奥、卡拉·戈麦斯、马克斯·韦林、迈克尔·布朗斯坦、布鲁诺·科雷亚。具有等变扩散模型的基于结构的药物设计GitHub

DiffSBDD生成以蛋白质口袋为条件的新配体的扩散模型。 DiffSBDD 可以用 2 种方法实现:(1)当口袋固定时口袋条件性配体的产生;(2)近似口袋-配体对的联合分布的类修复生成。在这两种方法中,DiffSBDD 依赖于调整的等变扩散模型( EDM,ICML 2022 )和等变 EGNN 作为去噪模型。实际上,配体和蛋白质被表示为具有分类特征和 3D 坐标的点云(蛋白质可以是α-碳残基或全原子,残基的一键编码 ESM2 将来可以在这里使用),因此扩散在 3D 坐标上进行,确保等方差。

DiffSBDD。来源: 施耐庵、杜等人

DiffLinker:产生分子接头的扩散

伊利亚·伊加绍夫、汉尼斯·斯特尔克、克莱门特·维格纳克、维克托·加西亚·萨托拉斯、帕斯卡尔·弗罗萨德、马克斯·韦林、迈克尔·布朗斯坦、布鲁诺·科雷亚。用于分子接头设计的等变 3D 条件扩散模型GitHub

DiffLinker 是以 3D 片段为条件的生成分子接头的扩散模型。虽然以前的模型是自回归的(因此不是置换等变的)并且只能连接 2 个片段,但是 DiffLinker 生成整个结构并且可以连接 2+个片段。在 DiffLinker 中,每个点云都以上下文(所有其他已知片段和/或蛋白质口袋)为条件,上下文通常是固定的。扩散框架类似于 EDM,但现在以 3D 数据而不是标量为条件。去噪模型是相同的等变 EGNN。有趣的是,DiffLinker 有一个额外的模块来预测链接器的大小(分子的数量),因此您不必事先指定它。

DiffLinker。来源: 伊加绍夫等人

了解更多信息

我们暂时不用你的浏览器标签📚但是,请期待更多的几何扩散模型!

特别感谢 汉尼斯·斯特尔克拉迪斯拉夫·拉帕切克 对帖子的校对!关注推特上的 【汉尼斯】拉迪斯拉夫 ,以及 me ,或者订阅电报中的GraphML频道。**

****

分子生成。生成与稳定扩散 2

Python 深度学习在雷达卫星图像去噪中的应用

原文:https://towardsdatascience.com/denoising-radar-satellite-images-using-deep-learning-in-python-946daad31022

如何应对雷达卫星的固有干扰

轨道上的哨兵卫星,图片由 Rama 提供,维基共享

伊曼纽·达尔萨索(CNAM 和巴黎电信研究员)尤瑟夫·科米切(嗨!巴黎机器学习工程师),皮埃尔·布兰查德(嗨!巴黎机器学习工程师)

当人们想到卫星图像时,他们通常会想到显示大规模飓风的图片。这种图像由光学传感器捕捉,并被科学家广泛用于测量和预测森林火灾、自然灾害和全球变暖的其他后果,被公司用于为客户提供导航功能,被军队用于监测和跟踪敌人的军队,被城市化机构用于测量栖息地破碎或光污染。

总的来说,他们倾向于拥有出色的细节水平,但他们面临(至少)两个主要问题来详述地球的细节:夜晚和天气。

一种特殊的传感器可以帮助科学家在黑暗中透过云层和雨水进行观察。我们正在谈论合成孔径雷达(SARs)。合成孔径雷达系统可以搭载在卫星、飞机甚至无人驾驶飞机上,使其能够获取全球和局部范围的数据。虽然光学系统依赖于阳光(即传感器是被动的),但雷达会发送电磁波,并测量地面上的物体反向散射的分量(即传感器是主动的)。

这样,SAR 传感器可以在一天中的任何时间和任何气象条件下获取数据,因为传输波的波长允许它穿透云层。然而,它们遇到了一个固有的问题:斑点。我们将在本文中了解什么是散斑波动,以及如何在我们的软件包deepdespecling的帮助下,显著提高雷达图像的可解释性。

为什么要使用雷达卫星图像?

作者照片

普通的光学卫星携带多种数码相机和摄像机来拍摄地球图像。然而,合成孔径雷达卫星(SAR)向其目标物体发送电子无线电信号。然后,信号从土壤或海洋反射回来,并在一定时间内返回到发射器,这决定了距离,从而决定了观察到的拓扑结构。天线位置也决定了方位角和高度。无线电波的固有特性不允许它们给图像着色。然而,它们有非常值得赞赏的特性,这些特性使它们对科学界来说是必不可少的(但不仅仅是!)

事实上,无线电波不受天气或日周期的影响。它们使得在漆黑的夜晚或巨大的气旋中捕捉图像成为可能。但不仅仅是尽他们所能:

  • 检测湿度水平
  • 看穿火山烟雾
  • 测量树的高度,从而测量森林吸收二氧化碳的能力

光学(Sentinel-2)和合成孔径雷达(Sentinel-1)图像之间的互补性示例:在巴西亚马逊森林的这个区域,在光学图像中,云层遮挡了一些区域,而合成孔径雷达图像却可以访问这些区域:我们可以在图像的左侧看到街道,并在右侧识别森林/森林砍伐区域。来自伊曼纽·达尔萨索球场的图片。

SAR 图像的一个主要缺点:斑点。

虽然雷达卫星有许多优点,但它们固有地面临一个主要缺点:斑点。散斑是由于发射的无线电波的反弹特性而引起的颗粒干扰,其降低了图像的质量,并因此降低了人眼对图像的可解释性。

【萨热杜研究员解释的斑点现象】https://creativecommons.org/licenses/by-sa/4.0/,许可证:

在雷达图像上,斑点看起来像粒状的“盐和胡椒”纹理(见图 1)。这是因为在每个分辨单元内会出现来自多次散射返回的随机相长和相消干涉。

澳大利亚乌鲁鲁岩石的一个裁剪区域的降噪器示例(左侧有噪声,右侧有噪声:https://goo.gl/maps/jELs19EDypMUvjf3A

虽然它是雷达图像中固有的,但存在用于去除斑点的常见方法:多重观察或自适应滤波器,但它们通常会影响细节水平。

该方法由伊曼纽·达尔萨索、洛克·丹尼斯和佛罗伦萨·图平提出,在 PyTorch 中开发,并由 Hi!巴黎工程师 Youcef Kemiche 和 Pierre Blanchard 依赖于图像的实部和虚部的分离及其处理。它允许我们减少斑点并保留细节层次。

DeepDespeckling,一个解决这个问题的 Python 包

它是如何工作的?

到目前为止,大多数方法都考虑了监督训练策略:训练网络以产生尽可能接近无斑点参考图像的输出。无散斑图像通常是不可用的,这需要求助于自然或光学图像或选择长时间序列中的稳定区域,以避免缺乏地面真实性。另一方面,自我监督避免使用无斑点图像。

我们介绍了一种基于单视复 SAR 图像实部和虚部分离的自监督策略,称为 MERLIN(复自监督去斑点),并表明它提供了一种训练各种深度去斑点网络的直接方法。用 MERLIN 训练的网络考虑了由于特定于给定传感器和成像模式的 SAR 传递函数而产生的空间相关性。

通过只需要一张图像,并可能利用大量的档案,MERLIN 打开了无麻烦以及大规模训练去斑网络的大门。

MERLIN 的原理:在步骤 A 中,训练去斑点网络仅基于实部来估计反射率。损失函数根据虚部评估预测的可能性。一旦训练好网络,就可以如 B 所示使用:使用具有相同权重的网络分别处理实部和虚部。输出被组合以形成最终估计。作者伊曼纽·达尔萨索的图片。出处。

如需更详细的解释,请点击链接查看伊曼纽·达尔萨索、洛伊克·丹尼斯和佛罗伦萨·图平的作品。

deepdespeckling 包直接来自上面列出的研究人员的工作,旨在为开源社区提供一套方法来处理不同类型操作(聚光灯和条纹图)的斑点干扰。

如何安装?

怎么用?

请注意,两个独立的网络一直在训练两种图像形态:

TerraSAR-X 带状地图模式和 TerraSAR-X 高分辨率聚光灯模式。

下面举例说明高分辨率聚光灯数据。要对 SpotLight 数据应用可用函数,请将“deepdespeckling . merlin . test . spot light”替换为“deepdespeckling . merlin . test . strip map”。

测试部分

考虑到 SAR 图像的巨大尺寸,我们在这个包中开发了一组去斑功能,这样每个用户都可以将去斑效果应用到 CoSar 或 Numpy 图像。事实上,将去斑功能应用于大图像(通常有数千像素宽和高)对计算资源的要求非常高,可能需要相当长的时间。

您可以使用的 3 个功能如下:

  • 除斑()

该函数获取要去斑的整幅图像。如果图像尺寸很大,你最好依靠 GPU 来减少计算时间。

全尺寸噪声图像

全尺寸去噪图像

  • 去斑 _from_crop()

该功能允许您裁剪所提供图像的子集。如果您只需要去除较大图像的一部分斑点,这将非常有用。因此,与去斑点()相比,计算时间将会减少。

注意,该函数采用一个附加参数“fixed ”,该参数可以设置为。如果为真,裁剪将是一个 256 像素* 256 像素的正方形。如果为假,裁剪将为您的图纸尺寸。

实用的裁剪工具!

左侧有噪声的裁剪图像/右侧有去噪的裁剪图像

  • 去斑点 _ 从 _ 坐标()

该函数将去斑点功能应用于“coordinates _ dictionnary”中列出的坐标所描述的图像部分。如果您知道要去除斑点的细节的位置,这将非常有用。

从左侧的坐标图像中裁剪出的噪声/从右侧的坐标图像中裁剪出的噪声

列车部分

该软件包还允许您使用两种模型训练方法,无论您希望从头开始训练自己的模型并获得自己的重量,还是从我们预先训练的模型中训练模型。

  1. 从头开始训练你自己的模型(例如,将 from pretrained 参数设置为 False ,并从拟合中获得你自己的权重)

2.从预训练版本训练模型(即,将 from_pretrained 参数设置为 True 并使用我们的权重)

结论

这篇文章是关于用雷达传感器捕获的卫星图像的去噪。这些图像固有的颗粒干扰称为斑点。为了解决这个问题,来自巴黎电信的伊曼纽·达尔萨索和来自 Hi!巴黎。它允许您在条带地图和聚光灯操作中极大地提高 cosar 和 numpy 图像的可解释性。

我希望你喜欢它!如有问题和反馈,请随时联系我!

联系人

想知道更多关于你好!巴黎及其工程团队:

嗨!巴黎

嗨!巴黎工程团队

密集向量:用代码捕捉意义

原文:https://towardsdatascience.com/dense-vectors-capturing-meaning-with-code-88fc18bd94b9

语义搜索的自然语言处理

谷歌、网飞、Spotify 和许多其他公司背后的技术解释

vackground.com 在 Unsplash拍照。在松果(作者受雇于此)的语义搜索电子书NLP 上发表的原始文章。

对于现代自然语言处理(NLP)技术的成功,可能没有比语言的矢量表示更大的贡献了。随着 2013 年 word2vec 的推出,NLP 的迅速崛起被点燃[1]。

Word2vec 是代表文本的最具标志性和最早的密集向量的例子之一。但是自从 word2vec 时代以来,表示语言的发展已经以可笑的速度前进。

这篇文章将探讨为什么我们使用密集向量——以及目前可用的构建密集向量的一些最佳方法。

点击此处观看视频演示!

密集与稀疏向量

我们应该问的第一个问题是为什么我们要用向量来表示文本?简单的答案是,为了让计算机理解人类可读的文本,我们需要将我们的文本转换成机器可读的格式。

语言本来就充满了信息,所以我们需要相当大量的数据来表示哪怕是少量的文本。矢量自然是这种格式的好选择。

对于向量表示,我们也有两种选择;稀疏矢量或密集矢量。

稀疏向量可以更有效地存储,使我们能够对两个序列进行基于语法的比较。例如,给出两个句子;"Bill ran from the giraffe toward the dolphin""Bill ran from the dolphin toward the giraffe"我们将得到一个完美(或接近完美)的匹配。

为什么?因为尽管句子的意思不同,但它们是由相同的语法(例如,单词)组成的。因此,稀疏向量将是紧密匹配的,甚至是完美匹配的(取决于构造方法)。

稀疏向量之所以称为稀疏,是因为向量稀疏地填充了信息。通常,我们会在成千上万个零中寻找几个一(我们的相关信息)。因此,这些向量可能包含许多维度,通常有数万个维度。

在稀疏向量表示文本语法的情况下,我们可以将密集向量视为语义的数字表示。通常,我们把单词编码成非常密集的高维向量。词的抽象意义和关系是用数字编码的。

稀疏和密集向量比较。稀疏向量包含稀疏分布的信息比特,而密集向量在每个维度上都有密集打包的信息,信息丰富得多。

密集向量仍然是高维的(784 维很常见,但也可以更多或更少)。然而,每个维度都包含由神经网络确定的相关信息——压缩这些向量更复杂,因此它们通常会使用更多内存。

想象一下,我们为书中的每个单词创建密集的向量,减少这些向量的维度,然后以 3D 形式可视化它们——我们将能够识别关系。例如,一周中的几天可以聚集在一起:

相关关键词的聚类示例,如典型的单词嵌入,如 word2vecGLoVe

或者我们可以执行“基于单词”的算法:

Mikolov 的另一篇论文2中对单词向量进行运算的经典示例。

所有这些都是通过使用同样复杂的神经网络实现的,神经网络从海量文本数据中识别模式,并将它们转化为密集向量。

因此,我们可以将稀疏向量和密集向量之间的区别视为用语言表示语法,而不是用语言表示语义。

生成密集矢量

有许多技术可以用来构建密集向量,从单词或句子的向量表示,到职业棒球大联盟的球员[3],甚至是跨媒体的文本和图像。

我们通常采用现有的公共模型来生成向量。几乎每个场景都有一个高性能的模型,使用起来更容易,更快,也更准确。有些情况下,例如对于特定于行业或语言的嵌入,您有时需要从头开始微调甚至训练一个新的模型,但这并不常见。

我们将探索这些技术中最激动人心和最有价值的几项,包括:

  • “2vec”方法
  • 句子变形金刚
  • 密道寻回犬(DPR)
  • 视觉变形金刚(ViT)

Word2Vec

尽管我们现在拥有构建嵌入的高级技术,但是如果没有 word2vec,对密集向量的概述将是不完整的。虽然不是第一个,但它是第一个广泛使用的密集嵌入模型,这要归功于(1)非常好的,以及(2)发布的 word2vec 工具包——允许轻松训练或使用预先训练的 word2vec 嵌入。**

给定一个句子,通过一个编码器-解码器神经网络将一个特定的单词(翻译成一个热码编码向量)映射到周围的单词来创建单词嵌入。

word2vec 中构建密集矢量嵌入的跳格法。

这是 word2vec 的 skip-gram 版本,给定一个单词fox,它试图预测周围的单词(它的上下文)。训练后,我们丢弃左右块,只保留中间的密集向量。这个向量表示图左侧的单词,可以用于为下游语言模型嵌入这个单词。

我们还有连续单词包(CBOW) ,它切换方向,旨在根据上下文预测单词。这一次我们为右边的单词生成一个嵌入(在这种情况下,仍然是fox)。

在 word2vec 中构建密集矢量嵌入的连续单词包(CBOW)方法。

skip-gram 和 CBOW 是相似的,因为它们从编码器-解码器网络的中间隐藏层产生密集嵌入向量。

由此,Mikolov 等人提出了我们之前看到的应用于语言的矢量算术的臭名昭著的King - Man + Woman == Queen例子。

Word2vec 刺激了 NLP 的一系列发展。然而,当使用单一向量来表示较长的文本块时,word2vec 毫无用处。它允许我们对单个单词(或 n-grams)进行编码,但仅此而已,这意味着大段的文本只能由许多向量来表示。

为了有效地比较较长的文本块,我们需要用一个向量来表示它。因为这个限制,几个扩展的嵌入方法很快出现,比如 sentence2vec 和 doc2vec。

无论是 word2vec,sentence2vec,甚至是(击球手|投手)2vec(美国职业棒球大联盟球员的代表[3]),我们现在都有非常先进的技术来构建这些密集的向量。因此,尽管它是从“2vec”开始的,但我们今天并不经常看到它们被使用。

句子相似度

我们已经用 word2vec 探索了基于单词的嵌入的开端,并简要地提到了弹出的另一个2 vec,旨在将这种矢量嵌入方法应用于更长的文本块。

我们在变压器模型中看到了同样的演变。这些模型产生了信息极其丰富的密集向量,可用于从情感分析到问答的各种应用。由于这些丰富的嵌入,transformers 已经成为主导的现代语言模型。

伯特可能是这些变压器架构中最著名的(尽管以下适用于大多数变压器型号)。

在 BERT 中,我们为每个单词(或令牌)生成类似于 word2vec 的矢量嵌入。然而,由于更深的网络,嵌入更加丰富——由于注意机制,我们甚至可以对单词的上下文进行编码。

注意机制允许 BERT 通过考虑所述上下文单词的对齐来优先考虑哪些上下文单词应该对特定嵌入具有最大影响(我们可以将其想象为 BERT 字面上根据上下文注意特定单词)。

我们所说的“上下文”是指,word2vec 将为“银行”生成相同的向量,无论它是“一个绿草如茵的银行”还是“英格兰银行”——由于注意机制,BERT 将根据周围的上下文修改银行的编码。

但是,这里有一个问题。我们要重点比较的句子,而不是单词。并且为每个令牌产生 BERT 嵌入。所以这对我们句子对的比较没有帮助。我们需要的是一个单一的向量来表示我们的句子或段落,比如 sentence2vec。

第一个明确为此构建的转换器是 Sentence-BERT (SBERT) ,是 BERT [4]的修改版本。

BERT(和 SBERT)使用一个单词块记号赋予器——这意味着每个单词等于一个或多个记号。SBERT 允许我们为包含不超过 128 个标记的序列创建单个向量嵌入。任何超出这个限制的部分都会被删除。

这个限制对于长文本来说并不理想,但是在比较句子或者平均长度很小的段落时就足够了。许多最新的模型也允许更长的序列长度!

嵌入句子转换器

让我们看看如何使用sentence-transformers库[5]快速整合一些句子嵌入。首先,我们导入这个库并初始化一个来自微软的名为all-mpnet-base-v2(最大序列长度384)的句子转换器模型。

然后我们可以继续编码一些句子,一些句子比另一些更相似——同时共享很少的匹配单词。

我们的句子转换器从这些句子中产生了什么?我们句子的 768 维密集表示。在大多数情况下,当使用相似性度量(如余弦相似性)进行比较时,这些嵌入的性能非常好。

尽管我们关于蜜蜂和它们的蜂王共享个描述性词语的语义最相似的句子,当用余弦相似性测量时,我们的模型正确地将这些句子嵌入最近的向量空间中!

问题回答

transformer 模型的另一个广泛应用是问答。在问答中,我们可以使用几种不同的架构。其中最常见的就是开域 Q & A (ODQA)

ODQA 允许我们获取一大组包含问题答案的句子/段落(比如来自维基百科页面的段落)。然后,我们问一个问题,以返回一个(或多个)段落中最能回答我们问题的一小部分。

在这样做的时候,我们利用了三个组件或模型:

  • 某种数据库来存储我们的句子/段落(称为上下文)。
  • 一个检索器检索它认为与我们的问题相似的上下文。
  • 一个阅读器模型,它从我们的相关上下文中提取答案

一个开放域问答(ODQA)架构的例子。

这个架构的检索器部分是我们这里的重点。想象我们使用一个句子转换模型。给定一个问题,检索者会返回与我们的问题最相似的句子——但是我们想要答案而不是问题。

相反,我们希望有一个模型能够将问答配对映射到向量空间中的同一点。所以给出了两句话:

**"What is the capital of France?" AND "The capital of France is Paris."**

我们想要一个模型,将这两个句子映射到相同的(或非常接近的)向量。因此,当我们收到问题"What is the capital of France?"时,我们希望输出向量与我们的向量数据库中的"The capital of France is Paris."向量表示具有非常高的相似性。

这方面最流行的模型是艾的《密集通道检索器(DPR)】

DPR 由两个较小的模型组成——一个上下文编码器和一个查询编码器。同样,他们都使用 BERT 架构,并在问答对上进行并行训练。我们使用对比损失函数,计算为每个编码器输出的两个向量之间的差[6]。

DPR 的双编码器结构,我们有一个问题编码器和一个上下文编码器——两者都经过优化,为每个问题-上下文对输出相同(或接近)的嵌入。

因此,当我们给我们的问题编码器"What is the capital of France?"时,我们希望输出向量与我们的上下文编码器为"The capital of France is Paris."输出的向量相似。

我们不能把所有的问答关系都建立在训练期间被看到的基础上。因此,当我们输入一个新的问题,比如"What is the capital of Australia?",我们的模型可能会输出一个我们认为类似于"The capital of Australia is ___"的向量。当我们将其与数据库中的上下文嵌入进行比较时,这个应该类似于"The capital of Australia is Canberra"(或者我们希望如此)。

快速 DPR 设置

让我们快速看一下用 DPR 构建一些上下文和查询嵌入。我们将使用来自拥抱脸的transformers库。

首先,我们为我们的上下文(ctx)模型和question模型初始化记号化器和模型。

给定一个问题和几个上下文,我们标记和编码如下:

请注意,我们已经将这些问题包含在我们的上下文中,以确认双编码器架构不只是像句子转换器一样产生简单的语义相似性操作。

现在,我们可以将我们的查询嵌入xq与我们所有的上下文嵌入xb进行比较,看看哪些与余弦相似度最相似。

在我们的三个问题中,我们返回了两个正确答案作为最热门的答案。很明显,DPR 不是完美的模型,尤其是考虑到我们问题的简单性和 DPR 检索的小数据集。

然而,积极的一面是,在 ODQA 中,我们会返回更多的上下文,并允许一个阅读器模型来识别最佳答案。读者模型可以“重新排列”上下文,因此不需要立即检索顶部上下文来返回正确答案。如果我们在 66%的情况下检索到最相关的结果,这可能是一个好结果。

我们还可以看到,尽管在上下文中隐藏了与我们的问题完全匹配的问题,但它们只干扰了我们的最后一个问题,被前两个问题正确地忽略了。

视觉变形金刚

计算机视觉(CV)已经成为变压器模型中一些令人兴奋的进步的舞台,这些进步在历史上一直局限于 NLP。

这些进步有望使《变形金刚》成为第一个被广泛采用的在 NLP CV 两方面都出色的 ML 模型。同样的,我们一直在创造代表语言的密集向量。我们可以对图像做同样的事情——甚至将图像和文本编码到同一个向量空间中。

我们可以使用特定的文本和图像编码器将文本和图像编码到同一个向量空间。图片来源 Alvan Nee

视觉变压器(ViT) 是第一个在没有任何上游 CNN 帮助的情况下应用于 CV 的变压器(如同 VisualBERT [7])。作者发现,维生素 t 有时会胜过最先进的(SOTA)CNN(长期统治 CV 的大师)[8]。

这些 ViT 转换器已经与更传统的语言转换器一起使用,以产生迷人的图像和文本编码器,正如 OpenAI 的 CLIP 模型【9】。

剪辑模型使用两个编码器,像 DPR,但是这次我们使用一个 ViT 模型作为我们的图像编码器和一个屏蔽自我关注转换器,像文本的 BERT】。与 DPR 一样,这两个模型被并行训练,并通过对比损失函数进行优化——为图像-文本对产生高相似度向量。

这意味着我们可以对一组图像进行编码,然后将这些图像与我们选择的标题进行匹配。我们可以使用整篇文章中使用的编码和余弦相似性逻辑。让我们试一试。

图文嵌入

我们先来获取几张图片进行测试。我们将使用 Unsplash 中的三幅狗做不同事情的图片(下面标题中的链接)。

从 Unsplash 下载的图片(标题是手动添加的——它们不包含在图片中),照片署名为克里斯蒂安·卡斯蒂略[ 12Alvan Nee

我们可以使用拥抱脸中的transformers来初始化剪辑modelprocessor

现在让我们创建三个真正的标题(加上一些随机的)来描述我们的图像,并在将它们传递给我们的model之前通过我们的processor对它们进行预处理。我们将得到输出逻辑,并使用一个argmax函数来得到我们的预测。

在那里,我们有完美的图像到文本的匹配与剪辑!当然,它并不完美(我们这里的例子相当简单),但是它很快就产生了一些令人惊叹的结果。

我们的模型已经处理了比较文本和图像嵌入。但是,如果我们想要提取那些在比较中使用的相同嵌入,我们访问outputs.text_embedsoutputs.image_embeds

同样,我们可以遵循与之前余弦相似度相同的逻辑来查找最接近的匹配。让我们用这种替代方法比较一下'a dog hiding behind a tree'的嵌入和我们的三个图像。

不出所料,我们把躲在树后的狗还回来了!

以上是对 NLP 中早期密集矢量嵌入和当前 SOTA 的概述。我们已经介绍了文本和图像嵌入的一些最激动人心的应用,例如:

  • sentence-transformers语义相似。
  • 用脸书艾的 DPR 模型进行问答检索。
  • 用 OpenAI 的剪辑进行图文匹配。

我们希望你能从这篇文章中学到一些东西。

参考

[1] T. Mikolov 等人,向量空间中单词表示的有效估计 (2013)

2 T. Mikolov 等人,连续空间词表征中的语言规律 (2013),NAACL HLT

[3] M. Alcorn,(击球手|投手)2vec:具有神经球员嵌入的无统计人才建模 (2017),麻省理工斯隆:体育分析会议

[4] N. Reimers,I. Girevych,句子-BERT:使用连体 BERT 网络的句子嵌入 (2019),EMNLP

[5] N .赖默斯,句子变压器文件,sbert.net

[6] V. Karpukhin 等人,面向开放领域问答的密集段落检索 (2020),EMNLP

[7]李力宏等,视觉伯特:视觉与语言的简单与表演基线 (2019),arXiv

[8] A. Dosovitskiy 等人,一幅图像值 16x16 个字:大规模图像识别的变形金刚 (2020),arXiv

[9] A .拉德福德等,剪辑:连接文本和图像 (2021),OpenAI 博客

[10] 夹模特卡,抱紧脸

*所有图片均由作者提供,除非另有说明

基于密度的聚类:DBSCAN 与 HDBSCAN

原文:https://towardsdatascience.com/density-based-clustering-dbscan-vs-hdbscan-39e02af990c7

为您的数据选择哪种算法

美国宇航局在 Unsplash 拍摄的照片

介绍

聚类分析是数据科学中的一个相关领域,它能够将相似的对象分组到不同的子组中。虽然有不同的聚类算法家族,但最广为人知的是 K-Means 。这是一种基于质心的算法,这意味着数据中的对象通过被分配到最近的质心来进行聚类。然而,K-Means 的一个主要缺陷是它缺乏对异常值或噪声数据点的检测,这导致它们被错误地分类。此外,K-Means 对球状星团有一种固有的偏好,并且在由任意形状的星团组成的数据上不能很好地工作。这在图 1 中举例说明,其显示了数据集【1】,该数据集由被噪声数据点包围的任意构象的六个不同聚类组成。K-Means(使用 n_clusters = 6 运行)很难处理这些变化的形状,此外,还会将所有有噪声的数据点分配给聚类。

图 1:由任意形状的聚类和噪声组成的数据的 K 均值。图片作者。

这种类型的问题可以通过使用基于密度的聚类算法来解决,该算法将聚类描述为通过低密度区域与其他聚类分隔开的高密度区域。这一领域的两个流行算法是 DBSCAN (基于密度的噪声应用空间聚类)及其分层后继算法 HDBSCAN

基于密度的噪声应用空间聚类

该算法【2】基于密度对数据进行聚类,通常要求一个聚类内的密度一致,聚类之间的密度下降。它有两个主要参数,需要仔细选择以获得最佳结果:

  1. eps :定义每个数据点周围邻域的半径。
  2. min_samples :一个数据点被认为是核心点所需的邻域内的最小数据点数。

DBSCAN 将数据点分为三类:

  1. 核心点:在半径 eps 的邻域内至少有 min_samples 个数据点的点。
  2. 边界点:具有少于 min_samples 的点,但在其邻域内至少有一个核心点。
  3. 噪声:一个既不是核心点也不是边界点的点,其邻域内的最小样本点少于个。

该算法从随机选取一个点开始,如果满足参数要求,就开始将数据点分配给聚类。然后通过递归计算每个数据点周围的邻域来扩展聚类,直到访问了所有点。描述 DBSCAN 算法的优秀视觉指南可以在这篇文章中找到。

HDBSCAN

HDBSCAN 是最近开发的一种算法【3】,它建立在 DBSCAN 的基础上,与其前身不同,它能够识别不同密度的簇。该算法通过寻找每个数据点的核心距离非常相似地开始。然而,与 DBSCAN 不同,它使用 eps 作为相应树状图的截止点,HDBSCAN 通过查看分裂来压缩树状图,分裂仅产生少量脱离聚类的点。结果是一个更小的树,具有更少的丢失点的聚类(图 2),然后可以用于选择最稳定和持久的聚类。

图 2:扩展与压缩的树状图。数字摘自 hdbscan.readthedocs.io

保留的簇的大小由 HDBSCAN 唯一需要的输入参数定义:

  • min _ cluster _ size:形成一个聚类的最小数据点数。

该参数确定点是脱离一个聚类还是分裂形成两个新的聚类。这一过程使得生成的树可以在不同的高度被切割,并根据其稳定性采摘不同密度的簇。

关于 HDBSCAN 底层功能的更广泛的解释可以在它的相应文档中找到。

比较

因素

如果用户对手头的数据有一些领域知识,DBSCAN 可能会非常有效。这具体指的是 eps 参数,该参数不太直观,通常需要一些关于数据点应被分配到一个聚类的接近度的知识(或微调)(详见本帖)。相比之下,HDBSCAN 的唯一必需参数 min_cluster_size 更直观。

噪音

DBSCAN 对噪声非常敏感(图 3),这可能导致不正确的聚类。HDBSCAN 虽然并不完美,但在将噪声数据点分配给聚类时通常会更加谨慎。

图 3: DBSCAN 产生各种小簇,应归类为噪声。HDBSCAN 也可以做到这一点,但程度要小得多。有噪声的数据点以灰色显示。图片作者。

变化密度

DBSCAN 往往不能识别具有不均匀密度的簇。这个问题是 HDBSCAN 开发的主要动机,因此,它可以更好地处理不同密度的集群(图 4)。

图 4:由具有不同密度的簇组成的数据的 DBSCAN 与 HDSCAN。图片作者。

表演

可扩展性研究还表明,随着数据量的增加,HDBSCAN 的计算性能优于 DBSCAN。下图显示,在 200,000 个数据点时,HDBSCAN 的速度大约是 DBSCAN 的两倍(图 5)。

图 5:不同聚类算法的性能。图取自并采用自 hdbscan.readthedocs.io

结论

虽然 DBSCAN 和 h DBSCAN 都可以很好地处理包含噪声和任意形状和大小的簇的数据,但它们确实有一些复杂的差异。虽然如果您对数据有领域知识,DBSCAN 的附加 eps 参数会很有用,但它通常被认为是一个非常不直观的参数。相比之下,HDBSCAN 的 min_cluster_size 设置起来更直观。HDBSCAN 在对噪声和不同密度的集群进行分类时也占了上风。最后,HDBSCAN 比 DBSCAN 速度更快,计算效率更高。

参考

[1]麦金尼斯等人(2017 年)。hdbscan:基于分层密度的聚类开源软件杂志,《开放杂志》,第 2 卷,第 11 期。2017【数据文件】。

2 Ester 等人(1996 年)。“一种基于密度的算法,用于在带有噪声的大型空间数据库中发现聚类。”KDD 96:第二届知识发现和数据挖掘国际会议论文集,第 226-231 页。

[3]坎佩洛等人(2013 年)。"基于层次密度估计的密度聚类."PAKDD 2013:知识发现和数据挖掘的进展,第 160–172 页。

诗歌的依赖性管理

原文:https://towardsdatascience.com/dependency-management-with-poetry-f1d598591161

使用 pyenv 和诗歌组织 Python

照片由 Clément HélardotUnsplash 上拍摄

Python 的一个重要特点是管理它的版本、环境和依赖性——从而将项目交付给其他人。在本帖中,我们将看看打包和依赖管理器 诗歌——在作者看来,这是做这些事情的最佳方式。

在开始之前,让我们定义一些术语并澄清一些困惑,这也是我最初的受害者:除了诗歌,我们将使用pyenv——一个 Python 安装管理器。pyenv 允许我们在我们的系统 s.t .上管理不同的 Python 版本。例如,我们可以在项目 A 中使用 Python 2.7,在项目 B 中使用 Python 3.10——此外,如果我们只是通过pip install ...将所有需要的包安装到我们的系统 Python 中,还可以防止我们的 Python 安装受到污染。****

除此之外,还有不同的打包和依赖管理器——比如 poem,但也有 pipenv、venv 或 conda。在这篇文章中,我们将强调诗歌,正如我之前提到的,我个人认为这是最好的解决方案。

安装 Pyenv

我们首先安装 pyenv(假设您在 Linux 机器上,对于所有其他操作系统,请遵循安装指南):

**curl [https://pyenv.run](https://pyenv.run) | bash**

然后,在 pyenv 中,我们安装我们希望用于项目的 Python 版本,例如:

**pyenv install -v 3.10.0**

设置诗歌

按照描述安装 Python 后,我们将注意力转移到诗歌上。

通过以下方式安装:

**curl -sSL [https://install.python-poetry.org](https://install.python-poetry.org) | python3 -**

然后,切换到您的 Python 项目文件夹,将诗歌指向您想要使用的 Python 版本——在我们的示例中,这是通过(您可能需要一个pyproject.toml文件,见下文)完成的:

**pyenv local 3.10.0poetry env use $(which python)**

用诗歌工作

使用两个文件的诗歌功能:pyproject.tomlpoetry.lock

pyproject.toml中,我们描述所需的依赖/包,例如torch。开发人员在添加了所有需要的依赖项之后,运行poetry update

这触发了poetry.lock文件的创建,该文件“捕获”并描述了由来自pyproject.toml的所有数据包组成的确切环境。现在,当用户或其他开发人员下载 repo /代码时,他们只需运行poetry install,这将安装所有的依赖项——导致他们获得与第一个开发人员预期的完全相同的 Python 环境。

这是一个巨大的优势,也是必须的——与每个人只安装自己的依赖项相反——不这样做只会带来太多的麻烦。

要使用您的环境(运行脚本,…),运行poetry shell。这将激活您的环境,在其中您可以照常工作——例如通过python file.py运行 Python 程序。

示例项目

最后,让我们将所有这些放在一个示例项目中——并且第一次公开一个示例pyproject.toml文件的内容。

我们的项目将由三个文件组成:main.pypyproject.toml和自动生成的poetry.lock文件。

main.py有以下内容:

**import matplotlib.pyplot as plt
import numpy as np

def plot():
    x = np.linspace(0, 10, 50)
    y = np.sin(x)
    plt.plot(x, y)
    plt.savefig("plot.png")

if __name__ == "__main__":
    plot()**

正如我们所看到的,我们使用numpymatplotlib生成一条正弦曲线并绘制出来。因此需要安装这些模块,我们对诗歌就是这么做的。

让我们来看看pyproject.toml的内容:

第一部分包含一些关于项目的元数据:

**[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "…"
authors = ["hermanmichaels <hrmnmichaels@gmail.com>"]**

在此之后,是时候定义所需的依赖关系了。您也可以(建议)为每个包定义一个特定的版本,使poetry update过程具有确定性。此外,这里我们还定义了预期的 Python 版本。下面的代码片段安装了matplotlibnumpy,以及其他一些我为了方便而喜欢使用的包(在以后的帖子中会有更多的介绍!):

**[tool.poetry.dependencies]
python = "3.10"
matplotlib = "3.5.1"
mypy = "0.910"
numpy = "1.22.3"
black = "22.3.0"**

综合起来看:

**[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "…"
authors = ["hermanmichaels <hrmnmichaels@gmail.com>"]

[tool.poetry.dependencies]
python = "3.10"
matplotlib = "3.5.1"
mypy = "0.910"
numpy = "1.22.3"
black = "22.3.0"**

如上所述,为了生成poetry.lock文件,我们运行poetry update。然后,另一个使用这个项目的人,可以简单地运行poetry install来获得我们所有的依赖项。然后,您或他们可以通过运行以下命令来执行该程序:

poetry shell

python main.py

摘要

让我们快速总结一下:诗歌是一个(在我看来是最好的)打包和依赖管理器。它建立在一个工作的 Python 安装之上,我建议通过 Python 安装管理器 pyenv 来管理它。

按照上面的安装和设置步骤,创建一个新项目,并添加一个pyproject.toml file。在此,定义所有需要的依赖项。

然后,运行:

  • poetry update生成锁文件(或在添加了新的依赖项时更新它),其他人将使用该文件来获取相同的数据包集
  • poetry install安装来自锁文件的所有包(例如,当你下载了一个新的诗歌项目并想安装依赖项时——或者你的同事推了一个包含新包的新版本)
  • poetry shell进入环境并运行任何 Python 脚本

这就结束了对诗歌的介绍。希望这对您未来的工作有所帮助——欢迎随时回来获取更多信息!

将任何 ML 模型部署到任何云平台

原文:https://towardsdatascience.com/deploy-any-ml-model-to-any-cloud-platform-f27a8311f6d4

介绍 Truss,一个用于模型打包和部署的开源库

Truss 是用于 ML 模型服务的开源 Python 库|照片由 Joshua J. CottenUnsplash 上拍摄

模型服务不仅仅是一个难题,它是一个不断需要新的解决方案的难题。

作为 MLOps 的一部分,模型服务是 DevOps 的挑战,即保持一个复杂、脆弱的工件(模型)在多个动态环境中工作。随着为培训模型构建和更新框架,以及生产环境为新的功能和约束而发展,数据科学家必须重新实现模型服务脚本并重建模型部署流程。

在资源充足的大型组织中工作的数据科学家可以将他们的模型交给专业的 MLOps 团队进行服务和部署。但对于我们这些在初创公司和新公司工作的人来说,就像我在职业生涯的第一个十年所做的那样,我们必须自己处理 ML 部署的挑战。问题:服务和部署一个模型需要一套完全不同于培训的技能和技术。

简单介绍一下:我是 Tuhin Srivastava, Baseten 的首席执行官,Truss 最初就是在这里开发的。在努力找出数据科学家需要什么来实现 MLOps 的过程中,我们与数据科学领导者进行了交谈,并听到了类似这样的事情:

  • “我们希望避免任何形式的自定义开发,自行托管模型。如果我们自己来做,我们可能需要在虚拟机或 Kubernetes 集群上部署我们自己的 Docker,然后我们还必须处理围绕这些东西的所有开发工作。”— Faaez Ul Haq,数据科学主管@ Pipe
  • “我们的团队主要由数据科学家和语言学家组成,我们不是 DevOps 专家。我们可以写 Python,但我们不想整天写 YAML 配置。”—丹尼尔·怀特纳克,数据科学家@ SIL

数据科学家的工作环境是 Jupyter notebook,这是一个为迭代实验而设计的灵活而宽松的系统。Jupyter 笔记本是训练模型的一个很好的工具,但是作为一个非永久性的和面向开发的环境,它对于模型服务来说不是很好。模型服务需要像 Docker 这样的技术来带来一个稳定的、可预测的环境。

当今数据科学家如何处理服务模型

为生产中的模型提供服务通常归结为几个步骤:

  1. 序列化模型
  2. 将模型放在 Flask 之类的 web 服务器后面
  3. 将 web 服务器打包成 Docker 映像
  4. 在容器上运行 Docker 映像

在这些步骤中潜藏着额外的复杂性。该模型需要接受输入并以适当的格式生成输出,从 Python 优先的接口转换为 web 优先的接口。而且有些模型需要访问 GPU 硬件进行预测,或者安全访问秘密值,或者导入 Python 和系统包。

导航部署迷宫|照片由 Unsplash 上的 Robert Linder 拍摄

但是更大的问题是,即使基本步骤对于每个框架都是不同的,有时对于用同一框架构建的不同模型也是如此。因此,即使你知道如何服务 TensorFlow 模型,你也必须重新学习如何服务 PyTorch 模型,并在尝试拥抱脸模型时再次经历该过程。

“嗯,没关系,”你可能会说,“我只是要使用一个建模框架。我会成为 TensorFlow ML 工程师。”问题是,我们没有不同的框架,因为数据科学家不擅长达成一致。因为不同的问题需要不同的方法。每个流行的建模框架擅长不同种类的底层算法和结构。但是模型服务技术不需要完全不同。

CogBentoMLMLflow 这样的开源包有助于简化模型部署过程。我们希望扩展这些想法,开发一个开源库,特别是针对初创企业的数据科学家。我们的两个关键信念:

  1. 为 Python 用户构建:作为数据科学家,Python 是我们的舒适区。我们想要一个可以完全用 Python 管理的模型部署库。
  2. 与每个模型和平台一起工作:我们想要一个开源包,它可以处理模型部署,而不管模型框架和云平台。

在这些想法的指导下,我们构建并开源了 Truss

模型服务如何与 Truss 一起工作

提供和部署模型|作者图像的步骤

步骤 1:标准化模型打包

我们在本地机器上的 Jupyter 笔记本中,这是数据科学家的家乡。使用拥抱脸变压器,我们将在这个例子中引入 t5 小模型作为管道。

from transformers import pipeline
import truss

pipe = pipeline(tokenizer="t5-small", model="t5-small")
scaf = truss.mk_truss(pipe, target_directory="my_model")

拥抱脸是 Truss 开箱即用支持的许多流行框架之一,还包括 LightGBM、PyTorch、scikit-learn、TensorFlow 和 XGBoost(更多即将推出)。因此,我们需要做的就是在模型上运行 mk_truss,所有的东西都将被序列化和打包,以备使用。

第二步:扎实的地方发展

使用我们的 Truss,我们可以在 Jupyter 环境中调用模型:

print(scaf.server_predict({"inputs" : ["translate: hello world in german"]}))
# Expected result is {'predictions': [{'translation_text': 'Übersetzen: Hallo Welt in deutsch'}]}

但是 Truss 超越了代码内模型调用。有多种本地开发选项,包括在 Docker 容器中运行模型和发出 API 请求。

要启动 docker 容器:

truss run-image my_model

要提出请求:

curl -X POST [http://127.0.0.1:8080/v1/models/model:predict](http://127.0.0.1:8080/v1/models/model:predict) -d "{'inputs': [{'translation_text': 'Übersetzen: Hallo Welt in deutsch'}]}"

本地开发不仅仅是测试。您可以更新您的模型,以便通过预处理和后处理功能更好地与其他系统集成,创建样本输入以记录测试用例,以及配置您的 Truss 的每个方面以满足您的需求。有了上面介绍的各种调用选项,您将能够通过一个紧密的开发循环快速地为生产准备好您的模型。

步骤 3:无缝生产部署

多亏了 Docker,我们一直工作的开发环境与最终的生产环境非常匹配。根据您希望在哪里部署您的模型,请遵循针对平台的特定部署说明,如 AWS ECSBasetenGoogle Cloud Run 。您的模型也可以部署在任何可以运行 Docker 映像的地方。

根据您的环境,调用部署的模型可能略有不同,但是应该是与步骤 2 中的请求相匹配的 API 请求,但是目标是生产域。

步骤 4:共享和迭代

您的序列化模型以及相关的代码和配置文件构成了整个桁架。因此,将您的模型打包成一个 Truss 使其具有可移植性,从而释放了两个关键用例:版本控制和共享。

一切都在文件中,所以您可以在 Git 中提交您的模型。通过这种方式,您可以实现模型版本化,测试您模型的不同迭代,并将您的模型推送到 GitHub 或类似的存储库中。

尝试运行别人的模型最令人沮丧的部分是复制他们的环境。但是现在你的模型被保存为一个 Truss,所有人需要做的就是从 GitHub 或另一个资源库下载它,安装 DockerTruss Python 包,并在本地服务模型。我们很高兴能够在开源建模中实现更多的协作和迭代。

桁架标志|作者图片

机器学习模型只会变得更加复杂和强大。反过来,在本地和生产中可靠地服务于这些模型变得比以往任何时候都更加重要。我们致力于对 Truss 的长期支持和开发,并让我们知道应该在我们的路线图中添加什么。

Truss 为跨模型框架和部署目标的模型服务提供了统一的方法。从由 repo 主演的开始,通过端到端部署教程学习您最喜欢的框架和平台。

使用拥抱脸 DLC 在网站中部署聊天机器人

原文:https://towardsdatascience.com/deploy-chatbots-in-web-sites-using-hugging-face-dlcs-be59a86fd7ba

沃洛季米尔·赫里先科在 Unsplash 上的照片

带代码的分步教程

在本教程中,您将学习如何使用拥抱脸 DLC 来实现预训练模型,并在网站上部署它们以创建聊天机器人。虽然本教程主要关注对话类型模型,但是您可以将关键概念用于任何类型的应用程序。

资料来源:新科学

本文作者是罗伯托·扎帕

如今,由于亚马逊 AWS,我们有大量不同的方法来实现人工智能模型,使我们能够创建几年前无法实现的应用程序。最近,亚马逊宣布了通过拥抱脸实现 AWS 深度学习容器(DLCs)的可能性,从而简化了亚马逊 SageMaker 中模型的部署。在这篇文章中,我想把这种新方法与不同的 AWS 服务结合起来,用 chatbot 创建一个网站。我将使用预训练的 DialoGPT-medium 模型,可用于拥抱脸。

最终的架构将如下所示:

最终架构。来源:作者自己的作品

本教程结束时,您将能够:

  • 使用拥抱脸 DLCs 创建 SageMaker 端点
  • 创建 lambda 函数,并在其中调用我们的端点
  • 用 lambda 函数创建、设置和连接 API 网关
  • 在 Amplify 上创建一个网页,并将其连接到我们的网关

准备好了吗?我们走吧

使用拥抱脸 DLC 创建 SageMaker 端点

拥抱脸中选择您想要使用的型号,然后点击按钮“部署”→“亚马逊 SageMaker

拥抱脸网页。作者图片

现在您必须选择模型的任务和配置。不同的任务涉及不同的数据结构,我们必须提供给我们的模型,以便进行预测,你可以在这里看到不同的数据结构。选择对话AWS

拥抱脸网页。作者图片

在您想要使用的 SageMaker 笔记本实例中,复制并运行 Jupyter 笔记本中生成的代码。

很简单,对吧?

警告

当我写这篇教程的时候,有一个 bug 不允许在 SageMaker 中使用对话类型模型进行预测。为了绕过这个问题,需要对代码进行一些修改。别担心,你可以复制我的代码:

我们可以通过 SageMaker 控制台看到刚刚创建的端点→ “推理”“端点”。复制它的名称,因为在后面的章节中需要它。此外,记住在不再需要时删除端点,以免产生不必要的成本。你可以通过 SageMaker 控制台或者在你的 Jupyter 笔记本里面运行下面的代码来删除它:

predictor.delete_endpoint()

创建 lambda 函数,并在其中调用我们的端点

现在我们必须创建我们的 lambda 函数。登录 AWS Lambda 控制台,点击按钮创建功能。命名,选择你想要的语言(我选择 Python 3.8),点击“创建函数”。

AWS Lambda。作者图片

【代码】部分复制【lambda _ function . py】内的这段代码:

为了能够将端点连接到该功能,选择“配置”“环境变量”“编辑”,并在“端点名称】下和下写入上一节中复制的端点名称。点击【保存】

AWS Lambda。作者图片

最后但同样重要的是,我们必须为我们的功能添加正确的权限:选择“配置”“权限”,并单击角色名称

AWS Lambda。作者图片

向您的策略授予此权限:

用 lambda 函数创建、设置和连接 API 网关

API 网关中,点击“创建 API”,并点击 REST API 中的“构建”

AWS API 网关。作者图片

在下面的页面中,给你的 API 命名,选择【端点类型】 【边缘优化】,点击蓝色按钮【创建 API】

AWS API 网关。作者图片

“动作”中选择“创建方法”

AWS API 网关。作者图片

从新方法中选择“POST”并确认。选择 POST 方法并选择【集成类型】 Lambda 功能。写下 lambda 函数的名称后,点击“保存”按钮,在接下来的页面中点击“确定”按钮。

AWS API 网关。作者图片

现在,再次从“Action”选择“Enable COREs”,不做任何更改,点击“Enable CORS and replace existing CORS headers”按钮,在随后的消息中点击“Yes,replace existing values”

最后要做的是部署它:从“动作”选择“部署 API”。在【部署阶段】中选择【新阶段】,选择一个名称,点击“部署”按钮。

AWS API 网关。作者图片

在下一页中,您将看到一个“调用 URL”。复制并在 HTML 文件中使用它,以便能够调用 API 网关。

调用 URL 的示例。作者图片

测试我们的聊天机器人

我们快完成了!

现在我们有了调用 URL,我们可以使用下面的 python 程序测试我们的聊天机器人。您需要做的唯一更改是用您的调用 URL 替换 def main()函数中的 URL。

如果一切正常,我们可以继续将聊天机器人连接到我们的网页。

在 Amplify 上创建一个网页,并将其连接到我们的网关

AWS Amplify 中,选择按钮“新应用”“主机 web 应用”,在接下来的页面中选择“无 Git 提供者部署”,点击“继续”

AWS 放大。作者图片

现在决定你想要的名字,然后拖放你的 html/css 代码。HTML 文件必须命名为 "index.html" 。如果你只想上传 HTML 文件,你必须把它压缩成 zip 格式。否则,创建一个文件夹,给它你喜欢的名字,并把 html/css 文件放在里面。为了将您的 Amplify web 应用程序与您的 HTML 代码中的 API 网关连接起来,当您想要发送/接收来自模型的数据时,您必须使用 API 网关提供的 URL。

仅此而已!

结论和未来改进

在本教程中,我们学习如何将预训练的拥抱人脸模型部署到 Amazon SageMaker,并将 SageMaker 端点连接到其他 AWS 服务。在未来,学习如何改变我们的预训练模型以允许不同的用例并超越标准模型的限制可能是有用的,但那是另一回事了😁。

感谢您的阅读!我希望这对你有帮助。

关于作者

大家好,我是 Roberto Zappa,Neosperience 的机器学习工程师。我从事 ML 技术和 AWS 基础设施方面的工作。对技术充满热情,热爱机器学习和计算机视觉。

你可以在或者 linkedin 上找到我

Neosperience 通过软件解决方案释放同理心的力量,这些解决方案利用人工智能使品牌能够理解、参与和发展他们的客户群。在www.neosperience.com伸出手。

posted @ 2024-10-18 09:32  绝不原创的飞龙  阅读(277)  评论(0)    收藏  举报